From: Andreas Beeker
Date: Wed, 22 Jul 2020 22:08:33 +0000 (+0000)
Subject: #64411 - Provide JigSaw modules
X-Git-Tag: before_ooxml_3rd_edition~171
X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=c0f99416047be7c3b7e5413d7a0187a21022a02b;p=poi.git
#64411 - Provide JigSaw modules
- use classpath-build for Java 8, otherwise use modulepath
- save module-info classes to source, when using Java 9+ environment
- rename example packages - otherwise package clashes occured in the tests
- move agile encryption from ooxml to main.
remove EncryptionInfo XmlBeans and schema and use custom xml marshalling
- move ooxml test classes which reside in the same package as their tested main class
- rename base test classes to "BaseTest..." - temporarily I've used a light version of the main test classes to test scratchpad / ooxml
- build.xml - fixed the Rhino javascript errors of the dependency-macros
- DrawTextParagraph - fixed StringIndexOutOfBounds when logging set to debug level
- use JigSaw provider interface (= Java ServiceLoader), i.e. it wasn't possible (without openening everything), to access ooxml factory classes from main factory stub
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1880164 13f79535-47bb-0310-9956-ffa450edef68
---
diff --git a/build.xml b/build.xml
index 41fc1eae7c..5c107c47af 100644
--- a/build.xml
+++ b/build.xml
@@ -54,6 +54,7 @@ under the License.
+
@@ -153,9 +154,9 @@ under the License.
-
+
-
+
@@ -174,37 +175,70 @@ under the License.
-
-
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+ @{artifact}
+
+
+
+
+
+
+
+
+ @{artifact}
+
+
+
+
+
+
+
+ @{artifact}
+
+
+
+
+
+
+
+
+
+
+
+ @{usage}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -225,13 +259,13 @@ under the License.
-
-
+
+
-
-
+
+
@@ -249,7 +283,7 @@ under the License.
-
+
@@ -262,21 +296,19 @@ under the License.
-
+
-
-
+
+
-
-
@@ -375,7 +407,6 @@ under the License.
-
@@ -389,18 +420,8 @@ under the License.
-
-
-
-
-
-
-
-
-
-
-
+
@@ -410,38 +431,12 @@ under the License.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -452,17 +447,6 @@ under the License.
-
-
-
-
-
-
-
-
-
-
-
@@ -475,16 +459,6 @@ under the License.
-
-
-
-
-
-
-
-
-
-
@@ -494,26 +468,6 @@ under the License.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -531,7 +485,6 @@ under the License.
- compile Compile all files from main, ooxml and scratchpad
- test Run all unit tests from main, ooxml and scratchpad
- jar Produce jar files
- - jar-src Produce source-jar files
- assemble Produce the zipped distribution files
- site Generate all documentation (Requires Apache Forrest)
- dist Create a distribution (Requires Apache Forrest)
@@ -610,14 +563,17 @@ under the License.
-
-
+
+
-
-
+
+
+
+
+
@@ -719,6 +675,7 @@ under the License.
+
@@ -760,296 +717,358 @@ under the License.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
+
-
-
-
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
- Forking javac with max heap size ${ooxml.memory}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+ @{module}
+
+
+
+
+
+
+
+
-
+
+
+
-
-
-
-
-
-
-
-
-
-
+
-
+
+
+
+
-
-
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
@@ -1057,103 +1076,82 @@ under the License.
+
-
- sun/java2d/pipe/AAShapePipe.renderTiles(Lsun/java2d/SunGraphics2D;Ljava/awt/Shape;Lsun/java2d/pipe/AATileGenerator;[I)V
- sun/java2d/pipe/AlphaPaintPipe.renderPathTile(Ljava/lang/Object;[BIIIIII)V
- java/awt/TexturePaintContext.getRaster(IIII)Ljava/awt/image/Raster;
-
-
+
+
+ sun/java2d/pipe/AAShapePipe.renderTiles(Lsun/java2d/SunGraphics2D;Ljava/awt/Shape;Lsun/java2d/pipe/AATileGenerator;[I)V
+ sun/java2d/pipe/AlphaPaintPipe.renderPathTile(Ljava/lang/Object;[BIIIIII)V
+ java/awt/TexturePaintContext.getRaster(IIII)Ljava/awt/image/Raster;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ failureproperty="@{failureproperty}" showoutput="@{showoutput}" filtertrace="off">
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
@@ -1168,40 +1166,26 @@ under the License.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1264,7 +1276,12 @@ under the License.
-
+
+
+
+
+
+
@@ -1361,11 +1378,17 @@ under the License.
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
@@ -1377,6 +1400,7 @@ under the License.
+
@@ -1409,14 +1433,22 @@ under the License.
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
@@ -1443,44 +1475,43 @@ under the License.
-
+
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1496,12 +1527,28 @@ under the License.
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1520,41 +1567,97 @@ under the License.
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+ ${full.schema}${lite.exports}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
-
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1569,10 +1672,27 @@ under the License.
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1768,6 +1888,7 @@ under the License.
+
@@ -1781,6 +1902,9 @@ under the License.
+
+
+
@@ -1799,35 +1923,46 @@ under the License.
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
@@ -1893,7 +2028,7 @@ under the License.
-
+
@@ -2096,6 +2231,19 @@ under the License.
classname="de.thetaphi.forbiddenapis.ant.AntTask"
classpath="${forbidden.jar}"/>
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
-
-
-
+
+
+
-
-
-
-
-
+
+
+
+
+
+
@@ -2238,7 +2387,6 @@ under the License.
-
diff --git a/src/examples/src/org/apache/poi/crypt/examples/OOXMLPasswordsTry.java b/src/examples/src/org/apache/poi/crypt/examples/OOXMLPasswordsTry.java
deleted file mode 100644
index 261020fc79..0000000000
--- a/src/examples/src/org/apache/poi/crypt/examples/OOXMLPasswordsTry.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * ====================================================================
- * 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.crypt.examples;
-
-import java.io.File;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.security.GeneralSecurityException;
-import java.util.Optional;
-import java.util.function.Predicate;
-import java.util.stream.Stream;
-
-import org.apache.poi.poifs.crypt.Decryptor;
-import org.apache.poi.poifs.crypt.EncryptionInfo;
-import org.apache.poi.poifs.filesystem.POIFSFileSystem;
-
-/**
- * Tries a list of possible passwords for an OOXML protected file
- *
- * Note that this isn't very fast, and is aimed at when you have
- * just a few passwords to check.
- * For serious processing, you'd be best off grabbing the hash
- * out with POI or office2john.py, then running that against
- * "John The Ripper" or GPU enabled version of "hashcat"
- */
-public final class OOXMLPasswordsTry {
-
- private OOXMLPasswordsTry() {}
-
- @SuppressWarnings({"java:S106","java:S4823"})
- public static void main(String[] args) throws Exception {
- if (args.length < 2) {
- System.err.println("Use:");
- System.err.println(" OOXMLPasswordsTry ");
- System.exit(1);
- }
- String ooxml = args[0];
- String words = args[1];
-
- System.out.println("Trying passwords from " + words + " against " + ooxml);
- System.out.println();
-
- try (POIFSFileSystem fs = new POIFSFileSystem(new File(ooxml), true)) {
- EncryptionInfo info = new EncryptionInfo(fs);
- Decryptor d = Decryptor.getInstance(info);
-
- final long start = System.currentTimeMillis();
- final int[] count = { 0 };
- Predicate counter = s -> {
- if (++count[0] % 1000 == 0) {
- int secs = (int) ((System.currentTimeMillis() - start) / 1000);
- System.out.println("Done " + count[0] + " passwords, " + secs + " seconds, last password " + s);
- }
- return true;
- };
-
- // Try each password in turn, reporting progress
- try (Stream lines = Files.lines(Paths.get(words))) {
- Optional found = lines.filter(counter).filter(w -> isValid(d, w)).findFirst();
- System.out.println(found.map(s -> "Password found: " + s).orElse("Error - No password matched"));
- }
- }
- }
-
- private static boolean isValid(Decryptor dec, String password) {
- try {
- return dec.verifyPassword(password);
- } catch (GeneralSecurityException e) {
- return false;
- }
- }
-}
diff --git a/src/examples/src/org/apache/poi/examples/crypt/OOXMLPasswordsTry.java b/src/examples/src/org/apache/poi/examples/crypt/OOXMLPasswordsTry.java
new file mode 100644
index 0000000000..406d6dc335
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/crypt/OOXMLPasswordsTry.java
@@ -0,0 +1,89 @@
+/*
+ * ====================================================================
+ * 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.examples.crypt;
+
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.security.GeneralSecurityException;
+import java.util.Optional;
+import java.util.function.Predicate;
+import java.util.stream.Stream;
+
+import org.apache.poi.poifs.crypt.Decryptor;
+import org.apache.poi.poifs.crypt.EncryptionInfo;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+
+/**
+ * Tries a list of possible passwords for an OOXML protected file
+ *
+ * Note that this isn't very fast, and is aimed at when you have
+ * just a few passwords to check.
+ * For serious processing, you'd be best off grabbing the hash
+ * out with POI or office2john.py, then running that against
+ * "John The Ripper" or GPU enabled version of "hashcat"
+ */
+public final class OOXMLPasswordsTry {
+
+ private OOXMLPasswordsTry() {}
+
+ @SuppressWarnings({"java:S106","java:S4823"})
+ public static void main(String[] args) throws Exception {
+ if (args.length < 2) {
+ System.err.println("Use:");
+ System.err.println(" OOXMLPasswordsTry ");
+ System.exit(1);
+ }
+ String ooxml = args[0];
+ String words = args[1];
+
+ System.out.println("Trying passwords from " + words + " against " + ooxml);
+ System.out.println();
+
+ try (POIFSFileSystem fs = new POIFSFileSystem(new File(ooxml), true)) {
+ EncryptionInfo info = new EncryptionInfo(fs);
+ Decryptor d = Decryptor.getInstance(info);
+
+ final long start = System.currentTimeMillis();
+ final int[] count = { 0 };
+ Predicate counter = s -> {
+ if (++count[0] % 1000 == 0) {
+ int secs = (int) ((System.currentTimeMillis() - start) / 1000);
+ System.out.println("Done " + count[0] + " passwords, " + secs + " seconds, last password " + s);
+ }
+ return true;
+ };
+
+ // Try each password in turn, reporting progress
+ try (Stream lines = Files.lines(Paths.get(words))) {
+ Optional found = lines.filter(counter).filter(w -> isValid(d, w)).findFirst();
+ System.out.println(found.map(s -> "Password found: " + s).orElse("Error - No password matched"));
+ }
+ }
+ }
+
+ private static boolean isValid(Decryptor dec, String password) {
+ try {
+ return dec.verifyPassword(password);
+ } catch (GeneralSecurityException e) {
+ return false;
+ }
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/hpsf/CopyCompare.java b/src/examples/src/org/apache/poi/examples/hpsf/CopyCompare.java
new file mode 100644
index 0000000000..6c1189e375
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/hpsf/CopyCompare.java
@@ -0,0 +1,193 @@
+/* ====================================================================
+ 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.examples.hpsf;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+
+import org.apache.poi.hpsf.DocumentSummaryInformation;
+import org.apache.poi.hpsf.HPSFException;
+import org.apache.poi.hpsf.HPSFRuntimeException;
+import org.apache.poi.hpsf.PropertySet;
+import org.apache.poi.hpsf.PropertySetFactory;
+import org.apache.poi.hpsf.SummaryInformation;
+import org.apache.poi.hpsf.WritingNotSupportedException;
+import org.apache.poi.poifs.eventfilesystem.POIFSReader;
+import org.apache.poi.poifs.eventfilesystem.POIFSReaderEvent;
+import org.apache.poi.poifs.filesystem.DirectoryEntry;
+import org.apache.poi.poifs.filesystem.DocumentInputStream;
+import org.apache.poi.poifs.filesystem.EntryUtils;
+import org.apache.poi.poifs.filesystem.POIFSDocumentPath;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+import org.apache.poi.util.TempFile;
+
+/**
+ *
This class copies a POI file system to a new file and compares the copy
+ * with the original.
+ *
+ *
Property set streams are copied logically, i.e. the application
+ * establishes a {@link org.apache.poi.hpsf.PropertySet} of an original property
+ * set, creates a {@link org.apache.poi.hpsf.PropertySet} and writes the
+ * {@link org.apache.poi.hpsf.PropertySet} to the destination POI file
+ * system. - Streams which are no property set streams are copied bit by
+ * bit.
+ *
+ *
The comparison of the POI file systems is done logically. That means that
+ * the two disk files containing the POI file systems do not need to be
+ * exactly identical. However, both POI file systems must contain the same
+ * files, and most of these files must be bitwise identical. Property set
+ * streams, however, are compared logically: they must have the same sections
+ * with the same attributes, and the sections must contain the same properties.
+ * Details like the ordering of the properties do not matter.
+ */
+@SuppressWarnings({"java:S106","java:S4823"})
+public final class CopyCompare {
+ private CopyCompare() {}
+
+ /**
+ * Runs the example program. The application expects one or two arguments:
+ *
+ *
+ *
The first argument is the disk file name of the POI filesystem to copy.
+ *
The second argument is optional. If it is given, it is the name of
+ * a disk file the copy of the POI filesystem will be written to. If it is
+ * not given, the copy will be written to a temporary file which will be
+ * deleted at the end of the program.
+ *
+ *
+ * @param args Command-line arguments.
+ * @throws IOException if any I/O exception occurs.
+ * @throws UnsupportedEncodingException if a character encoding is not
+ * supported.
+ */
+ public static void main(final String[] args) throws IOException {
+ String originalFileName = null;
+ String copyFileName = null;
+
+ // Check the command-line arguments.
+ if (args.length == 1) {
+ originalFileName = args[0];
+ File f = TempFile.createTempFile("CopyOfPOIFileSystem-", ".ole2");
+ f.deleteOnExit();
+ copyFileName = f.getAbsolutePath();
+ } else if (args.length == 2) {
+ originalFileName = args[0];
+ copyFileName = args[1];
+ } else {
+ System.err.println("Usage: CopyCompare originPOIFS [copyPOIFS]");
+ System.exit(1);
+ }
+
+
+ // Read the origin POIFS using the eventing API.
+ final POIFSReader r = new POIFSReader();
+ try (final POIFSFileSystem poiFs = new POIFSFileSystem();
+ OutputStream fos = new FileOutputStream(copyFileName)) {
+ r.registerListener(e -> handleEvent(poiFs, e));
+ r.setNotifyEmptyDirectories(true);
+
+ r.read(new File(originalFileName));
+
+ // Write the new POIFS to disk.
+ poiFs.writeFilesystem(fos);
+ }
+
+ // Read all documents from the original POI file system and compare them with
+ // the equivalent document from the copy.
+ try (POIFSFileSystem opfs = new POIFSFileSystem(new File(originalFileName));
+ POIFSFileSystem cpfs = new POIFSFileSystem(new File(copyFileName))) {
+ final DirectoryEntry oRoot = opfs.getRoot();
+ final DirectoryEntry cRoot = cpfs.getRoot();
+ System.out.println(EntryUtils.areDirectoriesIdentical(oRoot, cRoot) ? "Equal" : "Not equal");
+ }
+ }
+
+ private interface InputStreamSupplier {
+ InputStream get() throws IOException, WritingNotSupportedException;
+ }
+
+ /**
+ * The method is called by POI's eventing API for each file in the origin POIFS.
+ */
+ public static void handleEvent(final POIFSFileSystem poiFs, final POIFSReaderEvent event) {
+ // The following declarations are shortcuts for accessing the "event" object.
+ final DocumentInputStream stream = event.getStream();
+
+ try {
+
+ // Find out whether the current document is a property set stream or not.
+ InputStreamSupplier su;
+ if (stream != null && PropertySet.isPropertySetStream(stream)) {
+ // Yes, the current document is a property set stream. Let's create
+ // a PropertySet instance from it.
+ PropertySet ps = PropertySetFactory.create(stream);
+
+ // Copy the property set to the destination POI file system.
+ final PropertySet mps;
+ if (ps instanceof DocumentSummaryInformation) {
+ mps = new DocumentSummaryInformation(ps);
+ } else if (ps instanceof SummaryInformation) {
+ mps = new SummaryInformation(ps);
+ } else {
+ mps = new PropertySet(ps);
+ }
+ su = mps::toInputStream;
+ } else {
+ // No, the current document is not a property set stream.
+ // We copy it unmodified to the destination POIFS.
+ su = event::getStream;
+ }
+
+ try (InputStream is = su.get()) {
+ final POIFSDocumentPath path = event.getPath();
+
+ // Ensures that the directory hierarchy for a document in a POI fileystem is in place.
+ // Get the root directory. It does not have to be created since it always exists in a POIFS.
+ DirectoryEntry de = poiFs.getRoot();
+ if (File.separator.equals(path.toString())) {
+ de.setStorageClsid(event.getStorageClassId());
+ }
+
+ for (int i=0; iThis is a sample application showing how to easily modify properties in
+ * the summary information and in the document summary information. The
+ * application reads the name of a POI filesystem from the command line and
+ * performs the following actions:
+ *
+ *
+ *
+ *
Open the POI filesystem.
+ *
+ *
Read the summary information.
+ *
+ *
Read and print the "author" property.
+ *
+ *
Change the author to "Rainer Klute".
+ *
+ *
Read the document summary information.
+ *
+ *
Read and print the "category" property.
+ *
+ *
Change the category to "POI example".
+ *
+ *
Read the custom properties (if available).
+ *
+ *
Insert a new custom property.
+ *
+ *
Write the custom properties back to the document summary
+ * information.
+ *
+ *
Write the summary information to the POI filesystem.
+ *
+ *
Write the document summary information to the POI filesystem.
+ *
+ *
Write the POI filesystem back to the original file.
+ *
+ * @param args The command-line parameters.
+ */
+ public static void main(final String[] args) throws Exception {
+ /* Read the name of the POI filesystem to modify from the command line.
+ * For brevity to boundary check is performed on the command-line
+ * arguments. */
+ File summaryFile = new File(args[0]);
+
+ /* Open the POI filesystem. */
+ try (POIFSFileSystem poifs = new POIFSFileSystem(summaryFile, false)) {
+
+ /* Read the summary information. */
+ DirectoryEntry dir = poifs.getRoot();
+ SummaryInformation si;
+ try {
+ si = (SummaryInformation) PropertySetFactory.create(
+ dir, SummaryInformation.DEFAULT_STREAM_NAME);
+ } catch (FileNotFoundException ex) {
+ // There is no summary information yet. We have to create a new one
+ si = PropertySetFactory.newSummaryInformation();
+ }
+ assert(si != null);
+
+ /* Change the author to "Rainer Klute". Any former author value will
+ * be lost. If there has been no author yet, it will be created. */
+ si.setAuthor("Rainer Klute");
+ System.out.println("Author changed to " + si.getAuthor() + ".");
+
+
+ /* Handling the document summary information is analogous to handling
+ * the summary information. An additional feature, however, are the
+ * custom properties. */
+
+ /* Read the document summary information. */
+ DocumentSummaryInformation dsi;
+ try {
+ dsi = (DocumentSummaryInformation) PropertySetFactory.create(
+ dir, DocumentSummaryInformation.DEFAULT_STREAM_NAME);
+ } catch (FileNotFoundException ex) {
+ /* There is no document summary information yet. We have to create a
+ * new one. */
+ dsi = PropertySetFactory.newDocumentSummaryInformation();
+ }
+ assert(dsi != null);
+
+ /* Change the category to "POI example". Any former category value will
+ * be lost. If there has been no category yet, it will be created. */
+ dsi.setCategory("POI example");
+ System.out.println("Category changed to " + dsi.getCategory() + ".");
+
+ /* Read the custom properties. If there are no custom properties yet,
+ * the application has to create a new CustomProperties object. It will
+ * serve as a container for custom properties. */
+ CustomProperties customProperties = dsi.getCustomProperties();
+ if (customProperties == null)
+ customProperties = new CustomProperties();
+
+ /* Insert some custom properties into the container. */
+ customProperties.put("Key 1", "Value 1");
+ customProperties.put("Schl\u00fcssel 2", "Wert 2");
+ customProperties.put("Sample Number", 12345);
+ customProperties.put("Sample Boolean", Boolean.TRUE);
+ customProperties.put("Sample Date", new Date());
+
+ /* Read a custom property. */
+ Object value = customProperties.get("Sample Number");
+ System.out.println("Custom Sample Number is now " + value);
+
+ /* Write the custom properties back to the document summary
+ * information. */
+ dsi.setCustomProperties(customProperties);
+
+ /* Write the summary information and the document summary information
+ * to the POI filesystem. */
+ si.write(dir, SummaryInformation.DEFAULT_STREAM_NAME);
+ dsi.write(dir, DocumentSummaryInformation.DEFAULT_STREAM_NAME);
+
+ /* Write the POI filesystem back to the original file. Please note that
+ * in production code you should take care when write directly to the
+ * origin, to make sure you don't loose things on error */
+ poifs.writeFilesystem();
+ }
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/hpsf/ReadCustomPropertySets.java b/src/examples/src/org/apache/poi/examples/hpsf/ReadCustomPropertySets.java
new file mode 100644
index 0000000000..ca8cba1ce9
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/hpsf/ReadCustomPropertySets.java
@@ -0,0 +1,109 @@
+/* ====================================================================
+ 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.examples.hpsf;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+import org.apache.poi.hpsf.HPSFRuntimeException;
+import org.apache.poi.hpsf.NoPropertySetStreamException;
+import org.apache.poi.hpsf.Property;
+import org.apache.poi.hpsf.PropertySet;
+import org.apache.poi.hpsf.PropertySetFactory;
+import org.apache.poi.hpsf.Section;
+import org.apache.poi.poifs.eventfilesystem.POIFSReader;
+import org.apache.poi.poifs.eventfilesystem.POIFSReaderEvent;
+
+/**
+ *
Sample application showing how to read a document's custom property set.
+ * Call it with the document's file name as command-line parameter.
+ *
+ *
Explanations can be found in the HPSF HOW-TO.
+ */
+@SuppressWarnings({"java:S106","java:S4823"})
+public final class ReadCustomPropertySets {
+
+ private ReadCustomPropertySets() {}
+
+ /**
+ *
Runs the example program.
+ *
+ * @param args Command-line arguments (unused).
+ * @throws IOException if any I/O exception occurs.
+ */
+ public static void main(final String[] args) throws IOException {
+ final String filename = args[0];
+ POIFSReader r = new POIFSReader();
+
+ /* Register a listener for *all* documents. */
+ r.registerListener(ReadCustomPropertySets::processPOIFSReaderEvent);
+ r.read(new File(filename));
+ }
+
+
+ public static void processPOIFSReaderEvent(final POIFSReaderEvent event) {
+ final String streamName = event.getPath() + event.getName();
+ PropertySet ps;
+ try {
+ ps = PropertySetFactory.create(event.getStream());
+ } catch (NoPropertySetStreamException ex) {
+ out("No property set stream: \"" + streamName + "\"");
+ return;
+ } catch (Exception ex) {
+ throw new HPSFRuntimeException("Property set stream \"" + streamName + "\": " + ex);
+ }
+
+ /* Print the name of the property set stream: */
+ out("Property set stream \"" + streamName + "\":");
+
+ /* Print the number of sections: */
+ final long sectionCount = ps.getSectionCount();
+ out(" No. of sections: " + sectionCount);
+
+ /* Print the list of sections: */
+ List sections = ps.getSections();
+ int nr = 0;
+ for (Section sec : sections) {
+ /* Print a single section: */
+ out(" Section " + nr++ + ":");
+ String s = sec.getFormatID().toString();
+ s = s.substring(0, s.length() - 1);
+ out(" Format ID: " + s);
+
+ /* Print the number of properties in this section. */
+ int propertyCount = sec.getPropertyCount();
+ out(" No. of properties: " + propertyCount);
+
+ /* Print the properties: */
+ Property[] properties = sec.getProperties();
+ for (Property p : properties) {
+ /* Print a single property: */
+ long id = p.getID();
+ long type = p.getType();
+ Object value = p.getValue();
+ out(" Property ID: " + id + ", type: " + type +
+ ", value: " + value);
+ }
+ }
+ }
+
+ private static void out(final String msg) {
+ System.out.println(msg);
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/hpsf/ReadTitle.java b/src/examples/src/org/apache/poi/examples/hpsf/ReadTitle.java
new file mode 100644
index 0000000000..b901a89a3d
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/hpsf/ReadTitle.java
@@ -0,0 +1,64 @@
+/* ====================================================================
+ 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.examples.hpsf;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.poi.hpsf.PropertySetFactory;
+import org.apache.poi.hpsf.SummaryInformation;
+import org.apache.poi.poifs.eventfilesystem.POIFSReader;
+import org.apache.poi.poifs.eventfilesystem.POIFSReaderEvent;
+
+/**
+ *
Sample application showing how to read a OLE 2 document's
+ * title. Call it with the document's file name as command line
+ * parameter.
+ *
+ *
Explanations can be found in the HPSF HOW-TO.
+ */
+@SuppressWarnings({"java:S106","java:S4823"})
+public final class ReadTitle {
+ private ReadTitle() {}
+
+ /**
+ *
Runs the example program.
+ *
+ * @param args Command-line arguments. The first command-line argument must
+ * be the name of a POI filesystem to read.
+ * @throws IOException if any I/O exception occurs.
+ */
+ public static void main(final String[] args) throws IOException {
+ final String filename = args[0];
+ POIFSReader r = new POIFSReader();
+ r.registerListener(ReadTitle::processPOIFSReaderEvent, SummaryInformation.DEFAULT_STREAM_NAME);
+ r.read(new File(filename));
+ }
+
+
+ private static void processPOIFSReaderEvent(final POIFSReaderEvent event) {
+ SummaryInformation si;
+ try {
+ si = (SummaryInformation) PropertySetFactory.create(event.getStream());
+ } catch (Exception ex) {
+ throw new RuntimeException("Property set stream \"" + event.getPath() + event.getName() + "\": " + ex);
+ }
+ final String title = si.getTitle();
+ System.out.println(title != null ? "Title: \"" + title + "\"" : "Document has no title.");
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/hpsf/WriteAuthorAndTitle.java b/src/examples/src/org/apache/poi/examples/hpsf/WriteAuthorAndTitle.java
new file mode 100644
index 0000000000..94ceaf6c03
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/hpsf/WriteAuthorAndTitle.java
@@ -0,0 +1,174 @@
+/* ====================================================================
+ 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.examples.hpsf;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.poi.hpsf.HPSFRuntimeException;
+import org.apache.poi.hpsf.MarkUnsupportedException;
+import org.apache.poi.hpsf.NoPropertySetStreamException;
+import org.apache.poi.hpsf.PropertySet;
+import org.apache.poi.hpsf.PropertySetFactory;
+import org.apache.poi.hpsf.Section;
+import org.apache.poi.hpsf.SummaryInformation;
+import org.apache.poi.hpsf.Variant;
+import org.apache.poi.hpsf.WritingNotSupportedException;
+import org.apache.poi.hpsf.wellknown.PropertyIDMap;
+import org.apache.poi.poifs.eventfilesystem.POIFSReader;
+import org.apache.poi.poifs.eventfilesystem.POIFSReaderEvent;
+import org.apache.poi.poifs.filesystem.DirectoryEntry;
+import org.apache.poi.poifs.filesystem.DocumentInputStream;
+import org.apache.poi.poifs.filesystem.POIFSDocumentPath;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+
+/**
+ *
This class is a sample application which shows how to write or modify the
+ * author and title property of an OLE 2 document. This could be done in two
+ * different ways:
+ *
+ *
+ *
+ *
The first approach is to open the OLE 2 file as a POI filesystem
+ * (see class {@link POIFSFileSystem}), read the summary information property
+ * set (see classes {@link SummaryInformation} and {@link PropertySet}), write
+ * the author and title properties into it and write the property set back into
+ * the POI filesystem.
+ *
+ *
The second approach does not modify the original POI filesystem, but
+ * instead creates a new one. All documents from the original POIFS are copied
+ * to the destination POIFS, except for the summary information stream. The
+ * latter is modified by setting the author and title property before writing
+ * it to the destination POIFS. It there are several summary information streams
+ * in the original POIFS - e.g. in subordinate directories - they are modified
+ * just the same.
+ *
+ *
+ *
+ *
This sample application takes the second approach. It expects the name of
+ * the existing POI filesystem's name as its first command-line parameter and
+ * the name of the output POIFS as the second command-line argument. The
+ * program then works as described above: It copies nearly all documents
+ * unmodified from the input POI filesystem to the output POI filesystem. If it
+ * encounters a summary information stream it reads its properties. Then it sets
+ * the "author" and "title" properties to new values and writes the modified
+ * summary information stream into the output file.
+ *
+ *
Further explanations can be found in the HPSF HOW-TO.
+ */
+@SuppressWarnings({"java:S106","java:S4823"})
+public final class WriteAuthorAndTitle {
+ private WriteAuthorAndTitle() {}
+
+ /**
+ *
Runs the example program.
+ *
+ * @param args Command-line arguments. The first command-line argument must
+ * be the name of a POI filesystem to read.
+ * @throws IOException if any I/O exception occurs.
+ */
+ public static void main(final String[] args) throws IOException {
+ /* Check whether we have exactly two command-line arguments. */
+ if (args.length != 2) {
+ System.err.println("Usage: WriteAuthorAndTitle originPOIFS destinationPOIFS");
+ System.exit(1);
+ }
+
+ /* Read the names of the origin and destination POI filesystems. */
+ final String srcName = args[0];
+ final String dstName = args[1];
+
+ /* Read the origin POIFS using the eventing API. The real work is done
+ * in the class ModifySICopyTheRest which is registered here as a
+ * POIFSReader. */
+ try (POIFSFileSystem poifs = new POIFSFileSystem();
+ OutputStream out = new FileOutputStream(dstName)) {
+ final POIFSReader r = new POIFSReader();
+ r.registerListener(e -> handleEvent(poifs, e));
+ r.read(new File(srcName));
+
+ /* Write the new POIFS to disk. */
+ poifs.writeFilesystem(out);
+ }
+ }
+
+ private interface InputStreamSupplier {
+ InputStream get() throws IOException, WritingNotSupportedException;
+ }
+
+ /**
+ * The method is called by POI's eventing API for each file in the origin POIFS.
+ */
+ private static void handleEvent(final POIFSFileSystem poiFs, final POIFSReaderEvent event) {
+ // The following declarations are shortcuts for accessing the "event" object.
+ final DocumentInputStream stream = event.getStream();
+
+ try {
+ final InputStreamSupplier isSup;
+
+ // Find out whether the current document is a property set stream or not.
+ if (PropertySet.isPropertySetStream(stream)) {
+ // Yes, the current document is a property set stream. Let's create a PropertySet instance from it.
+ PropertySet ps = PropertySetFactory.create(stream);
+
+ // Now we know that we really have a property set.
+ // The next step is to find out whether it is a summary information or not.
+ if (ps.isSummaryInformation()) {
+ // Create a mutable property set as a copy of the original read-only property set.
+ ps = new PropertySet(ps);
+
+ // Retrieve the section containing the properties to modify.
+ // A summary information property set contains exactly one section.
+ final Section s = ps.getSections().get(0);
+
+ // Set the properties.
+ s.setProperty(PropertyIDMap.PID_AUTHOR, Variant.VT_LPSTR, "Rainer Klute");
+ s.setProperty(PropertyIDMap.PID_TITLE, Variant.VT_LPWSTR, "Test");
+ }
+
+ isSup = ps::toInputStream;
+ } else {
+ // No, the current document is not a property set stream. We copy it unmodified to the destination POIFS.
+ isSup = event::getStream;
+ }
+
+ try (InputStream is = isSup.get()) {
+ final POIFSDocumentPath path = event.getPath();
+
+ // Ensures that the directory hierarchy for a document in a POI fileystem is in place.
+ // Get the root directory. It does not have to be created since it always exists in a POIFS.
+ DirectoryEntry de = poiFs.getRoot();
+
+ for (int i=0; iThis class is a simple sample application showing how to create a property
+ * set and write it to disk.
+ */
+@SuppressWarnings({"java:S106","java:S4823"})
+public final class WriteTitle {
+
+ private WriteTitle() {}
+
+ /**
+ *
Runs the example program.
+ *
+ * @param args Command-line arguments. The first and only command-line
+ * argument is the name of the POI file system to create.
+ * @throws IOException if any I/O exception occurs.
+ * @throws WritingNotSupportedException if HPSF does not (yet) support
+ * writing a certain property type.
+ */
+ public static void main(final String[] args)
+ throws WritingNotSupportedException, IOException
+ {
+ /* Check whether we have exactly one command-line argument. */
+ if (args.length != 1)
+ {
+ System.err.println("Usage: " + WriteTitle.class.getName() + "destinationPOIFS");
+ System.exit(1);
+ }
+
+ final String fileName = args[0];
+
+ /* Create a mutable property set. Initially it contains a single section
+ * with no properties. */
+ final PropertySet mps = new PropertySet();
+
+ /* Retrieve the section the property set already contains. */
+ final Section ms = mps.getSections().get(0);
+
+ /* Turn the property set into a summary information property. This is
+ * done by setting the format ID of its first section to
+ * SectionIDMap.SUMMARY_INFORMATION_ID. */
+ ms.setFormatID(SummaryInformation.FORMAT_ID);
+
+ /* Create an empty property. */
+ final Property p = new Property();
+
+ /* Fill the property with appropriate settings so that it specifies the
+ * document's title. */
+ p.setID(PropertyIDMap.PID_TITLE);
+ p.setType(Variant.VT_LPWSTR);
+ p.setValue("Sample title");
+
+ /* Place the property into the section. */
+ ms.setProperty(p);
+
+ /* Create the POI file system the property set is to be written to.
+ * For writing the property set into a POI file system it has to be
+ * handed over to the POIFS.createDocument() method as an input stream
+ * which produces the bytes making out the property set stream. */
+ try (final POIFSFileSystem poiFs = new POIFSFileSystem();
+ final InputStream is = mps.toInputStream();
+ final FileOutputStream fos = new FileOutputStream(fileName)) {
+
+ /* Create the summary information property set in the POI file
+ * system. It is given the default name most (if not all) summary
+ * information property sets have. */
+ poiFs.createDocument(is, SummaryInformation.DEFAULT_STREAM_NAME);
+
+ /* Write the whole POI file system to a disk file. */
+ poiFs.writeFilesystem(fos);
+ }
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/hslf/ApacheconEU08.java b/src/examples/src/org/apache/poi/examples/hslf/ApacheconEU08.java
new file mode 100644
index 0000000000..53d3c46077
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/hslf/ApacheconEU08.java
@@ -0,0 +1,460 @@
+/* ====================================================================
+ 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.examples.hslf;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.Graphics2D;
+import java.awt.Rectangle;
+import java.awt.geom.Rectangle2D;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.List;
+
+import org.apache.poi.hslf.usermodel.HSLFSlideShow;
+import org.apache.poi.sl.draw.DrawTableShape;
+import org.apache.poi.sl.draw.SLGraphics;
+import org.apache.poi.sl.usermodel.AutoShape;
+import org.apache.poi.sl.usermodel.GroupShape;
+import org.apache.poi.sl.usermodel.ShapeType;
+import org.apache.poi.sl.usermodel.Slide;
+import org.apache.poi.sl.usermodel.SlideShow;
+import org.apache.poi.sl.usermodel.TableCell;
+import org.apache.poi.sl.usermodel.TableShape;
+import org.apache.poi.sl.usermodel.TextBox;
+import org.apache.poi.sl.usermodel.TextParagraph;
+import org.apache.poi.sl.usermodel.TextRun;
+import org.apache.poi.sl.usermodel.TextShape.TextPlaceholder;
+import org.apache.poi.sl.usermodel.VerticalAlignment;
+
+/**
+ * Presentation for Fast Feather Track on ApacheconEU 2008
+ *
+ * @author Yegor Kozlov
+ */
+@SuppressWarnings("java:S1192")
+public final class ApacheconEU08 {
+
+ private ApacheconEU08() {}
+
+ public static void main(String[] args) throws IOException {
+ // use HSLFSlideShow or XMLSlideShow
+ try (SlideShow,?> ppt = new HSLFSlideShow()) {
+ ppt.setPageSize(new Dimension(720, 540));
+
+ slide1(ppt);
+ slide2(ppt);
+ slide3(ppt);
+ slide4(ppt);
+ slide5(ppt);
+ slide6(ppt);
+ slide7(ppt);
+ slide8(ppt);
+ slide9(ppt);
+ slide10(ppt);
+ slide11(ppt);
+ slide12(ppt);
+
+ String ext = ppt.getClass().getName().contains("HSLF") ? "ppt" : "pptx";
+ try (FileOutputStream out = new FileOutputStream("apachecon_eu_08." + ext)) {
+ ppt.write(out);
+ }
+ }
+ }
+
+ public static void slide1(SlideShow,?> ppt) throws IOException {
+ Slide,?> slide = ppt.createSlide();
+
+ TextBox,?> box1 = slide.createTextBox();
+ box1.setTextPlaceholder(TextPlaceholder.CENTER_TITLE);
+ box1.setText("POI-HSLF");
+ box1.setAnchor(new Rectangle(54, 78, 612, 115));
+
+ TextBox,?> box2 = slide.createTextBox();
+ box2.setTextPlaceholder(TextPlaceholder.CENTER_BODY);
+ box2.setText("Java API To Access Microsoft PowerPoint Format Files");
+ box2.setAnchor(new Rectangle(108, 204, 504, 138));
+
+ TextBox,?> box3 = slide.createTextBox();
+ box3.getTextParagraphs().get(0).getTextRuns().get(0).setFontSize(32d);
+ box3.setText(
+ "Yegor Kozlov\r" +
+ "yegor - apache - org");
+ box3.setHorizontalCentered(true);
+ box3.setAnchor(new Rectangle(206, 348, 310, 84));
+ }
+
+ public static void slide2(SlideShow,?> ppt) throws IOException {
+ Slide,?> slide = ppt.createSlide();
+
+ TextBox,?> box1 = slide.createTextBox();
+ box1.setTextPlaceholder(TextPlaceholder.TITLE);
+ box1.setText("What is HSLF?");
+ box1.setAnchor(new Rectangle(36, 21, 648, 90));
+
+ TextBox,?> box2 = slide.createTextBox();
+ box2.setTextPlaceholder(TextPlaceholder.BODY);
+ box2.setText("HorribleSLideshowFormat is the POI Project's pure Java implementation " +
+ "of the Powerpoint binary file format. \r" +
+ "POI sub-project since 2005\r" +
+ "Started by Nick Burch, Yegor Kozlov joined soon after");
+ box2.setAnchor(new Rectangle(36, 126, 648, 356));
+ }
+
+ public static void slide3(SlideShow,?> ppt) throws IOException {
+ Slide,?> slide = ppt.createSlide();
+
+ TextBox,?> box1 = slide.createTextBox();
+ box1.setTextPlaceholder(TextPlaceholder.TITLE);
+ box1.setText("HSLF in a Nutshell");
+ box1.setAnchor(new Rectangle(36, 15, 648, 65));
+
+ TextBox,?> box2 = slide.createTextBox();
+ box2.setTextPlaceholder(TextPlaceholder.BODY);
+ box2.setText(
+ "HSLF provides a way to read, create and modify MS PowerPoint presentations\r" +
+ "Pure Java API - you don't need PowerPoint to read and write *.ppt files\r" +
+ "Comprehensive support of PowerPoint objects\r" +
+ "Rich text\r" +
+ "Tables\r" +
+ "Shapes\r" +
+ "Pictures\r" +
+ "Master slides\r" +
+ "Access to low level data structures"
+ );
+
+ List extends TextParagraph,?,?>> tp = box2.getTextParagraphs();
+ for (int i : new byte[]{0,1,2,8}) {
+ tp.get(i).getTextRuns().get(0).setFontSize(28d);
+ }
+ for (int i : new byte[]{3,4,5,6,7}) {
+ tp.get(i).getTextRuns().get(0).setFontSize(24d);
+ tp.get(i).setIndentLevel(1);
+ }
+ box2.setAnchor(new Rectangle(36, 80, 648, 400));
+ }
+
+ public static void slide4(SlideShow,?> ppt) throws IOException {
+ Slide,?> slide = ppt.createSlide();
+
+ String[][] txt1 = {
+ {"Note"},
+ {"This presentation was created programmatically using POI HSLF"}
+ };
+ TableShape,?> table1 = slide.createTable(2, 1);
+ for (int i = 0; i < txt1.length; i++) {
+ for (int j = 0; j < txt1[i].length; j++) {
+ TableCell,?> cell = table1.getCell(i, j);
+ cell.setText(txt1[i][j]);
+ TextRun rt = cell.getTextParagraphs().get(0).getTextRuns().get(0);
+ rt.setFontSize(10d);
+ rt.setFontFamily("Arial");
+ rt.setBold(true);
+ if(i == 0){
+ rt.setFontSize(32d);
+ rt.setFontColor(Color.white);
+ cell.setFillColor(new Color(0, 153, 204));
+ } else {
+ rt.setFontSize(28d);
+ cell.setFillColor(new Color(235, 239, 241));
+ }
+ cell.setVerticalAlignment(VerticalAlignment.MIDDLE);
+ }
+ }
+
+ DrawTableShape dts = new DrawTableShape(table1);
+ dts.setAllBorders(1.0, Color.black);
+ dts.setOutsideBorders(4.0);
+
+ table1.setColumnWidth(0, 450);
+ table1.setRowHeight(0, 50);
+ table1.setRowHeight(1, 80);
+
+ Dimension dim = ppt.getPageSize();
+ Rectangle2D oldAnchor = table1.getAnchor();
+ table1.setAnchor(new Rectangle2D.Double((dim.width-450)/2d, 100, oldAnchor.getWidth(), oldAnchor.getHeight()));
+
+ TextBox,?> box1 = slide.createTextBox();
+ box1.setHorizontalCentered(true);
+ box1.getTextParagraphs().get(0).getTextRuns().get(0).setFontSize(24d);
+ box1.setText("The source code is available at\r" +
+ "http://people.apache.org/~yegor/apachecon_eu08/");
+ box1.setAnchor(new Rectangle(80, 356, 553, 65));
+ }
+
+ public static void slide5(SlideShow,?> ppt) throws IOException {
+ Slide,?> slide = ppt.createSlide();
+
+ TextBox,?> box1 = slide.createTextBox();
+ box1.setTextPlaceholder(TextPlaceholder.TITLE);
+ box1.setText("HSLF in Action - 1\rData Extraction");
+ box1.setAnchor(new Rectangle(36, 21, 648, 100));
+
+ TextBox,?> box2 = slide.createTextBox();
+ box2.setTextPlaceholder(TextPlaceholder.BODY);
+ box2.setText(
+ "Text from slides and notes\r" +
+ "Images\r" +
+ "Shapes and their properties (type, position in the slide, color, font, etc.)");
+ box2.setAnchor(new Rectangle(36, 150, 648, 300));
+ }
+
+ public static void slide6(SlideShow,?> ppt) throws IOException {
+ Slide,?> slide = ppt.createSlide();
+
+ TextBox,?> box1 = slide.createTextBox();
+ box1.setTextPlaceholder(TextPlaceholder.TITLE);
+ box1.setText("HSLF in Action - 2");
+ box1.setAnchor(new Rectangle(36, 20, 648, 90));
+
+ TextBox,?> box2 = slide.createTextBox();
+ box2.getTextParagraphs().get(0).getTextRuns().get(0).setFontSize(18d);
+ box2.setText("Creating a simple presentation from scratch");
+ box2.setAnchor(new Rectangle(170, 100, 364, 30));
+
+ TextBox,?> box3 = slide.createTextBox();
+ TextRun rt3 = box3.getTextParagraphs().get(0).getTextRuns().get(0);
+ rt3.setFontFamily("Courier New");
+ rt3.setFontSize(8d);
+ box3.setText(
+ "SlideShow ppt = new SlideShow();\u000b" +
+ "Slide slide = ppt.createSlide();\u000b" +
+ "\u000b" +
+ "TextBox box2 = new TextBox();\u000b" +
+ "box2.setHorizontalAlignment(TextBox.AlignCenter);\u000b" +
+ "box2.setVerticalAlignment(TextBox.AnchorMiddle);\u000b" +
+ "box2.getTextRun().setText(\"Java Code\");\u000b" +
+ "box2.getFill().setForegroundColor(new Color(187, 224, 227));\u000b" +
+ "box2.setLineColor(Color.black);\u000b" +
+ "box2.setLineWidth(0.75);\u000b" +
+ "box2.setAnchor(new Rectangle(66, 243, 170, 170));\u000b" +
+ "slide.addShape(box2);\u000b" +
+ "\u000b" +
+ "TextBox box3 = new TextBox();\u000b" +
+ "box3.setHorizontalAlignment(TextBox.AlignCenter);\u000b" +
+ "box3.setVerticalAlignment(TextBox.AnchorMiddle);\u000b" +
+ "box3.getTextRun().setText(\"*.ppt file\");\u000b" +
+ "box3.setLineWidth(0.75);\u000b" +
+ "box3.setLineColor(Color.black);\u000b" +
+ "box3.getFill().setForegroundColor(new Color(187, 224, 227));\u000b" +
+ "box3.setAnchor(new Rectangle(473, 243, 170, 170));\u000b" +
+ "slide.addShape(box3);\u000b" +
+ "\u000b" +
+ "AutoShape box4 = new AutoShape(ShapeTypes.Arrow);\u000b" +
+ "box4.getFill().setForegroundColor(new Color(187, 224, 227));\u000b" +
+ "box4.setLineWidth(0.75);\u000b" +
+ "box4.setLineColor(Color.black);\u000b" +
+ "box4.setAnchor(new Rectangle(253, 288, 198, 85));\u000b" +
+ "slide.addShape(box4);\u000b" +
+ "\u000b" +
+ "FileOutputStream out = new FileOutputStream(\"hslf-demo.ppt\");\u000b" +
+ "ppt.write(out);\u000b" +
+ "out.close();");
+ box3.setAnchor(new Rectangle(30, 150, 618, 411));
+ box3.setHorizontalCentered(true);
+ }
+
+ public static void slide7(SlideShow,?> ppt) throws IOException {
+ Slide,?> slide = ppt.createSlide();
+
+ TextBox,?> box2 = slide.createTextBox();
+ box2.setHorizontalCentered(true);
+ box2.setVerticalAlignment(VerticalAlignment.MIDDLE);
+ box2.setText("Java Code");
+ box2.setFillColor(new Color(187, 224, 227));
+ box2.setStrokeStyle(0.75, Color.black);
+ box2.setAnchor(new Rectangle(66, 243, 170, 170));
+
+ TextBox,?> box3 = slide.createTextBox();
+ box3.setHorizontalCentered(true);
+ box3.setVerticalAlignment(VerticalAlignment.MIDDLE);
+ box3.setText("*.ppt file");
+ box3.setFillColor(new Color(187, 224, 227));
+ box3.setStrokeStyle(0.75, Color.black);
+ box3.setAnchor(new Rectangle(473, 243, 170, 170));
+
+ AutoShape,?> box4 = slide.createAutoShape();
+ box4.setShapeType(ShapeType.RIGHT_ARROW);
+ box4.setFillColor(new Color(187, 224, 227));
+ box4.setStrokeStyle(0.75, Color.black);
+ box4.setAnchor(new Rectangle(253, 288, 198, 85));
+ }
+
+ public static void slide8(SlideShow,?> ppt) throws IOException {
+ Slide,?> slide = ppt.createSlide();
+
+ TextBox,?> box1 = slide.createTextBox();
+ box1.setTextPlaceholder(TextPlaceholder.TITLE);
+ box1.setText("Wait, there is more!");
+ box1.setAnchor(new Rectangle(36, 21, 648, 90));
+
+ TextBox,?> box2 = slide.createTextBox();
+ box2.setTextPlaceholder(TextPlaceholder.BODY);
+ box2.setText(
+ "Rich text\r" +
+ "Tables\r" +
+ "Pictures (JPEG, PNG, BMP, WMF, PICT)\r" +
+ "Comprehensive formatting features");
+ box2.setAnchor(new Rectangle(36, 126, 648, 356));
+ }
+
+ public static void slide9(SlideShow,?> ppt) throws IOException {
+ Slide,?> slide = ppt.createSlide();
+
+ TextBox,?> box1 = slide.createTextBox();
+ box1.setTextPlaceholder(TextPlaceholder.TITLE);
+ box1.setText("HSLF in Action - 3");
+ box1.setAnchor(new Rectangle(36, 20, 648, 50));
+
+ TextBox,?> box2 = slide.createTextBox();
+ box2.getTextParagraphs().get(0).getTextRuns().get(0).setFontSize(18d);
+ box2.setText("PPGraphics2D: PowerPoint Graphics2D driver");
+ box2.setAnchor(new Rectangle(178, 70, 387, 30));
+
+ TextBox,?> box3 = slide.createTextBox();
+ TextRun rt3 = box3.getTextParagraphs().get(0).getTextRuns().get(0);
+ rt3.setFontFamily("Courier New");
+ rt3.setFontSize(8d);
+ box3.setText(
+ "//bar chart data. The first value is the bar color, the second is the width\u000b" +
+ "Object[] def = new Object[]{\u000b" +
+ " Color.yellow, new Integer(100),\u000b" +
+ " Color.green, new Integer(150),\u000b" +
+ " Color.gray, new Integer(75),\u000b" +
+ " Color.red, new Integer(200),\u000b" +
+ "};\u000b" +
+ "\u000b" +
+ "SlideShow ppt = new SlideShow();\u000b" +
+ "Slide slide = ppt.createSlide();\u000b" +
+ "\u000b" +
+ "ShapeGroup group = new ShapeGroup();\u000b" +
+ "//define position of the drawing in the slide\u000b" +
+ "Rectangle bounds = new java.awt.Rectangle(200, 100, 350, 300);\u000b" +
+ "group.setAnchor(bounds);\u000b" +
+ "slide.addShape(group);\u000b" +
+ "Graphics2D graphics = new PPGraphics2D(group);\u000b" +
+ "\u000b" +
+ "//draw a simple bar graph\u000b" +
+ "int x = bounds.x + 50, y = bounds.y + 50;\u000b" +
+ "graphics.setFont(new Font(\"Arial\", Font.BOLD, 10));\u000b" +
+ "for (int i = 0, idx = 1; i < def.length; i+=2, idx++) {\u000b" +
+ " graphics.setColor(Color.black);\u000b" +
+ " int width = ((Integer)def[i+1]).intValue();\u000b" +
+ " graphics.drawString(\"Q\" + idx, x-20, y+20);\u000b" +
+ " graphics.drawString(width + \"%\", x + width + 10, y + 20);\u000b" +
+ " graphics.setColor((Color)def[i]);\u000b" +
+ " graphics.fill(new Rectangle(x, y, width, 30));\u000b" +
+ " y += 40;\u000b" +
+ "}\u000b" +
+ "graphics.setColor(Color.black);\u000b" +
+ "graphics.setFont(new Font(\"Arial\", Font.BOLD, 14));\u000b" +
+ "graphics.draw(bounds);\u000b" +
+ "graphics.drawString(\"Performance\", x + 70, y + 40);\u000b" +
+ "\u000b" +
+ "FileOutputStream out = new FileOutputStream(\"hslf-demo.ppt\");\u000b" +
+ "ppt.write(out);\u000b" +
+ "out.close();");
+ box3.setAnchor(new Rectangle(96, 110, 499, 378));
+ box3.setHorizontalCentered(true);
+ }
+
+ public static void slide10(SlideShow,?> ppt) throws IOException {
+ //bar chart data. The first value is the bar color, the second is the width
+ Object[] def = new Object[]{
+ Color.yellow, 100,
+ Color.green, 150,
+ Color.gray, 75,
+ Color.red, 200,
+ };
+
+ Slide,?> slide = ppt.createSlide();
+
+ GroupShape,?> group = slide.createGroup();
+ //define position of the drawing in the slide
+ Rectangle bounds = new java.awt.Rectangle(200, 100, 350, 300);
+ group.setAnchor(bounds);
+ Graphics2D graphics = new SLGraphics(group);
+
+ //draw a simple bar graph
+ int x = bounds.x + 50;
+ int y = bounds.y + 50;
+ graphics.setFont(new Font("Arial", Font.BOLD, 10));
+ for (int i = 0, idx = 1; i < def.length; i+=2, idx++) {
+ graphics.setColor(Color.black);
+ int width = (Integer) def[i + 1];
+ graphics.drawString("Q" + idx, x-20, y+20);
+ graphics.drawString(width + "%", x + width + 10, y + 20);
+ graphics.setColor((Color)def[i]);
+ graphics.fill(new Rectangle(x, y, width, 30));
+ y += 40;
+ }
+ graphics.setColor(Color.black);
+ graphics.setFont(new Font("Arial", Font.BOLD, 14));
+ graphics.draw(bounds);
+ graphics.drawString("Performance", x + 70, y + 40);
+
+ }
+
+ public static void slide11(SlideShow,?> ppt) throws IOException {
+ Slide,?> slide = ppt.createSlide();
+
+ TextBox,?> box1 = slide.createTextBox();
+ box1.setTextPlaceholder(TextPlaceholder.TITLE);
+ box1.setText("HSLF Development Plans");
+ box1.setAnchor(new Rectangle(36, 21, 648, 90));
+
+ TextBox,?> box2 = slide.createTextBox();
+ box2.setTextPlaceholder(TextPlaceholder.BODY);
+ box2.setText(
+ "Support for more PowerPoint functionality\r" +
+ "Rendering slides into java.awt.Graphics2D\r" +
+ "A way to export slides into images or other formats\r" +
+ "Integration with Apache FOP - Formatting Objects Processor\r" +
+ "Transformation of XSL-FO into PPT\r" +
+ "PPT2PDF transcoder"
+ );
+
+ List extends TextParagraph,?,?>> tp = box2.getTextParagraphs();
+ for (int i : new byte[]{0,1,3}) {
+ tp.get(i).getTextRuns().get(0).setFontSize(28d);
+ }
+ for (int i : new byte[]{2,4,5}) {
+ tp.get(i).getTextRuns().get(0).setFontSize(24d);
+ tp.get(i).setIndentLevel(1);
+ }
+
+ box2.setAnchor(new Rectangle(36, 126, 648, 400));
+ }
+
+ public static void slide12(SlideShow,?> ppt) throws IOException {
+ Slide,?> slide = ppt.createSlide();
+
+ TextBox,?> box1 = slide.createTextBox();
+ box1.setTextPlaceholder(TextPlaceholder.CENTER_TITLE);
+ box1.setText("Questions?");
+ box1.setAnchor(new Rectangle(54, 167, 612, 115));
+
+ TextBox,?> box2 = slide.createTextBox();
+ box2.setTextPlaceholder(TextPlaceholder.CENTER_BODY);
+ box2.setText(
+ "https://poi.apache.org/hslf/\r" +
+ "http://people.apache.org/~yegor");
+ box2.setAnchor(new Rectangle(108, 306, 504, 138));
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/hslf/BulletsDemo.java b/src/examples/src/org/apache/poi/examples/hslf/BulletsDemo.java
new file mode 100644
index 0000000000..5ab8b86a39
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/hslf/BulletsDemo.java
@@ -0,0 +1,60 @@
+/* ====================================================================
+ 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.examples.hslf;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.poi.hslf.usermodel.HSLFSlide;
+import org.apache.poi.hslf.usermodel.HSLFSlideShow;
+import org.apache.poi.hslf.usermodel.HSLFTextBox;
+import org.apache.poi.hslf.usermodel.HSLFTextParagraph;
+
+/**
+ * How to create a single-level bulleted list
+ * and change some of the bullet attributes
+ */
+public final class BulletsDemo {
+
+ public static void main(String[] args) throws IOException {
+ try (HSLFSlideShow ppt = new HSLFSlideShow()) {
+ HSLFSlide slide = ppt.createSlide();
+
+ HSLFTextBox shape = new HSLFTextBox();
+ HSLFTextParagraph rt = shape.getTextParagraphs().get(0);
+ rt.getTextRuns().get(0).setFontSize(42d);
+ rt.setBullet(true);
+ rt.setIndent(0d); //bullet offset
+ rt.setLeftMargin(50d); //text offset (should be greater than bullet offset)
+ rt.setBulletChar('\u263A'); //bullet character
+ shape.setText(
+ "January\r" +
+ "February\r" +
+ "March\r" +
+ "April");
+ slide.addShape(shape);
+
+ shape.setAnchor(new java.awt.Rectangle(50, 50, 500, 300)); //position of the text box in the slide
+ slide.addShape(shape);
+
+ try (FileOutputStream out = new FileOutputStream("bullets.ppt")) {
+ ppt.write(out);
+ }
+ }
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/hslf/CreateHyperlink.java b/src/examples/src/org/apache/poi/examples/hslf/CreateHyperlink.java
new file mode 100644
index 0000000000..be8a227d1a
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/hslf/CreateHyperlink.java
@@ -0,0 +1,64 @@
+/* ====================================================================
+ 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.examples.hslf;
+
+import java.awt.Rectangle;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.poi.hslf.usermodel.HSLFHyperlink;
+import org.apache.poi.hslf.usermodel.HSLFSlide;
+import org.apache.poi.hslf.usermodel.HSLFSlideShow;
+import org.apache.poi.hslf.usermodel.HSLFTextBox;
+
+/**
+ * Demonstrates how to create hyperlinks in PowerPoint presentations
+ */
+public final class CreateHyperlink {
+
+ private CreateHyperlink() {}
+
+ public static void main(String[] args) throws IOException {
+ try (HSLFSlideShow ppt = new HSLFSlideShow()) {
+ HSLFSlide slideA = ppt.createSlide();
+ ppt.createSlide();
+ HSLFSlide slideC = ppt.createSlide();
+
+ // link to a URL
+ HSLFTextBox textBox1 = slideA.createTextBox();
+ textBox1.setText("Apache POI");
+ textBox1.setAnchor(new Rectangle(100, 100, 200, 50));
+
+ HSLFHyperlink link1 = textBox1.getTextParagraphs().get(0).getTextRuns().get(0).createHyperlink();
+ link1.linkToUrl("http://www.apache.org");
+ link1.setLabel(textBox1.getText());
+
+ // link to another slide
+ HSLFTextBox textBox2 = slideA.createTextBox();
+ textBox2.setText("Go to slide #3");
+ textBox2.setAnchor(new Rectangle(100, 300, 200, 50));
+
+ HSLFHyperlink link2 = textBox2.getTextParagraphs().get(0).getTextRuns().get(0).createHyperlink();
+ link2.linkToSlide(slideC);
+
+ try (FileOutputStream out = new FileOutputStream("hyperlink.ppt")) {
+ ppt.write(out);
+ }
+ }
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/hslf/DataExtraction.java b/src/examples/src/org/apache/poi/examples/hslf/DataExtraction.java
new file mode 100644
index 0000000000..6996dc8751
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/hslf/DataExtraction.java
@@ -0,0 +1,148 @@
+/* ====================================================================
+ 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.examples.hslf;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.poi.hslf.usermodel.HSLFObjectData;
+import org.apache.poi.hslf.usermodel.HSLFObjectShape;
+import org.apache.poi.hslf.usermodel.HSLFPictureData;
+import org.apache.poi.hslf.usermodel.HSLFPictureShape;
+import org.apache.poi.hslf.usermodel.HSLFShape;
+import org.apache.poi.hslf.usermodel.HSLFSlide;
+import org.apache.poi.hslf.usermodel.HSLFSlideShow;
+import org.apache.poi.hslf.usermodel.HSLFSoundData;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.hwpf.HWPFDocument;
+import org.apache.poi.hwpf.usermodel.Paragraph;
+import org.apache.poi.hwpf.usermodel.Range;
+import org.apache.poi.util.IOUtils;
+
+/**
+ * Demonstrates how you can extract misc embedded data from a ppt file
+ */
+@SuppressWarnings({"java:S106","java:S4823"})
+public final class DataExtraction {
+
+ private DataExtraction() {}
+
+ public static void main(String[] args) throws Exception {
+
+ if (args.length == 0) {
+ usage();
+ return;
+ }
+
+ try (FileInputStream fis = new FileInputStream(args[0]);
+ HSLFSlideShow ppt = new HSLFSlideShow(fis)) {
+
+ //extract all sound files embedded in this presentation
+ HSLFSoundData[] sound = ppt.getSoundData();
+ for (HSLFSoundData aSound : sound) {
+ handleSound(aSound);
+ }
+
+ int oleIdx = -1;
+ int picIdx = -1;
+ for (HSLFSlide slide : ppt.getSlides()) {
+ //extract embedded OLE documents
+ for (HSLFShape shape : slide.getShapes()) {
+ if (shape instanceof HSLFObjectShape) {
+ handleShape((HSLFObjectShape) shape, ++oleIdx);
+ } else if (shape instanceof HSLFPictureShape) {
+ handlePicture((HSLFPictureShape) shape, ++picIdx);
+ }
+ }
+ }
+ }
+ }
+
+ private static void handleShape(HSLFObjectShape ole, int oleIdx) throws IOException {
+ HSLFObjectData data = ole.getObjectData();
+ String name = ole.getInstanceName();
+ switch (name == null ? "" : name) {
+ case "Worksheet":
+ //read xls
+ handleWorkbook(data, name, oleIdx);
+ break;
+ case "Document":
+ //read the word document
+ handleDocument(data, name, oleIdx);
+ break;
+ default:
+ handleUnknown(data, ole.getProgId(), oleIdx);
+ break;
+ }
+
+ }
+
+ private static void handleWorkbook(HSLFObjectData data, String name, int oleIdx) throws IOException {
+ try (InputStream is = data.getInputStream();
+ HSSFWorkbook wb = new HSSFWorkbook(is);
+ FileOutputStream out = new FileOutputStream(name + "-(" + (oleIdx) + ").xls")) {
+ wb.write(out);
+ }
+ }
+
+ private static void handleDocument(HSLFObjectData data, String name, int oleIdx) throws IOException {
+ try (InputStream is = data.getInputStream();
+ HWPFDocument doc = new HWPFDocument(is);
+ FileOutputStream out = new FileOutputStream(name + "-(" + (oleIdx) + ").doc")) {
+ Range r = doc.getRange();
+ for (int k = 0; k < r.numParagraphs(); k++) {
+ Paragraph p = r.getParagraph(k);
+ System.out.println(p.text());
+ }
+
+ //save on disk
+ doc.write(out);
+ }
+ }
+
+ private static void handleUnknown(HSLFObjectData data, String name, int oleIdx) throws IOException {
+ try (InputStream is = data.getInputStream();
+ FileOutputStream out = new FileOutputStream(name + "-" + (oleIdx + 1) + ".dat")) {
+ IOUtils.copy(is, out);
+ }
+ }
+
+ private static void handlePicture(HSLFPictureShape p, int picIdx) throws IOException {
+ HSLFPictureData data = p.getPictureData();
+ String ext = data.getType().extension;
+ try (FileOutputStream out = new FileOutputStream("pict-" + picIdx + ext)) {
+ out.write(data.getData());
+ }
+ }
+
+ private static void handleSound(HSLFSoundData aSound) throws IOException {
+ String type = aSound.getSoundType(); //*.wav
+ String name = aSound.getSoundName(); //typically file name
+
+ //save the sound on disk
+ try (FileOutputStream out = new FileOutputStream(name + type)) {
+ out.write(aSound.getData());
+ }
+ }
+
+ private static void usage(){
+ System.out.println("Usage: DataExtraction ppt");
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/hslf/Graphics2DDemo.java b/src/examples/src/org/apache/poi/examples/hslf/Graphics2DDemo.java
new file mode 100644
index 0000000000..938b52e6d2
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/hslf/Graphics2DDemo.java
@@ -0,0 +1,82 @@
+/* ====================================================================
+ 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.examples.hslf;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Graphics2D;
+import java.awt.Rectangle;
+import java.io.FileOutputStream;
+
+import org.apache.poi.hslf.usermodel.HSLFGroupShape;
+import org.apache.poi.hslf.usermodel.HSLFSlide;
+import org.apache.poi.hslf.usermodel.HSLFSlideShow;
+import org.apache.poi.sl.draw.SLGraphics;
+
+/**
+ * Demonstrates how to draw into a slide using the HSLF Graphics2D driver.
+ */
+public final class Graphics2DDemo {
+
+ private Graphics2DDemo() {}
+
+ /**
+ * A simple bar chart demo
+ */
+ public static void main(String[] args) throws Exception {
+ try (HSLFSlideShow ppt = new HSLFSlideShow();
+ FileOutputStream out = new FileOutputStream("hslf-graphics.ppt")) {
+ //bar chart data. The first value is the bar color, the second is the width
+ Object[] def = {
+ Color.yellow, 40,
+ Color.green, 60,
+ Color.gray, 30,
+ Color.red, 80,
+ };
+
+ HSLFSlide slide = ppt.createSlide();
+
+ HSLFGroupShape group = new HSLFGroupShape();
+ //define position of the drawing in the slide
+ Rectangle bounds = new Rectangle(200, 100, 350, 300);
+ group.setAnchor(bounds);
+ group.setInteriorAnchor(new Rectangle(0, 0, 100, 100));
+ slide.addShape(group);
+ Graphics2D graphics = new SLGraphics(group);
+
+ //draw a simple bar graph
+ int x = 10, y = 10;
+ graphics.setFont(new Font("Arial", Font.BOLD, 10));
+ for (int i = 0, idx = 1; i < def.length; i += 2, idx++) {
+ graphics.setColor(Color.black);
+ int width = (Integer) def[i + 1];
+ graphics.drawString("Q" + idx, x - 5, y + 10);
+ graphics.drawString(width + "%", x + width + 3, y + 10);
+ graphics.setColor((Color) def[i]);
+ graphics.fill(new Rectangle(x, y, width, 10));
+ y += 15;
+ }
+ graphics.setColor(Color.black);
+ graphics.setFont(new Font("Arial", Font.BOLD, 14));
+ graphics.draw(group.getInteriorAnchor());
+ graphics.drawString("Performance", x + 30, y + 10);
+
+ ppt.write(out);
+ }
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/hslf/HeadersFootersDemo.java b/src/examples/src/org/apache/poi/examples/hslf/HeadersFootersDemo.java
new file mode 100644
index 0000000000..4dd4349df5
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/hslf/HeadersFootersDemo.java
@@ -0,0 +1,50 @@
+/* ====================================================================
+ 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.examples.hslf;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.poi.hslf.model.HeadersFooters;
+import org.apache.poi.hslf.usermodel.HSLFSlideShow;
+
+/**
+ * Demonstrates how to set headers / footers
+ */
+public final class HeadersFootersDemo {
+ private HeadersFootersDemo() {}
+
+ public static void main(String[] args) throws IOException {
+ try (HSLFSlideShow ppt = new HSLFSlideShow()) {
+ HeadersFooters slideHeaders = ppt.getSlideHeadersFooters();
+ slideHeaders.setFootersText("Created by POI-HSLF");
+ slideHeaders.setSlideNumberVisible(true);
+ slideHeaders.setDateTimeText("custom date time");
+
+ HeadersFooters notesHeaders = ppt.getNotesHeadersFooters();
+ notesHeaders.setFootersText("My notes footers");
+ notesHeaders.setHeaderText("My notes header");
+
+ ppt.createSlide();
+
+ try (FileOutputStream out = new FileOutputStream("headers_footers.ppt")) {
+ ppt.write(out);
+ }
+ }
+ }
+
+}
diff --git a/src/examples/src/org/apache/poi/examples/hslf/Hyperlinks.java b/src/examples/src/org/apache/poi/examples/hslf/Hyperlinks.java
new file mode 100644
index 0000000000..307178d0e0
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/hslf/Hyperlinks.java
@@ -0,0 +1,77 @@
+/* ====================================================================
+ 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.examples.hslf;
+
+import java.io.FileInputStream;
+import java.util.List;
+import java.util.Locale;
+
+import org.apache.poi.hslf.usermodel.HSLFHyperlink;
+import org.apache.poi.hslf.usermodel.HSLFSimpleShape;
+import org.apache.poi.hslf.usermodel.HSLFSlide;
+import org.apache.poi.hslf.usermodel.HSLFSlideShow;
+import org.apache.poi.hslf.usermodel.HSLFTextParagraph;
+import org.apache.poi.hslf.usermodel.HSLFTextRun;
+
+/**
+ * Demonstrates how to read hyperlinks from a presentation
+ */
+@SuppressWarnings({"java:S106", "java:S4823"})
+public final class Hyperlinks {
+
+ private Hyperlinks() {}
+
+ public static void main(String[] args) throws Exception {
+ for (String arg : args) {
+ try (FileInputStream is = new FileInputStream(arg);
+ HSLFSlideShow ppt = new HSLFSlideShow(is)) {
+
+ for (HSLFSlide slide : ppt.getSlides()) {
+ System.out.println("\nslide " + slide.getSlideNumber());
+
+ // read hyperlinks from the slide's text runs
+ System.out.println("- reading hyperlinks from the text runs");
+ slide.getTextParagraphs().stream().
+ flatMap(List::stream).
+ map(HSLFTextParagraph::getTextRuns).
+ flatMap(List::stream).
+ forEach(run -> out(run.getHyperlink(), run));
+
+ // in PowerPoint you can assign a hyperlink to a shape without text,
+ // for example to a Line object. The code below demonstrates how to
+ // read such hyperlinks
+ System.out.println("- reading hyperlinks from the slide's shapes");
+ slide.getShapes().stream().
+ filter(sh -> sh instanceof HSLFSimpleShape).
+ forEach(sh -> out(((HSLFSimpleShape) sh).getHyperlink(), null));
+ }
+ }
+ }
+ }
+
+ private static void out(HSLFHyperlink link, HSLFTextRun run) {
+ if (link == null) {
+ return;
+ }
+ String rawText = run == null ? null : run.getRawText();
+ //in ppt end index is inclusive
+ String formatStr = "title: %1$s, address: %2$s" + (rawText == null ? "" : ", start: %3$s, end: %4$s, substring: %5$s");
+ String line = String.format(Locale.ROOT, formatStr, link.getLabel(), link.getAddress(), link.getStartIndex(), link.getEndIndex(), rawText);
+ System.out.println(line);
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/hslf/SoundFinder.java b/src/examples/src/org/apache/poi/examples/hslf/SoundFinder.java
new file mode 100644
index 0000000000..0f6929fee7
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/hslf/SoundFinder.java
@@ -0,0 +1,69 @@
+/* ====================================================================
+ 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.examples.hslf;
+import java.io.FileInputStream;
+import java.io.IOException;
+
+import org.apache.poi.hslf.record.InteractiveInfoAtom;
+import org.apache.poi.hslf.record.RecordTypes;
+import org.apache.poi.hslf.usermodel.HSLFShape;
+import org.apache.poi.hslf.usermodel.HSLFSlide;
+import org.apache.poi.hslf.usermodel.HSLFSlideShow;
+import org.apache.poi.hslf.usermodel.HSLFSoundData;
+
+/**
+ * For each slide iterate over shapes and found associated sound data.
+ */
+@SuppressWarnings({"java:S106", "java:S4823"})
+public final class SoundFinder {
+ private SoundFinder() {}
+
+ public static void main(String[] args) throws IOException {
+ try (FileInputStream fis = new FileInputStream(args[0])) {
+ try (HSLFSlideShow ppt = new HSLFSlideShow(fis)) {
+ HSLFSoundData[] sounds = ppt.getSoundData();
+
+ for (HSLFSlide slide : ppt.getSlides()) {
+ for (HSLFShape shape : slide.getShapes()) {
+ int soundRef = getSoundReference(shape);
+ if (soundRef == -1) continue;
+
+
+ System.out.println("Slide[" + slide.getSlideNumber() + "], shape[" + shape.getShapeId() + "], soundRef: " + soundRef);
+ System.out.println(" " + sounds[soundRef].getSoundName());
+ System.out.println(" " + sounds[soundRef].getSoundType());
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Check if a given shape is associated with a sound.
+ * @return 0-based reference to a sound in the sound collection
+ * or -1 if the shape is not associated with a sound
+ */
+ private static int getSoundReference(HSLFShape shape){
+ int soundRef = -1;
+ //dive into the shape container and search for InteractiveInfoAtom
+ InteractiveInfoAtom info = shape.getClientDataRecord(RecordTypes.InteractiveInfo.typeID);
+ if (info != null && info.getAction() == InteractiveInfoAtom.ACTION_MEDIA) {
+ soundRef = info.getSoundRef();
+ }
+ return soundRef;
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/hslf/TableDemo.java b/src/examples/src/org/apache/poi/examples/hslf/TableDemo.java
new file mode 100644
index 0000000000..561ec717ab
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/hslf/TableDemo.java
@@ -0,0 +1,132 @@
+/* ====================================================================
+ 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.examples.hslf;
+
+import java.awt.Color;
+import java.io.FileOutputStream;
+
+import org.apache.poi.hslf.usermodel.HSLFSlide;
+import org.apache.poi.hslf.usermodel.HSLFSlideShow;
+import org.apache.poi.hslf.usermodel.HSLFTable;
+import org.apache.poi.hslf.usermodel.HSLFTableCell;
+import org.apache.poi.hslf.usermodel.HSLFTextRun;
+import org.apache.poi.sl.draw.DrawTableShape;
+import org.apache.poi.sl.usermodel.TextParagraph.TextAlign;
+import org.apache.poi.sl.usermodel.VerticalAlignment;
+
+/**
+ * Demonstrates how to create tables
+ */
+public final class TableDemo {
+
+ //test data for the first table
+ static final String[][] txt1 = {
+ {"INPUT FILE", "NUMBER OF RECORDS"},
+ {"Item File", "11,559"},
+ {"Vendor File", "502"},
+ {"Purchase History File - # of PO\u2019s\r(12/01/04 - 05/31/06)", "12,852"},
+ {"Purchase History File - # of PO Lines\r(12/01/04 - 05/31/06)", "53,523" },
+ {"Total PO History Spend", "$10,172,038"}
+ };
+
+ //test data for the second taable
+ static final String[][] txt2 = {
+ {"Data Source"},
+ {"CAS Internal Metrics - Item Master Summary\r" +
+ "CAS Internal Metrics - Vendor Summary\r" +
+ "CAS Internal Metrics - PO History Summary"}
+ };
+
+
+ public static void main(String[] args) throws Exception {
+ try (HSLFSlideShow ppt = new HSLFSlideShow()) {
+ HSLFSlide slide = ppt.createSlide();
+ create1stTable(slide);
+ create2ndTable(slide);
+
+ try (FileOutputStream out = new FileOutputStream("hslf-table.ppt")) {
+ ppt.write(out);
+ }
+ }
+ }
+
+ static void create1stTable(HSLFSlide slide) {
+ //six rows, two columns
+ HSLFTable table1 = slide.createTable(6, 2);
+ for (int i = 0; i < txt1.length; i++) {
+ for (int j = 0; j < txt1[i].length; j++) {
+ HSLFTableCell cell = table1.getCell(i, j);
+ HSLFTextRun rt = cell.getTextParagraphs().get(0).getTextRuns().get(0);
+ rt.setFontFamily("Arial");
+ rt.setFontSize(10d);
+ if(i == 0){
+ cell.getFill().setForegroundColor(new Color(227, 227, 227));
+ } else {
+ rt.setBold(true);
+ }
+ cell.setVerticalAlignment(VerticalAlignment.MIDDLE);
+ cell.setHorizontalCentered(true);
+ cell.setText(txt1[i][j]);
+ }
+ }
+
+ DrawTableShape dts1 = new DrawTableShape(table1);
+ dts1.setAllBorders(1.0, Color.black);
+
+ table1.setColumnWidth(0, 300);
+ table1.setColumnWidth(1, 150);
+
+ int pgWidth = slide.getSlideShow().getPageSize().width;
+ table1.moveTo((pgWidth - table1.getAnchor().getWidth())/2., 100.);
+ }
+
+ static void create2ndTable(HSLFSlide slide) {
+ //two rows, one column
+ HSLFTable table2 = slide.createTable(2, 1);
+ for (int i = 0; i < txt2.length; i++) {
+ for (int j = 0; j < txt2[i].length; j++) {
+ HSLFTableCell cell = table2.getCell(i, j);
+ HSLFTextRun rt = cell.getTextParagraphs().get(0).getTextRuns().get(0);
+ rt.setFontSize(10d);
+ rt.setFontFamily("Arial");
+ if(i == 0){
+ cell.getFill().setForegroundColor(new Color(0, 51, 102));
+ rt.setFontColor(Color.white);
+ rt.setBold(true);
+ rt.setFontSize(14d);
+ cell.setHorizontalCentered(true);
+ } else {
+ rt.getTextParagraph().setBullet(true);
+ rt.setFontSize(12d);
+ rt.getTextParagraph().setTextAlign(TextAlign.LEFT);
+ cell.setHorizontalCentered(false);
+ }
+ cell.setVerticalAlignment(VerticalAlignment.MIDDLE);
+ cell.setText(txt2[i][j]);
+ }
+ }
+ table2.setColumnWidth(0, 300);
+ table2.setRowHeight(0, 30);
+ table2.setRowHeight(1, 70);
+
+ DrawTableShape dts2 = new DrawTableShape(table2);
+ dts2.setOutsideBorders(Color.black, 1.0);
+
+ table2.moveTo(200, 400);
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/hsmf/Msg2txt.java b/src/examples/src/org/apache/poi/examples/hsmf/Msg2txt.java
new file mode 100644
index 0000000000..a4756a27d5
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/hsmf/Msg2txt.java
@@ -0,0 +1,158 @@
+/* ====================================================================
+ 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.examples.hsmf;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+
+import org.apache.poi.hsmf.MAPIMessage;
+import org.apache.poi.hsmf.datatypes.AttachmentChunks;
+import org.apache.poi.hsmf.exceptions.ChunkNotFoundException;
+
+/**
+ * Reads one or several Outlook MSG files and for each of them creates
+ * a text file from available chunks and a directory that contains
+ * attachments.
+ */
+@SuppressWarnings({"java:S106","java:S4823"})
+public class Msg2txt {
+
+ /**
+ * The stem used to create file names for the text file and the directory
+ * that contains the attachments.
+ */
+ private String fileNameStem;
+
+ /**
+ * The Outlook MSG file being processed.
+ */
+ private MAPIMessage msg;
+
+ public Msg2txt(String fileName) throws IOException {
+ fileNameStem = fileName;
+ if(fileNameStem.endsWith(".msg") || fileNameStem.endsWith(".MSG")) {
+ fileNameStem = fileNameStem.substring(0, fileNameStem.length() - 4);
+ }
+ msg = new MAPIMessage(fileName);
+ }
+
+ /**
+ * Processes the message.
+ *
+ * @throws IOException if an exception occurs while writing the message out
+ */
+ public void processMessage() throws IOException {
+ String txtFileName = fileNameStem + ".txt";
+ String attDirName = fileNameStem + "-att";
+ try (PrintWriter txtOut = new PrintWriter(txtFileName)) {
+ try {
+ String displayFrom = msg.getDisplayFrom();
+ txtOut.println("From: " + displayFrom);
+ } catch (ChunkNotFoundException e) {
+ // ignore
+ }
+ try {
+ String displayTo = msg.getDisplayTo();
+ txtOut.println("To: " + displayTo);
+ } catch (ChunkNotFoundException e) {
+ // ignore
+ }
+ try {
+ String displayCC = msg.getDisplayCC();
+ txtOut.println("CC: " + displayCC);
+ } catch (ChunkNotFoundException e) {
+ // ignore
+ }
+ try {
+ String displayBCC = msg.getDisplayBCC();
+ txtOut.println("BCC: " + displayBCC);
+ } catch (ChunkNotFoundException e) {
+ // ignore
+ }
+ try {
+ String subject = msg.getSubject();
+ txtOut.println("Subject: " + subject);
+ } catch (ChunkNotFoundException e) {
+ // ignore
+ }
+ try {
+ String body = msg.getTextBody();
+ txtOut.println(body);
+ } catch (ChunkNotFoundException e) {
+ System.err.println("No message body");
+ }
+
+ AttachmentChunks[] attachments = msg.getAttachmentFiles();
+ if (attachments.length > 0) {
+ File d = new File(attDirName);
+ if (d.mkdir()) {
+ for (AttachmentChunks attachment : attachments) {
+ processAttachment(attachment, d);
+ }
+ } else {
+ System.err.println("Can't create directory " + attDirName);
+ }
+ }
+ }
+ }
+
+ /**
+ * Processes a single attachment: reads it from the Outlook MSG file and
+ * writes it to disk as an individual file.
+ *
+ * @param attachment the chunk group describing the attachment
+ * @param dir the directory in which to write the attachment file
+ * @throws IOException when any of the file operations fails
+ */
+ public void processAttachment(AttachmentChunks attachment,
+ File dir) throws IOException {
+ String fileName = attachment.getAttachFileName().toString();
+ if(attachment.getAttachLongFileName() != null) {
+ fileName = attachment.getAttachLongFileName().toString();
+ }
+
+ File f = new File(dir, fileName);
+ try (OutputStream fileOut = new FileOutputStream(f)) {
+ fileOut.write(attachment.getAttachData().getValue());
+ }
+ }
+
+ /**
+ * Processes the list of arguments as a list of names of Outlook MSG files.
+ *
+ * @param args the list of MSG files to process
+ */
+ public static void main(String[] args) {
+ if(args.length <= 0) {
+ System.err.println("No files names provided");
+ } else {
+ for (String arg : args) {
+ try {
+ Msg2txt processor = new Msg2txt(arg);
+ processor.processMessage();
+ } catch (IOException e) {
+ System.err.println("Could not process " + arg + ": " + e);
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/examples/src/org/apache/poi/examples/hssf/eventusermodel/XLS2CSVmra.java b/src/examples/src/org/apache/poi/examples/hssf/eventusermodel/XLS2CSVmra.java
new file mode 100644
index 0000000000..42056d0716
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/hssf/eventusermodel/XLS2CSVmra.java
@@ -0,0 +1,327 @@
+/* ====================================================================
+ 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.examples.hssf.eventusermodel;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.poi.hssf.eventusermodel.EventWorkbookBuilder.SheetRecordCollectingListener;
+import org.apache.poi.hssf.eventusermodel.FormatTrackingHSSFListener;
+import org.apache.poi.hssf.eventusermodel.HSSFEventFactory;
+import org.apache.poi.hssf.eventusermodel.HSSFListener;
+import org.apache.poi.hssf.eventusermodel.HSSFRequest;
+import org.apache.poi.hssf.eventusermodel.MissingRecordAwareHSSFListener;
+import org.apache.poi.hssf.eventusermodel.dummyrecord.LastCellOfRowDummyRecord;
+import org.apache.poi.hssf.eventusermodel.dummyrecord.MissingCellDummyRecord;
+import org.apache.poi.hssf.model.HSSFFormulaParser;
+import org.apache.poi.hssf.record.BOFRecord;
+import org.apache.poi.hssf.record.BlankRecord;
+import org.apache.poi.hssf.record.BoolErrRecord;
+import org.apache.poi.hssf.record.BoundSheetRecord;
+import org.apache.poi.hssf.record.FormulaRecord;
+import org.apache.poi.hssf.record.LabelRecord;
+import org.apache.poi.hssf.record.LabelSSTRecord;
+import org.apache.poi.hssf.record.NoteRecord;
+import org.apache.poi.hssf.record.NumberRecord;
+import org.apache.poi.hssf.record.RKRecord;
+import org.apache.poi.hssf.record.SSTRecord;
+import org.apache.poi.hssf.record.StringRecord;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+
+/**
+ * A XLS -> CSV processor, that uses the MissingRecordAware
+ * EventModel code to ensure it outputs all columns and rows.
+ * @author Nick Burch
+ */
+@SuppressWarnings({"java:S106","java:S4823"})
+public class XLS2CSVmra implements HSSFListener {
+ private int minColumns;
+ private POIFSFileSystem fs;
+ private PrintStream output;
+
+ private int lastRowNumber;
+ private int lastColumnNumber;
+
+ /** Should we output the formula, or the value it has? */
+ private boolean outputFormulaValues = true;
+
+ /** For parsing Formulas */
+ private SheetRecordCollectingListener workbookBuildingListener;
+ private HSSFWorkbook stubWorkbook;
+
+ // Records we pick up as we process
+ private SSTRecord sstRecord;
+ private FormatTrackingHSSFListener formatListener;
+
+ /** So we known which sheet we're on */
+ private int sheetIndex = -1;
+ private BoundSheetRecord[] orderedBSRs;
+ private List boundSheetRecords = new ArrayList<>();
+
+ // For handling formulas with string results
+ private int nextRow;
+ private int nextColumn;
+ private boolean outputNextStringRecord;
+
+ /**
+ * Creates a new XLS -> CSV converter
+ * @param fs The POIFSFileSystem to process
+ * @param output The PrintStream to output the CSV to
+ * @param minColumns The minimum number of columns to output, or -1 for no minimum
+ */
+ public XLS2CSVmra(POIFSFileSystem fs, PrintStream output, int minColumns) {
+ this.fs = fs;
+ this.output = output;
+ this.minColumns = minColumns;
+ }
+
+ /**
+ * Creates a new XLS -> CSV converter
+ * @param filename The file to process
+ * @param minColumns The minimum number of columns to output, or -1 for no minimum
+ */
+ public XLS2CSVmra(String filename, int minColumns) throws IOException, FileNotFoundException {
+ this(
+ new POIFSFileSystem(new FileInputStream(filename)),
+ System.out, minColumns
+ );
+ }
+
+ /**
+ * Initiates the processing of the XLS file to CSV
+ */
+ public void process() throws IOException {
+ MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(this);
+ formatListener = new FormatTrackingHSSFListener(listener);
+
+ HSSFEventFactory factory = new HSSFEventFactory();
+ HSSFRequest request = new HSSFRequest();
+
+ if(outputFormulaValues) {
+ request.addListenerForAllRecords(formatListener);
+ } else {
+ workbookBuildingListener = new SheetRecordCollectingListener(formatListener);
+ request.addListenerForAllRecords(workbookBuildingListener);
+ }
+
+ factory.processWorkbookEvents(request, fs);
+ }
+
+ /**
+ * Main HSSFListener method, processes events, and outputs the
+ * CSV as the file is processed.
+ */
+ @Override
+ public void processRecord(org.apache.poi.hssf.record.Record record) {
+ int thisRow = -1;
+ int thisColumn = -1;
+ String thisStr = null;
+
+ switch (record.getSid())
+ {
+ case BoundSheetRecord.sid:
+ boundSheetRecords.add((BoundSheetRecord)record);
+ break;
+ case BOFRecord.sid:
+ BOFRecord br = (BOFRecord)record;
+ if(br.getType() == BOFRecord.TYPE_WORKSHEET) {
+ // Create sub workbook if required
+ if(workbookBuildingListener != null && stubWorkbook == null) {
+ stubWorkbook = workbookBuildingListener.getStubHSSFWorkbook();
+ }
+
+ // Output the worksheet name
+ // Works by ordering the BSRs by the location of
+ // their BOFRecords, and then knowing that we
+ // process BOFRecords in byte offset order
+ sheetIndex++;
+ if(orderedBSRs == null) {
+ orderedBSRs = BoundSheetRecord.orderByBofPosition(boundSheetRecords);
+ }
+ output.println();
+ output.println(
+ orderedBSRs[sheetIndex].getSheetname() +
+ " [" + (sheetIndex+1) + "]:"
+ );
+ }
+ break;
+
+ case SSTRecord.sid:
+ sstRecord = (SSTRecord) record;
+ break;
+
+ case BlankRecord.sid:
+ BlankRecord brec = (BlankRecord) record;
+
+ thisRow = brec.getRow();
+ thisColumn = brec.getColumn();
+ thisStr = "";
+ break;
+ case BoolErrRecord.sid:
+ BoolErrRecord berec = (BoolErrRecord) record;
+
+ thisRow = berec.getRow();
+ thisColumn = berec.getColumn();
+ thisStr = "";
+ break;
+
+ case FormulaRecord.sid:
+ FormulaRecord frec = (FormulaRecord) record;
+
+ thisRow = frec.getRow();
+ thisColumn = frec.getColumn();
+
+ if(outputFormulaValues) {
+ if(Double.isNaN( frec.getValue() )) {
+ // Formula result is a string
+ // This is stored in the next record
+ outputNextStringRecord = true;
+ nextRow = frec.getRow();
+ nextColumn = frec.getColumn();
+ } else {
+ thisStr = formatListener.formatNumberDateCell(frec);
+ }
+ } else {
+ thisStr = '"' +
+ HSSFFormulaParser.toFormulaString(stubWorkbook, frec.getParsedExpression()) + '"';
+ }
+ break;
+ case StringRecord.sid:
+ if(outputNextStringRecord) {
+ // String for formula
+ StringRecord srec = (StringRecord)record;
+ thisStr = srec.getString();
+ thisRow = nextRow;
+ thisColumn = nextColumn;
+ outputNextStringRecord = false;
+ }
+ break;
+
+ case LabelRecord.sid:
+ LabelRecord lrec = (LabelRecord) record;
+
+ thisRow = lrec.getRow();
+ thisColumn = lrec.getColumn();
+ thisStr = '"' + lrec.getValue() + '"';
+ break;
+ case LabelSSTRecord.sid:
+ LabelSSTRecord lsrec = (LabelSSTRecord) record;
+
+ thisRow = lsrec.getRow();
+ thisColumn = lsrec.getColumn();
+ if(sstRecord == null) {
+ thisStr = '"' + "(No SST Record, can't identify string)" + '"';
+ } else {
+ thisStr = '"' + sstRecord.getString(lsrec.getSSTIndex()).toString() + '"';
+ }
+ break;
+ case NoteRecord.sid:
+ NoteRecord nrec = (NoteRecord) record;
+
+ thisRow = nrec.getRow();
+ thisColumn = nrec.getColumn();
+ // TODO: Find object to match nrec.getShapeId()
+ thisStr = '"' + "(TODO)" + '"';
+ break;
+ case NumberRecord.sid:
+ NumberRecord numrec = (NumberRecord) record;
+
+ thisRow = numrec.getRow();
+ thisColumn = numrec.getColumn();
+
+ // Format
+ thisStr = formatListener.formatNumberDateCell(numrec);
+ break;
+ case RKRecord.sid:
+ RKRecord rkrec = (RKRecord) record;
+
+ thisRow = rkrec.getRow();
+ thisColumn = rkrec.getColumn();
+ thisStr = '"' + "(TODO)" + '"';
+ break;
+ default:
+ break;
+ }
+
+ // Handle new row
+ if(thisRow != -1 && thisRow != lastRowNumber) {
+ lastColumnNumber = -1;
+ }
+
+ // Handle missing column
+ if(record instanceof MissingCellDummyRecord) {
+ MissingCellDummyRecord mc = (MissingCellDummyRecord)record;
+ thisRow = mc.getRow();
+ thisColumn = mc.getColumn();
+ thisStr = "";
+ }
+
+ // If we got something to print out, do so
+ if(thisStr != null) {
+ if(thisColumn > 0) {
+ output.print(',');
+ }
+ output.print(thisStr);
+ }
+
+ // Update column and row count
+ if(thisRow > -1)
+ lastRowNumber = thisRow;
+ if(thisColumn > -1)
+ lastColumnNumber = thisColumn;
+
+ // Handle end of row
+ if(record instanceof LastCellOfRowDummyRecord) {
+ // Print out any missing commas if needed
+ if(minColumns > 0) {
+ // Columns are 0 based
+ if(lastColumnNumber == -1) { lastColumnNumber = 0; }
+ for(int i=lastColumnNumber; i<(minColumns); i++) {
+ output.print(',');
+ }
+ }
+
+ // We're onto a new row
+ lastColumnNumber = -1;
+
+ // End the row
+ output.println();
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ if(args.length < 1) {
+ System.err.println("Use:");
+ System.err.println(" XLS2CSVmra [min columns]");
+ System.exit(1);
+ }
+
+ int minColumns = -1;
+ if(args.length >= 2) {
+ minColumns = Integer.parseInt(args[1]);
+ }
+
+ XLS2CSVmra xls2csv = new XLS2CSVmra(args[0], minColumns);
+ xls2csv.process();
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/hssf/usermodel/AddDimensionedImage.java b/src/examples/src/org/apache/poi/examples/hssf/usermodel/AddDimensionedImage.java
new file mode 100644
index 0000000000..129f9327da
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/hssf/usermodel/AddDimensionedImage.java
@@ -0,0 +1,23 @@
+/* ====================================================================
+ 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.examples.hssf.usermodel;
+
+/* Placeholder - this is now handled in the Common SS example **/
+public class AddDimensionedImage extends org.apache.poi.examples.ss.AddDimensionedImage {
+}
\ No newline at end of file
diff --git a/src/examples/src/org/apache/poi/examples/hssf/usermodel/Alignment.java b/src/examples/src/org/apache/poi/examples/hssf/usermodel/Alignment.java
new file mode 100644
index 0000000000..25affcb0f8
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/hssf/usermodel/Alignment.java
@@ -0,0 +1,72 @@
+
+/* ====================================================================
+ 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.examples.hssf.usermodel;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.poi.hssf.usermodel.HSSFCell;
+import org.apache.poi.hssf.usermodel.HSSFCellStyle;
+import org.apache.poi.hssf.usermodel.HSSFRow;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.HorizontalAlignment;
+
+/**
+ * Shows how various alignment options work.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class Alignment {
+ public static void main(String[] args) throws IOException {
+ try (HSSFWorkbook wb = new HSSFWorkbook()) {
+ HSSFSheet sheet = wb.createSheet("new sheet");
+ HSSFRow row = sheet.createRow(2);
+ createCell(wb, row, 0, HorizontalAlignment.CENTER);
+ createCell(wb, row, 1, HorizontalAlignment.CENTER_SELECTION);
+ createCell(wb, row, 2, HorizontalAlignment.FILL);
+ createCell(wb, row, 3, HorizontalAlignment.GENERAL);
+ createCell(wb, row, 4, HorizontalAlignment.JUSTIFY);
+ createCell(wb, row, 5, HorizontalAlignment.LEFT);
+ createCell(wb, row, 6, HorizontalAlignment.RIGHT);
+
+ // Write the output to a file
+ try (FileOutputStream fileOut = new FileOutputStream("workbook.xls")) {
+ wb.write(fileOut);
+ }
+ }
+ }
+
+ /**
+ * Creates a cell and aligns it a certain way.
+ *
+ * @param wb the workbook
+ * @param row the row to create the cell in
+ * @param column the column number to create the cell in
+ * @param align the alignment for the cell.
+ */
+ private static void createCell(HSSFWorkbook wb, HSSFRow row, int column, HorizontalAlignment align) {
+ HSSFCell cell = row.createCell(column);
+ cell.setCellValue("Align It");
+ HSSFCellStyle cellStyle = wb.createCellStyle();
+ cellStyle.setAlignment(align);
+ cell.setCellStyle(cellStyle);
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/hssf/usermodel/BigExample.java b/src/examples/src/org/apache/poi/examples/hssf/usermodel/BigExample.java
new file mode 100644
index 0000000000..f50391ade8
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/hssf/usermodel/BigExample.java
@@ -0,0 +1,169 @@
+/* ====================================================================
+ 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.examples.hssf.usermodel;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.poi.hssf.usermodel.HSSFCell;
+import org.apache.poi.hssf.usermodel.HSSFCellStyle;
+import org.apache.poi.hssf.usermodel.HSSFDataFormat;
+import org.apache.poi.hssf.usermodel.HSSFFont;
+import org.apache.poi.hssf.usermodel.HSSFRow;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.hssf.util.HSSFColor.HSSFColorPredefined;
+import org.apache.poi.ss.usermodel.BorderStyle;
+import org.apache.poi.ss.usermodel.FillPatternType;
+
+/**
+ * Demonstrates many features of the user API at once. Used in the HOW-TO guide.
+ */
+public class BigExample {
+ public static void main(String[] args) throws IOException {
+ int rownum;
+
+ // create a new workbook
+ try (HSSFWorkbook wb = new HSSFWorkbook()) {
+ // create a new sheet
+ HSSFSheet s = wb.createSheet();
+ // declare a row object reference
+ HSSFRow r;
+ // declare a cell object reference
+ HSSFCell c;
+ // create 3 cell styles
+ HSSFCellStyle cs = wb.createCellStyle();
+ HSSFCellStyle cs2 = wb.createCellStyle();
+ HSSFCellStyle cs3 = wb.createCellStyle();
+ // create 2 fonts objects
+ HSSFFont f = wb.createFont();
+ HSSFFont f2 = wb.createFont();
+
+ //set font 1 to 12 point type
+ f.setFontHeightInPoints((short) 12);
+ //make it red
+ f.setColor(HSSFColorPredefined.RED.getIndex());
+ // make it bold
+ //arial is the default font
+ f.setBold(true);
+
+ //set font 2 to 10 point type
+ f2.setFontHeightInPoints((short) 10);
+ //make it the color at palette index 0xf (white)
+ f2.setColor(HSSFColorPredefined.WHITE.getIndex());
+ //make it bold
+ f2.setBold(true);
+
+ //set cell style
+ cs.setFont(f);
+ //set the cell format see HSSFDataFormat for a full list
+ cs.setDataFormat(HSSFDataFormat.getBuiltinFormat("($#,##0_);[Red]($#,##0)"));
+
+ //set a thin border
+ cs2.setBorderBottom(BorderStyle.THIN);
+ //fill w fg fill color
+ cs2.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+ // set foreground fill to red
+ cs2.setFillForegroundColor(HSSFColorPredefined.RED.getIndex());
+
+ // set the font
+ cs2.setFont(f2);
+
+ // set the sheet name to HSSF Test
+ wb.setSheetName(0, "HSSF Test");
+ // create a sheet with 300 rows (0-299)
+ for (rownum = 0; rownum < 300; rownum++) {
+ // create a row
+ r = s.createRow(rownum);
+ // on every other row
+ if ((rownum % 2) == 0) {
+ // make the row height bigger (in twips - 1/20 of a point)
+ r.setHeight((short) 0x249);
+ }
+
+ // create 50 cells (0-49) (the += 2 becomes apparent later
+ for (int cellnum = 0; cellnum < 50; cellnum += 2) {
+ // create a numeric cell
+ c = r.createCell(cellnum);
+ // do some goofy math to demonstrate decimals
+ c.setCellValue((rownum * 10000.0) + cellnum
+ + (rownum / 1000.0)
+ + (cellnum / 10000.0));
+
+ // on every other row
+ if ((rownum % 2) == 0) {
+ // set this cell to the first cell style we defined
+ c.setCellStyle(cs);
+ }
+
+ // create a string cell (see why += 2 in the
+ c = r.createCell(cellnum + 1);
+
+ // set the cell's string value to "TEST"
+ c.setCellValue("TEST");
+ // make this column a bit wider
+ s.setColumnWidth(cellnum + 1, (int) ((50 * 8) / ((double) 1 / 20)));
+
+ // on every other row
+ if ((rownum % 2) == 0) {
+ // set this to the white on red cell style
+ // we defined above
+ c.setCellStyle(cs2);
+ }
+
+ }
+ }
+
+ //draw a thick black border on the row at the bottom using BLANKS
+ // advance 2 rows
+ rownum++;
+ rownum++;
+
+ r = s.createRow(rownum);
+
+ // define the third style to be the default
+ // except with a thick black border at the bottom
+ cs3.setBorderBottom(BorderStyle.THICK);
+
+ //create 50 cells
+ for (int cellnum = 0; cellnum < 50; cellnum++) {
+ //create a blank type cell (no value)
+ c = r.createCell(cellnum);
+ // set it to the thick black border style
+ c.setCellStyle(cs3);
+ }
+
+ //end draw thick black border
+
+
+ // demonstrate adding/naming and deleting a sheet
+ // create a sheet, set its title then delete it
+ wb.createSheet();
+ wb.setSheetName(1, "DeletedSheet");
+ wb.removeSheetAt(1);
+ //end deleted sheet
+
+ // create a new file
+ try (FileOutputStream out = new FileOutputStream("workbook.xls")) {
+ // write the workbook to the output stream
+ // close our file (don't blow out our file handles
+ wb.write(out);
+ }
+ }
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/hssf/usermodel/Borders.java b/src/examples/src/org/apache/poi/examples/hssf/usermodel/Borders.java
new file mode 100644
index 0000000000..fba3b33214
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/hssf/usermodel/Borders.java
@@ -0,0 +1,64 @@
+/* ====================================================================
+ 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.examples.hssf.usermodel;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.poi.hssf.usermodel.HSSFCell;
+import org.apache.poi.hssf.usermodel.HSSFCellStyle;
+import org.apache.poi.hssf.usermodel.HSSFRow;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.hssf.util.HSSFColor.HSSFColorPredefined;
+import org.apache.poi.ss.usermodel.BorderStyle;
+
+/**
+ * Demonstrates how to create borders around cells.
+ */
+public class Borders {
+ public static void main(String[] args) throws IOException {
+ try (HSSFWorkbook wb = new HSSFWorkbook()) {
+ HSSFSheet sheet = wb.createSheet("new sheet");
+
+ // Create a row and put some cells in it. Rows are 0 based.
+ HSSFRow row = sheet.createRow(1);
+
+ // Create a cell and put a value in it.
+ HSSFCell cell = row.createCell(1);
+ cell.setCellValue(4);
+
+ // Style the cell with borders all around.
+ HSSFCellStyle style = wb.createCellStyle();
+ style.setBorderBottom(BorderStyle.THIN);
+ style.setBottomBorderColor(HSSFColorPredefined.BLACK.getIndex());
+ style.setBorderLeft(BorderStyle.THIN);
+ style.setLeftBorderColor(HSSFColorPredefined.GREEN.getIndex());
+ style.setBorderRight(BorderStyle.THIN);
+ style.setRightBorderColor(HSSFColorPredefined.BLUE.getIndex());
+ style.setBorderTop(BorderStyle.MEDIUM_DASHED);
+ style.setTopBorderColor(HSSFColorPredefined.ORANGE.getIndex());
+ cell.setCellStyle(style);
+
+ // Write the output to a file
+ try (FileOutputStream fileOut = new FileOutputStream("workbook.xls")) {
+ wb.write(fileOut);
+ }
+ }
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/hssf/usermodel/CellComments.java b/src/examples/src/org/apache/poi/examples/hssf/usermodel/CellComments.java
new file mode 100644
index 0000000000..6b6434b3e1
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/hssf/usermodel/CellComments.java
@@ -0,0 +1,124 @@
+/* ====================================================================
+ 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.examples.hssf.usermodel;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.poi.hssf.usermodel.HSSFComment;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.ClientAnchor;
+import org.apache.poi.ss.usermodel.Comment;
+import org.apache.poi.ss.usermodel.CreationHelper;
+import org.apache.poi.ss.usermodel.Drawing;
+import org.apache.poi.ss.usermodel.Font;
+import org.apache.poi.ss.usermodel.IndexedColors;
+import org.apache.poi.ss.usermodel.RichTextString;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.usermodel.WorkbookFactory;
+
+/**
+ * Demonstrates how to work with excel cell comments.
+ *
+ * Excel comment is a kind of a text shape,
+ * so inserting a comment is very similar to placing a text box in a worksheet
+ */
+public class CellComments {
+
+ public static void main(String[] args) throws IOException {
+ createWorkbook(false, ".xls");
+ createWorkbook(true, ".xlsx");
+ }
+
+ private static void createWorkbook(boolean xssf, String extension) throws IOException {
+ try (Workbook wb = WorkbookFactory.create(xssf)) {
+ Sheet sheet = wb.createSheet("Cell comments in POI " + extension);
+ CreationHelper creationHelper = wb.getCreationHelper();
+
+ // Create the drawing patriarch. This is the top level container for all shapes including cell comments.
+ Drawing> patr = sheet.createDrawingPatriarch();
+
+ //create a cell in row 3
+ Cell cell1 = sheet.createRow(3).createCell(1);
+ cell1.setCellValue(creationHelper.createRichTextString("Hello, World"));
+
+ //anchor defines size and position of the comment in worksheet
+ ClientAnchor clientAnchor = creationHelper.createClientAnchor();
+ clientAnchor.setCol1(4);
+ clientAnchor.setRow1(2);
+ clientAnchor.setCol2(6);
+ clientAnchor.setRow2(5);
+ Comment comment1 = patr.createCellComment(clientAnchor);
+
+ // set text in the comment
+ comment1.setString(creationHelper.createRichTextString("We can set comments in POI"));
+
+ //set comment author.
+ //you can see it in the status bar when moving mouse over the commented cell
+ comment1.setAuthor("Apache Software Foundation");
+
+ // The first way to assign comment to a cell is via Cell.setCellComment method
+ cell1.setCellComment(comment1);
+
+ //create another cell in row 6
+ Cell cell2 = sheet.createRow(6).createCell(1);
+ cell2.setCellValue(36.6);
+
+
+ clientAnchor = creationHelper.createClientAnchor();
+ clientAnchor.setCol1(4);
+ clientAnchor.setRow1(8);
+ clientAnchor.setCol2(6);
+ clientAnchor.setRow2(11);
+ Comment comment2 = patr.createCellComment(clientAnchor);
+ //modify background color of the comment, only available in HSSF currently
+ if (wb instanceof HSSFWorkbook) {
+ ((HSSFComment) comment2).setFillColor(204, 236, 255);
+ }
+
+ RichTextString string = creationHelper.createRichTextString("Normal body temperature");
+
+ //apply custom font to the text in the comment
+ Font font = wb.createFont();
+ font.setFontName("Arial");
+ font.setFontHeightInPoints((short) 10);
+ font.setBold(true);
+ font.setColor(IndexedColors.RED.getIndex());
+ string.applyFont(font);
+
+ comment2.setString(string);
+ comment2.setVisible(true); //by default comments are hidden. This one is always visible.
+
+ comment2.setAuthor("Bill Gates");
+
+ /*
+ * The second way to assign comment to a cell is to implicitly specify its row and column.
+ * Note, it is possible to set row and column of a non-existing cell.
+ * It works, the comment is visible.
+ */
+ comment2.setRow(6);
+ comment2.setColumn(1);
+
+ try (FileOutputStream out = new FileOutputStream("poi_comment" + extension)) {
+ wb.write(out);
+ }
+ }
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/hssf/usermodel/CellTypes.java b/src/examples/src/org/apache/poi/examples/hssf/usermodel/CellTypes.java
new file mode 100644
index 0000000000..5ba1656ff1
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/hssf/usermodel/CellTypes.java
@@ -0,0 +1,46 @@
+/* ====================================================================
+ 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.examples.hssf.usermodel;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Date;
+
+import org.apache.poi.hssf.usermodel.HSSFRow;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.FormulaError;
+
+public class CellTypes {
+ public static void main(String[] args) throws IOException {
+ try (HSSFWorkbook wb = new HSSFWorkbook()) {
+ HSSFSheet sheet = wb.createSheet("new sheet");
+ HSSFRow row = sheet.createRow(2);
+ row.createCell(0).setCellValue(1.1);
+ row.createCell(1).setCellValue(new Date());
+ row.createCell(2).setCellValue("a string");
+ row.createCell(3).setCellValue(true);
+ row.createCell(4).setCellErrorValue(FormulaError.NUM);
+
+ // Write the output to a file
+ try (FileOutputStream fileOut = new FileOutputStream("workbook.xls")) {
+ wb.write(fileOut);
+ }
+ }
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/hssf/usermodel/CreateCells.java b/src/examples/src/org/apache/poi/examples/hssf/usermodel/CreateCells.java
new file mode 100644
index 0000000000..8994d6f17e
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/hssf/usermodel/CreateCells.java
@@ -0,0 +1,55 @@
+/* ====================================================================
+ 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.examples.hssf.usermodel;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.poi.hssf.usermodel.HSSFCell;
+import org.apache.poi.hssf.usermodel.HSSFRow;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+
+/**
+ * Illustrates how to create cell values.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class CreateCells {
+ public static void main(String[] args) throws IOException {
+ try (HSSFWorkbook wb = new HSSFWorkbook()) {
+ HSSFSheet sheet = wb.createSheet("new sheet");
+
+ // Create a row and put some cells in it. Rows are 0 based.
+ HSSFRow row = sheet.createRow(0);
+ // Create a cell and put a value in it.
+ HSSFCell cell = row.createCell(0);
+ cell.setCellValue(1);
+
+ // Or do it on one line.
+ row.createCell(1).setCellValue(1.2);
+ row.createCell(2).setCellValue("This is a string");
+ row.createCell(3).setCellValue(true);
+
+ // Write the output to a file
+ try (FileOutputStream fileOut = new FileOutputStream("workbook.xls")) {
+ wb.write(fileOut);
+ }
+ }
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/hssf/usermodel/CreateDateCells.java b/src/examples/src/org/apache/poi/examples/hssf/usermodel/CreateDateCells.java
new file mode 100644
index 0000000000..b77e0d1474
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/hssf/usermodel/CreateDateCells.java
@@ -0,0 +1,62 @@
+/* ====================================================================
+ 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.examples.hssf.usermodel;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Date;
+
+import org.apache.poi.hssf.usermodel.HSSFCell;
+import org.apache.poi.hssf.usermodel.HSSFCellStyle;
+import org.apache.poi.hssf.usermodel.HSSFDataFormat;
+import org.apache.poi.hssf.usermodel.HSSFRow;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+
+/**
+ * An example on how to cells with dates. The important thing to note
+ * about dates is that they are really normal numeric cells that are
+ * formatted specially.
+ */
+public class CreateDateCells {
+ public static void main(String[] args) throws IOException {
+ try (HSSFWorkbook wb = new HSSFWorkbook()) {
+ HSSFSheet sheet = wb.createSheet("new sheet");
+
+ // Create a row and put some cells in it. Rows are 0 based.
+ HSSFRow row = sheet.createRow(0);
+
+ // Create a cell and put a date value in it. The first cell is not styled as a date.
+ HSSFCell cell = row.createCell(0);
+ cell.setCellValue(new Date());
+
+ // we style the second cell as a date (and time). It is important to create a new cell style from the workbook
+ // otherwise you can end up modifying the built in style and effecting not only this cell but other cells.
+ HSSFCellStyle cellStyle = wb.createCellStyle();
+ cellStyle.setDataFormat(HSSFDataFormat.getBuiltinFormat("m/d/yy h:mm"));
+ cell = row.createCell(1);
+ cell.setCellValue(new Date());
+ cell.setCellStyle(cellStyle);
+
+ // Write the output to a file
+ try (FileOutputStream fileOut = new FileOutputStream("workbook.xls")) {
+ wb.write(fileOut);
+ }
+ }
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/hssf/usermodel/EmbeddedObjects.java b/src/examples/src/org/apache/poi/examples/hssf/usermodel/EmbeddedObjects.java
new file mode 100644
index 0000000000..149c908fac
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/hssf/usermodel/EmbeddedObjects.java
@@ -0,0 +1,78 @@
+/* ====================================================================
+ 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.examples.hssf.usermodel;
+
+import java.io.Closeable;
+import java.io.FileInputStream;
+
+import org.apache.poi.hslf.usermodel.HSLFSlideShow;
+import org.apache.poi.hssf.usermodel.HSSFObjectData;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.hwpf.HWPFDocument;
+import org.apache.poi.poifs.filesystem.DirectoryNode;
+import org.apache.poi.poifs.filesystem.Entry;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+
+/**
+ * Demonstrates how you can extract embedded data from a .xls file
+ */
+@SuppressWarnings({"java:S106","java:S4823","java:S1192"})
+public final class EmbeddedObjects {
+ private EmbeddedObjects() {}
+
+ @SuppressWarnings("unused")
+ public static void main(String[] args) throws Exception {
+ try (
+ FileInputStream fis = new FileInputStream(args[0]);
+ POIFSFileSystem fs = new POIFSFileSystem(fis);
+ HSSFWorkbook workbook = new HSSFWorkbook(fs)
+ ) {
+ for (HSSFObjectData obj : workbook.getAllEmbeddedObjects()) {
+ //the OLE2 Class Name of the object
+ String oleName = obj.getOLE2ClassName();
+ DirectoryNode dn = (obj.hasDirectoryEntry()) ? (DirectoryNode) obj.getDirectory() : null;
+ Closeable document = null;
+ switch (oleName) {
+ case "Worksheet":
+ document = new HSSFWorkbook(dn, fs, false);
+ break;
+ case "Document":
+ document = new HWPFDocument(dn);
+ break;
+ case "Presentation":
+ document = new HSLFSlideShow(dn);
+ break;
+ default:
+ if (dn != null) {
+ // The DirectoryEntry is a DocumentNode. Examine its entries to find out what it is
+ for (Entry entry : dn) {
+ String name = entry.getName();
+ }
+ } else {
+ // There is no DirectoryEntry
+ // Recover the object's data from the HSSFObjectData instance.
+ byte[] objectData = obj.getObjectData();
+ }
+ break;
+ }
+ if (document != null) {
+ document.close();
+ }
+ }
+ }
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/hssf/usermodel/EventExample.java b/src/examples/src/org/apache/poi/examples/hssf/usermodel/EventExample.java
new file mode 100644
index 0000000000..0e71406b58
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/hssf/usermodel/EventExample.java
@@ -0,0 +1,122 @@
+
+/* ====================================================================
+ 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.examples.hssf.usermodel;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.poi.hssf.eventusermodel.HSSFEventFactory;
+import org.apache.poi.hssf.eventusermodel.HSSFListener;
+import org.apache.poi.hssf.eventusermodel.HSSFRequest;
+import org.apache.poi.hssf.record.BOFRecord;
+import org.apache.poi.hssf.record.BoundSheetRecord;
+import org.apache.poi.hssf.record.LabelSSTRecord;
+import org.apache.poi.hssf.record.NumberRecord;
+import org.apache.poi.hssf.record.RowRecord;
+import org.apache.poi.hssf.record.SSTRecord;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+
+/**
+ * This example shows how to use the event API for reading a file.
+ */
+@SuppressWarnings({"java:S106","java:S4823"})
+public class EventExample implements HSSFListener {
+ private SSTRecord sstrec;
+
+ /**
+ * This method listens for incoming records and handles them as required.
+ * @param record The record that was found while reading.
+ */
+ @Override
+ public void processRecord(org.apache.poi.hssf.record.Record record)
+ {
+ switch (record.getSid())
+ {
+ // the BOFRecord can represent either the beginning of a sheet or the workbook
+ case BOFRecord.sid:
+ BOFRecord bof = (BOFRecord) record;
+ if (bof.getType() == BOFRecord.TYPE_WORKBOOK)
+ {
+ System.out.println("Encountered workbook");
+ // assigned to the class level member
+ } else if (bof.getType() == BOFRecord.TYPE_WORKSHEET)
+ {
+ System.out.println("Encountered sheet reference");
+ }
+ break;
+ case BoundSheetRecord.sid:
+ BoundSheetRecord bsr = (BoundSheetRecord) record;
+ System.out.println("New sheet named: " + bsr.getSheetname());
+ break;
+ case RowRecord.sid:
+ RowRecord rowrec = (RowRecord) record;
+ System.out.println("Row found, first column at "
+ + rowrec.getFirstCol() + " last column at " + rowrec.getLastCol());
+ break;
+ case NumberRecord.sid:
+ NumberRecord numrec = (NumberRecord) record;
+ System.out.println("Cell found with value " + numrec.getValue()
+ + " at row " + numrec.getRow() + " and column " + numrec.getColumn());
+ break;
+ // SSTRecords store a array of unique strings used in Excel.
+ case SSTRecord.sid:
+ sstrec = (SSTRecord) record;
+ for (int k = 0; k < sstrec.getNumUniqueStrings(); k++)
+ {
+ System.out.println("String table value " + k + " = " + sstrec.getString(k));
+ }
+ break;
+ case LabelSSTRecord.sid:
+ LabelSSTRecord lrec = (LabelSSTRecord) record;
+ System.out.println("String cell found with value "
+ + sstrec.getString(lrec.getSSTIndex()));
+ break;
+ }
+ }
+
+ /**
+ * Read an excel file and spit out what we find.
+ *
+ * @param args Expect one argument that is the file to read.
+ * @throws IOException When there is an error processing the file.
+ */
+ public static void main(String[] args) throws IOException
+ {
+ // create a new file input stream with the input file specified
+ // at the command line
+ try (FileInputStream fin = new FileInputStream(args[0])) {
+ // create a new org.apache.poi.poifs.filesystem.Filesystem
+ try (POIFSFileSystem poifs = new POIFSFileSystem(fin)) {
+ // get the Workbook (excel part) stream in a InputStream
+ try (InputStream din = poifs.createDocumentInputStream("Workbook")) {
+ // construct out HSSFRequest object
+ HSSFRequest req = new HSSFRequest();
+ // lazy listen for ALL records with the listener shown above
+ req.addListenerForAllRecords(new EventExample());
+ // create our event factory
+ HSSFEventFactory factory = new HSSFEventFactory();
+ // process our events based on the document input stream
+ factory.processEvents(req, din);
+ }
+ }
+ }
+ System.out.println("done.");
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/hssf/usermodel/FrillsAndFills.java b/src/examples/src/org/apache/poi/examples/hssf/usermodel/FrillsAndFills.java
new file mode 100644
index 0000000000..ca2a61e41e
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/hssf/usermodel/FrillsAndFills.java
@@ -0,0 +1,64 @@
+/* ====================================================================
+ 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.examples.hssf.usermodel;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.poi.hssf.usermodel.HSSFCell;
+import org.apache.poi.hssf.usermodel.HSSFCellStyle;
+import org.apache.poi.hssf.usermodel.HSSFRow;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.hssf.util.HSSFColor.HSSFColorPredefined;
+import org.apache.poi.ss.usermodel.FillPatternType;
+
+/**
+ * Shows how to use various fills.
+ */
+public class FrillsAndFills {
+ public static void main(String[] args) throws IOException {
+ try (HSSFWorkbook wb = new HSSFWorkbook()) {
+ HSSFSheet sheet = wb.createSheet("new sheet");
+
+ // Create a row and put some cells in it. Rows are 0 based.
+ HSSFRow row = sheet.createRow(1);
+
+ // Aqua background
+ HSSFCellStyle style = wb.createCellStyle();
+ style.setFillBackgroundColor(HSSFColorPredefined.AQUA.getIndex());
+ style.setFillPattern(FillPatternType.BIG_SPOTS);
+ HSSFCell cell = row.createCell(1);
+ cell.setCellValue("X");
+ cell.setCellStyle(style);
+
+ // Orange "foreground", foreground being the fill foreground not the font color.
+ style = wb.createCellStyle();
+ style.setFillForegroundColor(HSSFColorPredefined.ORANGE.getIndex());
+ style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+ cell = row.createCell(2);
+ cell.setCellValue("X");
+ cell.setCellStyle(style);
+
+ // Write the output to a file
+ try (FileOutputStream fileOut = new FileOutputStream("workbook.xls")) {
+ wb.write(fileOut);
+ }
+ }
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/hssf/usermodel/HSSFReadWrite.java b/src/examples/src/org/apache/poi/examples/hssf/usermodel/HSSFReadWrite.java
new file mode 100644
index 0000000000..f53065de94
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/hssf/usermodel/HSSFReadWrite.java
@@ -0,0 +1,251 @@
+/* ====================================================================
+ 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.examples.hssf.usermodel;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.poi.hssf.usermodel.HSSFCell;
+import org.apache.poi.hssf.usermodel.HSSFCellStyle;
+import org.apache.poi.hssf.usermodel.HSSFDataFormat;
+import org.apache.poi.hssf.usermodel.HSSFFont;
+import org.apache.poi.hssf.usermodel.HSSFRichTextString;
+import org.apache.poi.hssf.usermodel.HSSFRow;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.BorderStyle;
+import org.apache.poi.ss.usermodel.FillPatternType;
+import org.apache.poi.ss.util.CellRangeAddress;
+
+/**
+ * File for HSSF testing/examples
+ *
+ * THIS IS NOT THE MAIN HSSF FILE!! This is a utility for testing functionality.
+ * It does contain sample API usage that may be educational to regular API
+ * users.
+ */
+@SuppressWarnings({"java:S106","java:S4823"})
+public final class HSSFReadWrite {
+
+ private HSSFReadWrite() {}
+
+ /**
+ * creates an {@link HSSFWorkbook} with the specified OS filename.
+ */
+ private static HSSFWorkbook readFile(String filename) throws IOException {
+ try (FileInputStream fis = new FileInputStream(filename)) {
+ return new HSSFWorkbook(fis); // NOSONAR - should not be closed here
+ }
+ }
+
+ /**
+ * given a filename this outputs a sample sheet with just a set of
+ * rows/cells.
+ */
+ private static void testCreateSampleSheet(String outputFilename) throws IOException {
+ try (HSSFWorkbook wb = new HSSFWorkbook();
+ FileOutputStream out = new FileOutputStream(outputFilename)) {
+ HSSFSheet s = wb.createSheet();
+ HSSFCellStyle cs = wb.createCellStyle();
+ HSSFCellStyle cs2 = wb.createCellStyle();
+ HSSFCellStyle cs3 = wb.createCellStyle();
+ HSSFFont f = wb.createFont();
+ HSSFFont f2 = wb.createFont();
+
+ f.setFontHeightInPoints((short) 12);
+ f.setColor((short) 0xA);
+ f.setBold(true);
+ f2.setFontHeightInPoints((short) 10);
+ f2.setColor((short) 0xf);
+ f2.setBold(true);
+ cs.setFont(f);
+ cs.setDataFormat(HSSFDataFormat.getBuiltinFormat("($#,##0_);[Red]($#,##0)"));
+ cs2.setBorderBottom(BorderStyle.THIN);
+ cs2.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+ cs2.setFillForegroundColor((short) 0xA);
+ cs2.setFont(f2);
+ wb.setSheetName(0, "HSSF Test");
+ int rownum;
+ for (rownum = 0; rownum < 300; rownum++) {
+ HSSFRow r = s.createRow(rownum);
+ if ((rownum % 2) == 0) {
+ r.setHeight((short) 0x249);
+ }
+
+ for (int cellnum = 0; cellnum < 50; cellnum += 2) {
+ HSSFCell c = r.createCell(cellnum);
+ c.setCellValue((rownum * 10000.0) + cellnum
+ + (rownum / 1000.0) + (cellnum / 10000.0));
+ if ((rownum % 2) == 0) {
+ c.setCellStyle(cs);
+ }
+ c = r.createCell(cellnum + 1);
+ c.setCellValue(new HSSFRichTextString("TEST"));
+ // 50 characters divided by 1/20th of a point
+ s.setColumnWidth(cellnum + 1, (int) (50 * 8 / 0.05));
+ if ((rownum % 2) == 0) {
+ c.setCellStyle(cs2);
+ }
+ }
+ }
+
+ // draw a thick black border on the row at the bottom using BLANKS
+ rownum++;
+ rownum++;
+ HSSFRow r = s.createRow(rownum);
+ cs3.setBorderBottom(BorderStyle.THICK);
+ for (int cellnum = 0; cellnum < 50; cellnum++) {
+ HSSFCell c = r.createCell(cellnum);
+ c.setCellStyle(cs3);
+ }
+ s.addMergedRegion(new CellRangeAddress(0, 3, 0, 3));
+ s.addMergedRegion(new CellRangeAddress(100, 110, 100, 110));
+
+ // end draw thick black border
+ // create a sheet, set its title then delete it
+ wb.createSheet();
+ wb.setSheetName(1, "DeletedSheet");
+ wb.removeSheetAt(1);
+
+ // end deleted sheet
+ wb.write(out);
+ }
+ }
+
+ /**
+ * Method main
+ *
+ * Given 1 argument takes that as the filename, inputs it and dumps the
+ * cell values/types out to sys.out.
+ *
+ * given 2 arguments where the second argument is the word "write" and the
+ * first is the filename - writes out a sample (test) spreadsheet
+ * see {@link HSSFReadWrite#testCreateSampleSheet(String)}.
+ *
+ * given 2 arguments where the first is an input filename and the second
+ * an output filename (not write), attempts to fully read in the
+ * spreadsheet and fully write it out.
+ *
+ * given 3 arguments where the first is an input filename and the second an
+ * output filename (not write) and the third is "modify1", attempts to read in the
+ * spreadsheet, deletes rows 0-24, 74-99. Changes cell at row 39, col 3 to
+ * "MODIFIED CELL" then writes it out. Hence this is "modify test 1". If you
+ * take the output from the write test, you'll have a valid scenario.
+ */
+ public static void main(String[] args) throws Exception {
+ if (args.length < 1) {
+ System.err.println("At least one argument expected");
+ return;
+ }
+
+ String fileName = args[0];
+ if (args.length < 2) {
+
+ try (HSSFWorkbook wb = HSSFReadWrite.readFile(fileName)) {
+ System.out.println("Data dump:\n");
+
+ for (int k = 0; k < wb.getNumberOfSheets(); k++) {
+ HSSFSheet sheet = wb.getSheetAt(k);
+ int rows = sheet.getPhysicalNumberOfRows();
+ System.out.println("Sheet " + k + " \"" + wb.getSheetName(k) + "\" has " + rows + " row(s).");
+ for (int r = 0; r < rows; r++) {
+ HSSFRow row = sheet.getRow(r);
+ if (row == null) {
+ continue;
+ }
+
+ System.out.println("\nROW " + row.getRowNum() + " has " + row.getPhysicalNumberOfCells() + " cell(s).");
+ for (int c = 0; c < row.getLastCellNum(); c++) {
+ HSSFCell cell = row.getCell(c);
+ String value;
+
+ if (cell != null) {
+ switch (cell.getCellType()) {
+
+ case FORMULA:
+ value = "FORMULA value=" + cell.getCellFormula();
+ break;
+
+ case NUMERIC:
+ value = "NUMERIC value=" + cell.getNumericCellValue();
+ break;
+
+ case STRING:
+ value = "STRING value=" + cell.getStringCellValue();
+ break;
+
+ case BLANK:
+ value = "";
+ break;
+
+ case BOOLEAN:
+ value = "BOOLEAN value-" + cell.getBooleanCellValue();
+ break;
+
+ case ERROR:
+ value = "ERROR value=" + cell.getErrorCellValue();
+ break;
+
+ default:
+ value = "UNKNOWN value of type " + cell.getCellType();
+ }
+ System.out.println("CELL col=" + cell.getColumnIndex() + " VALUE=" + value);
+ }
+ }
+ }
+ }
+ }
+ } else if (args.length == 2) {
+ if ("write".equalsIgnoreCase(args[1])) {
+ System.out.println("Write mode");
+ long time = System.currentTimeMillis();
+ HSSFReadWrite.testCreateSampleSheet(fileName);
+
+ System.out.println("" + (System.currentTimeMillis() - time) + " ms generation time");
+ } else {
+ System.out.println("readwrite test");
+ try (HSSFWorkbook wb = HSSFReadWrite.readFile(fileName);
+ FileOutputStream stream = new FileOutputStream(args[1])) {
+ wb.write(stream);
+ }
+ }
+ } else if (args.length == 3 && "modify1".equalsIgnoreCase(args[2])) {
+ // delete row 0-24, row 74 - 99 && change cell 3 on row 39 to string "MODIFIED CELL!!"
+
+ try (HSSFWorkbook wb = HSSFReadWrite.readFile(fileName);
+ FileOutputStream stream = new FileOutputStream(args[1])) {
+ HSSFSheet sheet = wb.getSheetAt(0);
+
+ for (int k = 0; k < 25; k++) {
+ HSSFRow row = sheet.getRow(k);
+ sheet.removeRow(row);
+ }
+ for (int k = 74; k < 100; k++) {
+ HSSFRow row = sheet.getRow(k);
+ sheet.removeRow(row);
+ }
+ HSSFRow row = sheet.getRow(39);
+ HSSFCell cell = row.getCell(3);
+ cell.setCellValue("MODIFIED CELL!!!!!");
+
+ wb.write(stream);
+ }
+ }
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/hssf/usermodel/HyperlinkFormula.java b/src/examples/src/org/apache/poi/examples/hssf/usermodel/HyperlinkFormula.java
new file mode 100644
index 0000000000..96fa30a90f
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/hssf/usermodel/HyperlinkFormula.java
@@ -0,0 +1,45 @@
+/* ====================================================================
+ 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.examples.hssf.usermodel;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.poi.hssf.usermodel.HSSFCell;
+import org.apache.poi.hssf.usermodel.HSSFRow;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+
+/**
+ * Test if hyperlink formula, with url that got more than 127 characters, works
+ */
+public class HyperlinkFormula {
+ public static void main(String[] args) throws IOException {
+ try (HSSFWorkbook wb = new HSSFWorkbook()) {
+ HSSFSheet sheet = wb.createSheet("new sheet");
+ HSSFRow row = sheet.createRow(0);
+
+ HSSFCell cell = row.createCell(0);
+ cell.setCellFormula("HYPERLINK(\"http://127.0.0.1:8080/toto/truc/index.html?test=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\", \"test\")");
+
+ try (FileOutputStream fileOut = new FileOutputStream("workbook.xls")) {
+ wb.write(fileOut);
+ }
+ }
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/hssf/usermodel/Hyperlinks.java b/src/examples/src/org/apache/poi/examples/hssf/usermodel/Hyperlinks.java
new file mode 100644
index 0000000000..ae4a37ea13
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/hssf/usermodel/Hyperlinks.java
@@ -0,0 +1,97 @@
+/* ====================================================================
+ 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.examples.hssf.usermodel;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.poi.common.usermodel.HyperlinkType;
+import org.apache.poi.hssf.usermodel.HSSFCell;
+import org.apache.poi.hssf.usermodel.HSSFCellStyle;
+import org.apache.poi.hssf.usermodel.HSSFCreationHelper;
+import org.apache.poi.hssf.usermodel.HSSFFont;
+import org.apache.poi.hssf.usermodel.HSSFHyperlink;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.hssf.util.HSSFColor.HSSFColorPredefined;
+import org.apache.poi.ss.usermodel.Font;
+
+/**
+ * Demonstrates how to create hyperlinks.
+ */
+public class Hyperlinks {
+
+ public static void main(String[] args) throws IOException {
+ try (HSSFWorkbook wb = new HSSFWorkbook()) {
+ HSSFCreationHelper helper = wb.getCreationHelper();
+
+ //cell style for hyperlinks
+ //by default hyperlinks are blue and underlined
+ HSSFCellStyle hlinkStyle = wb.createCellStyle();
+ HSSFFont hlinkFont = wb.createFont();
+ hlinkFont.setUnderline(Font.U_SINGLE);
+ hlinkFont.setColor(HSSFColorPredefined.BLUE.getIndex());
+ hlinkStyle.setFont(hlinkFont);
+
+ HSSFCell cell;
+ HSSFSheet sheet = wb.createSheet("Hyperlinks");
+
+ //URL
+ cell = sheet.createRow(0).createCell(0);
+ cell.setCellValue("URL Link");
+ HSSFHyperlink link = helper.createHyperlink(HyperlinkType.URL);
+ link.setAddress("https://poi.apache.org/");
+ cell.setHyperlink(link);
+ cell.setCellStyle(hlinkStyle);
+
+ //link to a file in the current directory
+ cell = sheet.createRow(1).createCell(0);
+ cell.setCellValue("File Link");
+ link = helper.createHyperlink(HyperlinkType.FILE);
+ link.setAddress("link1.xls");
+ cell.setHyperlink(link);
+ cell.setCellStyle(hlinkStyle);
+
+ //e-mail link
+ cell = sheet.createRow(2).createCell(0);
+ cell.setCellValue("Email Link");
+ link = helper.createHyperlink(HyperlinkType.EMAIL);
+ //note, if subject contains white spaces, make sure they are url-encoded
+ link.setAddress("mailto:poi@apache.org?subject=Hyperlinks");
+ cell.setHyperlink(link);
+ cell.setCellStyle(hlinkStyle);
+
+ //link to a place in this workbook
+
+ //create a target sheet and cell
+ HSSFSheet sheet2 = wb.createSheet("Target Sheet");
+ sheet2.createRow(0).createCell(0).setCellValue("Target Cell");
+
+ cell = sheet.createRow(3).createCell(0);
+ cell.setCellValue("Worksheet Link");
+ link = helper.createHyperlink(HyperlinkType.DOCUMENT);
+ link.setAddress("'Target Sheet'!A1");
+ cell.setHyperlink(link);
+ cell.setCellStyle(hlinkStyle);
+
+ try (FileOutputStream out = new FileOutputStream("hssf-links.xls")) {
+ wb.write(out);
+ }
+ }
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/hssf/usermodel/InCellLists.java b/src/examples/src/org/apache/poi/examples/hssf/usermodel/InCellLists.java
new file mode 100644
index 0000000000..76d0172f82
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/hssf/usermodel/InCellLists.java
@@ -0,0 +1,541 @@
+/* ====================================================================
+ 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.examples.hssf.usermodel;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.poi.hssf.usermodel.HSSFCell;
+import org.apache.poi.hssf.usermodel.HSSFCellStyle;
+import org.apache.poi.hssf.usermodel.HSSFDataFormat;
+import org.apache.poi.hssf.usermodel.HSSFRichTextString;
+import org.apache.poi.hssf.usermodel.HSSFRow;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+
+/**
+ * This class contains code that demonstrates how to insert plain, numbered
+ * and bulleted lists into an Excel spreadsheet cell.
+ *
+ * Look at the code contained in the demonstrateMethodCalls() method. It calls
+ * other methods that create plain, numbered and bulleted single and
+ * multi-level lists. The demonstrateMethodCalls() method appears at the top
+ * of the class definition.
+ *
+ * Though different methods are provided to construct single and multi-level
+ * plain, numbered and bulleted lists, close examination will reveal that they
+ * are not strictly necessary. If the inputs to the listInCell() and
+ * multilLevelListInCell() methods are constructed to include the bullet
+ * character or the item numbers then these methods alone may be sufficient.
+ *
+ * @author Mark Beardsley [msb at apache.org]
+ */
+@SuppressWarnings({"java:S106","java:S4823"})
+public class InCellLists {
+
+ // This character looks like a solid, black, loser case letter 'o'
+ // positioned up from the base line of the text.
+ private static final char BULLET_CHARACTER = '\u2022';
+
+ // The tab character - \t - cannot be used to create a tab space
+ // within a cell as it is rendered as a square. Therefore, four
+ // spaces are used to simulate that character.
+ private static final String TAB = " ";
+
+ /**
+ * Call each of the list creation methods.
+ *
+ * @param outputFilename A String that encapsulates the name of and path to
+ * the Excel spreadsheet file this code will create.
+ */
+ public void demonstrateMethodCalls(String outputFilename) throws IOException {
+ try (HSSFWorkbook workbook = new HSSFWorkbook()) {
+ HSSFSheet sheet = workbook.createSheet("In Cell Lists");
+ HSSFRow row = sheet.createRow(0);
+
+ // Create a cell at A1 and insert a single, bulleted, item into
+ // that cell.
+ HSSFCell cell = row.createCell(0);
+ this.bulletedItemInCell(workbook, "List Item", cell);
+
+ // Create a cell at A2 and insert a plain list - that is one
+ // whose items are neither bulleted or numbered - into that cell.
+ row = sheet.createRow(1);
+ cell = row.createCell(0);
+ List listItems = new ArrayList<>();
+ listItems.add("List Item One.");
+ listItems.add("List Item Two.");
+ listItems.add("List Item Three.");
+ listItems.add("List Item Four.");
+ this.listInCell(workbook, listItems, cell);
+ // The row height and cell width are set here to ensure that the
+ // list may be seen.
+ row.setHeight((short) 1100);
+ sheet.setColumnWidth(0, 9500);
+
+ // Create a cell at A3 and insert a numbered list into that cell.
+ // Note that a couple of items have been added to the listItems
+ // ArrayList
+ row = sheet.createRow(2);
+ cell = row.createCell(0);
+ listItems.add("List Item Five.");
+ listItems.add("List Item Six.");
+ this.numberedListInCell(workbook, listItems, cell, 1, 2);
+ row.setHeight((short) 1550);
+
+ // Create a cell at A4 and insert a numbered list into that cell.
+ // Note that a couple of items have been added to the listItems
+ // ArrayList
+ row = sheet.createRow(3);
+ cell = row.createCell(0);
+ listItems.add("List Item Seven.");
+ listItems.add("List Item Eight.");
+ listItems.add("List Item Nine.");
+ listItems.add("List Item Ten.");
+ this.bulletedListInCell(workbook, listItems, cell);
+ row.setHeight((short) 2550);
+
+ // Insert a plain, multi-level list into cell A5. Note that
+ // the major difference here is that the list items are passed as
+ // an ArrayList of MultiLevelListItems. Note that an ArrayList
+ // of instances of an inner class was used here in preference to
+ // a Hashtable or HashMap as the ArrayList will preserve the
+ // ordering of the items added to it; the first item added will
+ // be the first item recovered and the last item added, the last
+ // item recovered. Alternatively, a LinkedHashMap could be used
+ // to preserve order.
+ row = sheet.createRow(4);
+ cell = row.createCell(0);
+ List multiLevelListItems = new ArrayList<>();
+ listItems = new ArrayList<>();
+ listItems.add("ML List Item One - Sub Item One.");
+ listItems.add("ML List Item One - Sub Item Two.");
+ listItems.add("ML List Item One - Sub Item Three.");
+ listItems.add("ML List Item One - Sub Item Four.");
+ multiLevelListItems.add(new MultiLevelListItem("List Item One.", listItems));
+ // Passing either null or an empty ArrayList will signal that
+ // there are no lower level items associated with the top level
+ // item
+ multiLevelListItems.add(new MultiLevelListItem("List Item Two.", null));
+ multiLevelListItems.add(new MultiLevelListItem("List Item Three.", null));
+ listItems = new ArrayList<>();
+ listItems.add("ML List Item Four - Sub Item One.");
+ listItems.add("ML List Item Four - Sub Item Two.");
+ listItems.add("ML List Item Four - Sub Item Three.");
+ multiLevelListItems.add(new MultiLevelListItem("List Item Four.", listItems));
+ this.multiLevelListInCell(workbook, multiLevelListItems, cell);
+ row.setHeight((short) 2800);
+
+ // Insert a numbered multi-level list into cell A6. Note that the
+ // same ArrayList as constructed for the above plain multi-level
+ // list example will be re-used
+ row = sheet.createRow(5);
+ cell = row.createCell(0);
+ this.multiLevelNumberedListInCell(workbook, multiLevelListItems,
+ cell, 1, 1, 1, 2);
+ row.setHeight((short) 2800);
+
+ // Insert a numbered multi-level list into cell A7. Note that the
+ // same ArrayList as constructed for the plain multi-level list
+ // example will be re-used
+ row = sheet.createRow(6);
+ cell = row.createCell(0);
+ this.multiLevelBulletedListInCell(workbook, multiLevelListItems, cell);
+ row.setHeight((short) 2800);
+
+ // Save the completed workbook
+ try (FileOutputStream fos = new FileOutputStream(new File(outputFilename))) {
+ workbook.write(fos);
+ }
+ } catch (IOException ioEx) {
+ System.out.println("Caught a: " + ioEx.getClass().getName());
+ System.out.println("Message: " + ioEx.getMessage());
+ System.out.println("Stacktrace follows...........");
+ ioEx.printStackTrace(System.out);
+ }
+ }
+
+ /**
+ * Inserts a single bulleted item into a cell.
+ *
+ * @param workbook A reference to the HSSFWorkbook that 'contains' the
+ * cell.
+ * @param listItem An instance of the String class encapsulating the
+ * items text.
+ * @param cell An instance of the HSSFCell class that encapsulates a
+ * reference to the spreadsheet cell into which the list item
+ * will be written.
+ */
+ public void bulletedItemInCell(HSSFWorkbook workbook, String listItem, HSSFCell cell) {
+ // A format String must be built to ensure that the contents of the
+ // cell appear as a bulleted item.
+ HSSFDataFormat format = workbook.createDataFormat();
+ String formatString = InCellLists.BULLET_CHARACTER + " @";
+ int formatIndex = format.getFormat(formatString);
+
+ // Construct an HSSFCellStyle and set it's data formt to use the
+ // object created above.
+ HSSFCellStyle bulletStyle = workbook.createCellStyle();
+ bulletStyle.setDataFormat((short)formatIndex);
+
+ // Set the cells contents and style.
+ cell.setCellValue(new HSSFRichTextString(listItem));
+ cell.setCellStyle(bulletStyle);
+ }
+
+ /**
+ * Inserts a list of plain items - that is items that are neither
+ * numbered or bulleted - into a single cell.
+ *
+ * @param workbook A reference to the HSSFWorkbook that 'contains' the
+ * cell.
+ * @param listItems An ArrayList whose elements encapsulate the text for
+ * the list's items.
+ * @param cell An instance of the HSSFCell class that encapsulates a
+ * reference to the spreadsheet cell into which the list
+ * will be written.
+ */
+ public void listInCell(HSSFWorkbook workbook, List listItems, HSSFCell cell) {
+ StringBuilder buffer = new StringBuilder();
+ HSSFCellStyle wrapStyle = workbook.createCellStyle();
+ wrapStyle.setWrapText(true);
+ for(String listItem : listItems) {
+ buffer.append(listItem);
+ buffer.append("\n");
+ }
+ // The StringBuilder's contents are the source for the contents
+ // of the cell.
+ cell.setCellValue(new HSSFRichTextString(buffer.toString().trim()));
+ cell.setCellStyle(wrapStyle);
+ }
+
+ /**
+ * Inserts a numbered list into a single cell.
+ *
+ * @param workbook A reference to the HSSFWorkbook that 'contains' the
+ * cell.
+ * @param listItems An ArrayList whose elements encapsulate the text for
+ * the lists items.
+ * @param cell An instance of the HSSFCell class that encapsulates a
+ * reference to the spreadsheet cell into which the list
+ * will be written.
+ * @param startingValue A primitive int containing the number for the first
+ * item in the list.
+ * @param increment A primitive int containing the value that should be used
+ * to calculate subsequent item numbers.
+ */
+ public void numberedListInCell(HSSFWorkbook workbook,
+ List listItems,
+ HSSFCell cell,
+ int startingValue,
+ int increment) {
+ StringBuilder buffer = new StringBuilder();
+ int itemNumber = startingValue;
+ // Note that again, an HSSFCellStye object is required and that
+ // it's wrap text property should be set to 'true'
+ HSSFCellStyle wrapStyle = workbook.createCellStyle();
+ wrapStyle.setWrapText(true);
+ // Note that the basic method is identical to the listInCell() method
+ // with one difference; a number prefixed to the items text.
+ for(String listItem : listItems) {
+ buffer.append(itemNumber).append(". ");
+ buffer.append(listItem);
+ buffer.append("\n");
+ itemNumber += increment;
+ }
+ // The StringBuilder's contents are the source for the contents
+ // of the cell.
+ cell.setCellValue(new HSSFRichTextString(buffer.toString().trim()));
+ cell.setCellStyle(wrapStyle);
+ }
+
+ /**
+ * Insert a bulleted list into a cell.
+ *
+ * @param workbook A reference to the HSSFWorkbook that 'contains' the
+ * cell.
+ * @param listItems An ArrayList whose elements encapsulate the text for
+ * the lists items.
+ * @param cell An instance of the HSSFCell class that encapsulates a
+ * reference to the spreadsheet cell into which the list
+ * will be written.
+ */
+ public void bulletedListInCell(HSSFWorkbook workbook,
+ List listItems,
+ HSSFCell cell) {
+ StringBuilder buffer = new StringBuilder();
+ // Note that again, an HSSFCellStye object is required and that
+ // it's wrap text property should be set to 'true'
+ HSSFCellStyle wrapStyle = workbook.createCellStyle();
+ wrapStyle.setWrapText(true);
+ // Note that the basic method is identical to the listInCell() method
+ // with one difference; the bullet character prefixed to the items text.
+ for(String listItem : listItems) {
+ buffer.append(InCellLists.BULLET_CHARACTER + " ");
+ buffer.append(listItem);
+ buffer.append("\n");
+ }
+ // The StringBuilder's contents are the source for the contents
+ // of the cell.
+ cell.setCellValue(new HSSFRichTextString(buffer.toString().trim()));
+ cell.setCellStyle(wrapStyle);
+ }
+
+ /**
+ * Insert a multi-level list into a cell.
+ *
+ * @param workbook A reference to the HSSFWorkbook that 'contains' the
+ * cell.
+ * @param multiLevelListItems An ArrayList whose elements contain instances
+ * of the MultiLevelListItem class. Each element
+ * encapsulates the text for the high level item
+ * along with an ArrayList. Each element of this
+ * ArrayList encapsulates the text for a lower
+ * level item.
+ * @param cell An instance of the HSSFCell class that encapsulates a
+ * reference to the spreadsheet cell into which the list
+ * will be written.
+ */
+ public void multiLevelListInCell(HSSFWorkbook workbook,
+ List multiLevelListItems,
+ HSSFCell cell) {
+ StringBuilder buffer = new StringBuilder();
+ // Note that again, an HSSFCellStye object is required and that
+ // it's wrap text property should be set to 'true'
+ HSSFCellStyle wrapStyle = workbook.createCellStyle();
+ wrapStyle.setWrapText(true);
+ // Step through the ArrayList of MultilLevelListItem instances.
+ for(MultiLevelListItem multiLevelListItem : multiLevelListItems) {
+ // For each element in the ArrayList, get the text for the high
+ // level list item......
+ buffer.append(multiLevelListItem.getItemText());
+ buffer.append("\n");
+ // and then an ArrayList whose elements encapsulate the text
+ // for the lower level list items.
+ List lowerLevelItems = multiLevelListItem.getLowerLevelItems();
+ if (lowerLevelItems == null || lowerLevelItems.isEmpty()) {
+ continue;
+ }
+ for(String item : lowerLevelItems) {
+ buffer.append(InCellLists.TAB);
+ buffer.append(item);
+ buffer.append("\n");
+ }
+ }
+ // The StringBuilder's contents are the source for the contents
+ // of the cell.
+ cell.setCellValue(new HSSFRichTextString(buffer.toString().trim()));
+ cell.setCellStyle(wrapStyle);
+ }
+
+ /**
+ * Insert a multi-level list into a cell.
+ *
+ * @param workbook A reference to the HSSFWorkbook that 'contains' the
+ * cell.
+ * @param multiLevelListItems An ArrayList whose elements contain instances
+ * of the MultiLevelListItem class. Each element
+ * encapsulates the text for the high level item
+ * along with an ArrayList. Each element of this
+ * ArrayList encapsulates the text for a lower
+ * level item.
+ * @param cell An instance of the HSSFCell class that encapsulates a
+ * reference to the spreadsheet cell into which the list
+ * will be written.
+ * @param highLevelStartingValue A primitive int containing the number
+ * for the first high level item in the list.
+ * @param highLevelIncrement A primitive int containing the value that
+ * should be used to calculate the number of
+ * subsequent high level item.
+ * @param lowLevelStartingValue A primitive int will containing the number
+ * for the first low level item associated
+ * with a high level item.
+ * @param lowLevelIncrement A primitive int containing the value that
+ * should be used to calculate the number of
+ * subsequent low level item.
+ */
+ public void multiLevelNumberedListInCell(HSSFWorkbook workbook,
+ List multiLevelListItems,
+ HSSFCell cell,
+ int highLevelStartingValue,
+ int highLevelIncrement,
+ int lowLevelStartingValue,
+ int lowLevelIncrement) {
+ StringBuilder buffer = new StringBuilder();
+ int highLevelItemNumber = highLevelStartingValue;
+ // Note that again, an HSSFCellStye object is required and that
+ // it's wrap text property should be set to 'true'
+ HSSFCellStyle wrapStyle = workbook.createCellStyle();
+ wrapStyle.setWrapText(true);
+ // Step through the ArrayList of MultilLevelListItem instances.
+ for(MultiLevelListItem multiLevelListItem : multiLevelListItems) {
+ // For each element in the ArrayList, get the text for the high
+ // level list item......
+ buffer.append(highLevelItemNumber);
+ buffer.append(". ");
+ buffer.append(multiLevelListItem.getItemText());
+ buffer.append("\n");
+ // and then an ArrayList whose elements encapsulate the text
+ // for the lower level list items.
+ List lowerLevelItems = multiLevelListItem.getLowerLevelItems();
+ if(lowerLevelItems != null && !lowerLevelItems.isEmpty()) {
+ int lowLevelItemNumber = lowLevelStartingValue;
+ for(String item : lowerLevelItems) {
+ buffer.append(InCellLists.TAB);
+ buffer.append(highLevelItemNumber);
+ buffer.append(".");
+ buffer.append(lowLevelItemNumber);
+ buffer.append(" ");
+ buffer.append(item);
+ buffer.append("\n");
+ lowLevelItemNumber += lowLevelIncrement;
+ }
+ }
+ highLevelItemNumber += highLevelIncrement;
+ }
+ // The StringBuilder's contents are the source for the contents
+ // of the cell.
+ cell.setCellValue(new HSSFRichTextString(buffer.toString().trim()));
+ cell.setCellStyle(wrapStyle);
+ }
+
+ /**
+ * Insert a bulleted multi-level list into a cell.
+ *
+ * @param workbook A reference to the HSSFWorkbook that 'contains' the
+ * cell.
+ * @param multiLevelListItems An ArrayList whose elements contain instances
+ * of the MultiLevelListItem class. Each element
+ * encapsulates the text for the high level item
+ * along with an ArrayList. Each element of this
+ * ArrayList encapsulates the text for a lower
+ * level item.
+ * @param cell An instance of the HSSFCell class that encapsulates a
+ * reference to the spreadsheet cell into which the list
+ * will be written.
+ */
+ public void multiLevelBulletedListInCell(HSSFWorkbook workbook,
+ List multiLevelListItems,
+ HSSFCell cell) {
+ StringBuilder buffer = new StringBuilder();
+ // Note that again, an HSSFCellStye object is required and that
+ // it's wrap text property should be set to 'true'
+ HSSFCellStyle wrapStyle = workbook.createCellStyle();
+ wrapStyle.setWrapText(true);
+ // Step through the ArrayList of MultilLevelListItem instances.
+ for(MultiLevelListItem multiLevelListItem : multiLevelListItems) {
+ // For each element in the ArrayList, get the text for the high
+ // level list item......
+ buffer.append(InCellLists.BULLET_CHARACTER);
+ buffer.append(" ");
+ buffer.append(multiLevelListItem.getItemText());
+ buffer.append("\n");
+ // and then an ArrayList whose elements encapsulate the text
+ // for the lower level list items.
+ List lowerLevelItems = multiLevelListItem.getLowerLevelItems();
+ if(lowerLevelItems != null && !lowerLevelItems.isEmpty()) {
+ for(String item : lowerLevelItems) {
+ buffer.append(InCellLists.TAB);
+ buffer.append(InCellLists.BULLET_CHARACTER);
+ buffer.append(" ");
+ buffer.append(item);
+ buffer.append("\n");
+ }
+ }
+ }
+ // The StringBuilder's contents are the source for the contents
+ // of the cell.
+ cell.setCellValue(new HSSFRichTextString(buffer.toString().trim()));
+ cell.setCellStyle(wrapStyle);
+ }
+
+ /**
+ * The main entry point to the program. Demonstrates how to call the method
+ * that will create an Excel workbook containing many different sorts of
+ * lists.
+ *
+ * @param args the command line arguments.
+ */
+ public static void main(String[] args) throws IOException {
+ new InCellLists().demonstrateMethodCalls("Latest In Cell List.xls");
+ }
+
+ /**
+ * An instance of this inner class models an item or element in a
+ * multi-level list. Each multi-level list item consists of the text for the
+ * high level items and an ArrayList containing the text for each of the
+ * associated lower level items. When written into a cell, each multi-level
+ * list item will have this general appearance.
+ *
+ * Item One
+ * Sub Item One.
+ * Sub Item Two.
+ * Item Two
+ * Sub Item One.
+ * Sub Item Two.
+ * etc.
+ *
+ * It would be quite possible to modify this class to model much more
+ * complex list structures descending through two, three or even more
+ * levels.
+ */
+ public final class MultiLevelListItem {
+
+ private String itemText;
+ private List lowerLevelItems;
+
+ /**
+ * Create a new instance of the MultiLevelListItem class using the
+ * following parameters.
+ *
+ * @param itemText A String that encapsulates the text for the high
+ * level list item.
+ * @param lowerLevelItems An ArrayList whose elements encapsulate the
+ * text for the associated lower level list
+ * items.
+ */
+ public MultiLevelListItem(String itemText, List lowerLevelItems) {
+ this.itemText = itemText;
+ this.lowerLevelItems = lowerLevelItems;
+ }
+
+ /**
+ * Get the text for the high level list item.
+ *
+ * @return A String that encapsulates the text for the high level list
+ * item.
+ */
+ public String getItemText() {
+ return(this.itemText);
+ }
+
+ /**
+ * Get the text for the associated lower level list items.
+ *
+ * @return An ArrayList whose elements each encapsulate the text for a
+ * single associated lower level list item.
+ */
+ public List getLowerLevelItems() {
+ return lowerLevelItems;
+ }
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/hssf/usermodel/MergedCells.java b/src/examples/src/org/apache/poi/examples/hssf/usermodel/MergedCells.java
new file mode 100644
index 0000000000..16114b2d63
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/hssf/usermodel/MergedCells.java
@@ -0,0 +1,49 @@
+/* ====================================================================
+ 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.examples.hssf.usermodel;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.poi.hssf.usermodel.HSSFCell;
+import org.apache.poi.hssf.usermodel.HSSFRow;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.util.CellRangeAddress;
+
+/**
+ * An example of how to merge regions of cells.
+ */
+public class MergedCells {
+ public static void main(String[] args) throws IOException {
+ try (HSSFWorkbook wb = new HSSFWorkbook()) {
+ HSSFSheet sheet = wb.createSheet("new sheet");
+
+ HSSFRow row = sheet.createRow(1);
+ HSSFCell cell = row.createCell(1);
+ cell.setCellValue("This is a test of merging");
+
+ sheet.addMergedRegion(new CellRangeAddress(1, 1, 1, 2));
+
+ // Write the output to a file
+ try (FileOutputStream fileOut = new FileOutputStream("workbook.xls")) {
+ wb.write(fileOut);
+ }
+ }
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/hssf/usermodel/NewLinesInCells.java b/src/examples/src/org/apache/poi/examples/hssf/usermodel/NewLinesInCells.java
new file mode 100644
index 0000000000..487668da10
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/hssf/usermodel/NewLinesInCells.java
@@ -0,0 +1,57 @@
+/* ====================================================================
+ 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.examples.hssf.usermodel;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.poi.hssf.usermodel.HSSFCell;
+import org.apache.poi.hssf.usermodel.HSSFCellStyle;
+import org.apache.poi.hssf.usermodel.HSSFFont;
+import org.apache.poi.hssf.usermodel.HSSFRow;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+
+/**
+ * Demonstrates how to use newlines in cells.
+ */
+public class NewLinesInCells {
+ public static void main( String[] args ) throws IOException {
+ try (HSSFWorkbook wb = new HSSFWorkbook()) {
+ HSSFSheet s = wb.createSheet();
+ HSSFFont f2 = wb.createFont();
+
+ HSSFCellStyle cs = wb.createCellStyle();
+
+ cs.setFont(f2);
+ // Word Wrap MUST be turned on
+ cs.setWrapText(true);
+
+ HSSFRow r = s.createRow(2);
+ r.setHeight((short) 0x349);
+ HSSFCell c = r.createCell(2);
+ c.setCellValue("Use \n with word wrap on to create a new line");
+ c.setCellStyle(cs);
+ s.setColumnWidth(2, (int) ((50 * 8) / ((double) 1 / 20)));
+
+ try (FileOutputStream fileOut = new FileOutputStream("workbook.xls")) {
+ wb.write(fileOut);
+ }
+ }
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/hssf/usermodel/NewSheet.java b/src/examples/src/org/apache/poi/examples/hssf/usermodel/NewSheet.java
new file mode 100644
index 0000000000..d2d8f31d0a
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/hssf/usermodel/NewSheet.java
@@ -0,0 +1,44 @@
+/* ====================================================================
+ 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.examples.hssf.usermodel;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.util.WorkbookUtil;
+
+/**
+ * Creates a new workbook with a sheet that's been explicitly defined.
+ */
+public abstract class NewSheet {
+ public static void main(String[] args) throws IOException {
+ try (HSSFWorkbook wb = new HSSFWorkbook()) {
+ wb.createSheet("new sheet");
+ // create with default name
+ wb.createSheet();
+ final String name = "second sheet";
+ // setting sheet name later
+ wb.setSheetName(1, WorkbookUtil.createSafeSheetName(name));
+
+ try (FileOutputStream fileOut = new FileOutputStream("workbook.xls")) {
+ wb.write(fileOut);
+ }
+ }
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/hssf/usermodel/NewWorkbook.java b/src/examples/src/org/apache/poi/examples/hssf/usermodel/NewWorkbook.java
new file mode 100644
index 0000000000..16c0e04de1
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/hssf/usermodel/NewWorkbook.java
@@ -0,0 +1,38 @@
+
+/* ====================================================================
+ 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.examples.hssf.usermodel;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+
+/**
+ * This example creates a new blank workbook. This workbook will contain a single blank sheet.
+ */
+public class NewWorkbook {
+ public static void main(String[] args) throws IOException {
+ try (HSSFWorkbook wb = new HSSFWorkbook()) {
+ try (FileOutputStream fileOut = new FileOutputStream("workbook.xls")) {
+ wb.write(fileOut);
+ }
+ }
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/hssf/usermodel/OfficeDrawing.java b/src/examples/src/org/apache/poi/examples/hssf/usermodel/OfficeDrawing.java
new file mode 100644
index 0000000000..4603864d86
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/hssf/usermodel/OfficeDrawing.java
@@ -0,0 +1,330 @@
+/* ====================================================================
+ 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.examples.hssf.usermodel;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.poi.hssf.usermodel.HSSFChildAnchor;
+import org.apache.poi.hssf.usermodel.HSSFClientAnchor;
+import org.apache.poi.hssf.usermodel.HSSFFont;
+import org.apache.poi.hssf.usermodel.HSSFPatriarch;
+import org.apache.poi.hssf.usermodel.HSSFPicture;
+import org.apache.poi.hssf.usermodel.HSSFPolygon;
+import org.apache.poi.hssf.usermodel.HSSFRichTextString;
+import org.apache.poi.hssf.usermodel.HSSFRow;
+import org.apache.poi.hssf.usermodel.HSSFShape;
+import org.apache.poi.hssf.usermodel.HSSFShapeGroup;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.usermodel.HSSFSimpleShape;
+import org.apache.poi.hssf.usermodel.HSSFTextbox;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.ClientAnchor.AnchorType;
+import org.apache.poi.ss.usermodel.Font;
+import org.apache.poi.ss.usermodel.Workbook;
+
+/**
+ * Demonstrates how to use the office drawing capabilities of POI.
+ */
+public final class OfficeDrawing {
+ private OfficeDrawing() {}
+
+ public static void main(String[] args) throws IOException {
+ // Create the workbook and sheets.
+ try (HSSFWorkbook wb = new HSSFWorkbook()) {
+ HSSFSheet sheet1 = wb.createSheet("new sheet");
+ HSSFSheet sheet2 = wb.createSheet("second sheet");
+ HSSFSheet sheet3 = wb.createSheet("third sheet");
+ HSSFSheet sheet4 = wb.createSheet("fourth sheet");
+ HSSFSheet sheet5 = wb.createSheet("fifth sheet");
+
+ // Draw stuff in them
+ drawSheet1(sheet1);
+ drawSheet2(sheet2);
+ drawSheet3(sheet3);
+ drawSheet4(sheet4, wb);
+ drawSheet5(sheet5, wb);
+
+ // Write the file out.
+ try (FileOutputStream fileOut = new FileOutputStream("workbook.xls")) {
+ wb.write(fileOut);
+ }
+ }
+ }
+
+ private static void drawSheet1( HSSFSheet sheet1 )
+ {
+ // Create a row and size one of the cells reasonably large.
+ HSSFRow row = sheet1.createRow(2);
+ row.setHeight((short) 2800);
+ row.createCell(1);
+ sheet1.setColumnWidth(2, 9000);
+
+ // Create the drawing patriarch. This is the top level container for
+ // all shapes.
+ HSSFPatriarch patriarch = sheet1.createDrawingPatriarch();
+
+ // Draw some lines and an oval.
+ drawLinesToCenter( patriarch );
+ drawManyLines( patriarch );
+ drawOval( patriarch );
+ drawPolygon( patriarch );
+
+ // Draw a rectangle.
+ HSSFSimpleShape rect = patriarch.createSimpleShape( new HSSFClientAnchor(100, 100, 900, 200, (short)0, 0, (short)0, 0) );
+ rect.setShapeType(HSSFSimpleShape.OBJECT_TYPE_RECTANGLE);
+ }
+
+ private static void drawSheet2( HSSFSheet sheet2 )
+ {
+ // Create a row and size one of the cells reasonably large.
+ HSSFRow row = sheet2.createRow(2);
+ row.createCell(1);
+ row.setHeightInPoints(240);
+ sheet2.setColumnWidth(2, 9000);
+
+ // Create the drawing patriarch. This is the top level container for
+ // all shapes. This will clear out any existing shapes for that sheet.
+ HSSFPatriarch patriarch = sheet2.createDrawingPatriarch();
+
+ // Draw a grid in one of the cells.
+ drawGrid( patriarch );
+ }
+
+ private static void drawSheet3( HSSFSheet sheet3 )
+ {
+ // Create a row and size one of the cells reasonably large
+ HSSFRow row = sheet3.createRow(2);
+ row.setHeightInPoints(140);
+ row.createCell(1);
+ sheet3.setColumnWidth(2, 9000);
+
+ // Create the drawing patriarch. This is the top level container for
+ // all shapes. This will clear out any existing shapes for that sheet.
+ HSSFPatriarch patriarch = sheet3.createDrawingPatriarch();
+
+ // Create a shape group.
+ HSSFShapeGroup group = patriarch.createGroup(
+ new HSSFClientAnchor(0,0,900,200,(short)2,2,(short)2,2));
+
+ // Create a couple of lines in the group.
+ HSSFSimpleShape shape1 = group.createShape(new HSSFChildAnchor(3,3,500,500));
+ shape1.setShapeType(HSSFSimpleShape.OBJECT_TYPE_LINE);
+ ( (HSSFChildAnchor) shape1.getAnchor() ).setAnchor((short)3,3,500,500);
+ HSSFSimpleShape shape2 = group.createShape(new HSSFChildAnchor((short)1,200,400,600));
+ shape2.setShapeType(HSSFSimpleShape.OBJECT_TYPE_LINE);
+
+ }
+
+ private static void drawSheet4( HSSFSheet sheet4, HSSFWorkbook wb )
+ {
+ // Create the drawing patriarch. This is the top level container for
+ // all shapes. This will clear out any existing shapes for that sheet.
+ HSSFPatriarch patriarch = sheet4.createDrawingPatriarch();
+
+ // Create a couple of textboxes
+ HSSFTextbox textbox1 = patriarch.createTextbox(
+ new HSSFClientAnchor(0,0,0,0,(short)1,1,(short)2,2));
+ textbox1.setString(new HSSFRichTextString("This is a test") );
+ HSSFTextbox textbox2 = patriarch.createTextbox(
+ new HSSFClientAnchor(0,0,900,100,(short)3,3,(short)3,4));
+ textbox2.setString(new HSSFRichTextString("Woo") );
+ textbox2.setFillColor(200,0,0);
+ textbox2.setLineStyle(HSSFShape.LINESTYLE_DOTGEL);
+
+ // Create third one with some fancy font styling.
+ HSSFTextbox textbox3 = patriarch.createTextbox(
+ new HSSFClientAnchor(0,0,900,100,(short)4,4,(short)5,4+1));
+ HSSFFont font = wb.createFont();
+ font.setItalic(true);
+ font.setUnderline(Font.U_DOUBLE);
+ HSSFRichTextString string = new HSSFRichTextString("Woo!!!");
+ string.applyFont(2,5,font);
+ textbox3.setString(string );
+ textbox3.setFillColor(0x08000030);
+ textbox3.setLineStyle(HSSFShape.LINESTYLE_NONE); // no line around the textbox.
+ textbox3.setNoFill(true); // make it transparent
+ }
+
+ private static void drawSheet5( HSSFSheet sheet5, HSSFWorkbook wb ) throws IOException
+ {
+
+ // Create the drawing patriarch. This is the top level container for
+ // all shapes. This will clear out any existing shapes for that sheet.
+ HSSFPatriarch patriarch = sheet5.createDrawingPatriarch();
+
+ HSSFClientAnchor anchor;
+ anchor = new HSSFClientAnchor(0,0,0,255,(short)2,2,(short)4,7);
+ anchor.setAnchorType( AnchorType.MOVE_DONT_RESIZE );
+ patriarch.createPicture(anchor, loadPicture( "src/resources/logos/logoKarmokar4.png", wb ));
+
+ anchor = new HSSFClientAnchor(0,0,0,255,(short)4,2,(short)5,7);
+ anchor.setAnchorType( AnchorType.MOVE_DONT_RESIZE );
+ patriarch.createPicture(anchor, loadPicture( "src/resources/logos/logoKarmokar4edited.png", wb ));
+
+ anchor = new HSSFClientAnchor(0,0,1023,255,(short)6,2,(short)8,7);
+ anchor.setAnchorType( AnchorType.MOVE_DONT_RESIZE );
+ HSSFPicture picture = patriarch.createPicture(anchor, loadPicture( "src/resources/logos/logoKarmokar4s.png", wb ));
+ //Reset the image to the original size.
+ picture.resize();
+ picture.setLineStyle( HSSFShape.LINESTYLE_DASHDOTGEL );
+
+ }
+
+ private static int loadPicture( String path, HSSFWorkbook wb ) throws IOException
+ {
+ int pictureIndex;
+ try (FileInputStream fis = new FileInputStream(path);
+ ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
+ int c;
+ while ((c = fis.read()) != -1)
+ bos.write(c);
+ pictureIndex = wb.addPicture(bos.toByteArray(), Workbook.PICTURE_TYPE_PNG);
+ }
+ return pictureIndex;
+ }
+
+ private static void drawOval( HSSFPatriarch patriarch )
+ {
+ // Create an oval and style to taste.
+ HSSFClientAnchor a = new HSSFClientAnchor();
+ a.setAnchor((short)2, 2, 20, 20, (short) 2, 2, 190, 80);
+ HSSFSimpleShape s = patriarch.createSimpleShape(a);
+ s.setShapeType(HSSFSimpleShape.OBJECT_TYPE_OVAL);
+ s.setLineStyleColor(10,10,10);
+ s.setFillColor(90,10,200);
+ s.setLineWidth(HSSFShape.LINEWIDTH_ONE_PT * 3);
+ s.setLineStyle(HSSFShape.LINESTYLE_DOTSYS);
+ }
+
+ private static void drawPolygon( HSSFPatriarch patriarch )
+ {
+ // HSSFClientAnchor a = new HSSFClientAnchor( 0, 0, 1023, 255, (short) 2, 2, (short) 3, 3 );
+ // HSSFPolygon p = patriarch.createPolygon(a);
+ // p.setPolygonDrawArea(100,100);
+ // p.setPoints( new int[]{30, 90, 50}, new int[]{88, 5, 44} );
+
+
+ HSSFClientAnchor a = new HSSFClientAnchor();
+ a.setAnchor( (short) 2, 2, 0, 0, (short) 3, 3, 1023, 255 );
+ HSSFShapeGroup g = patriarch.createGroup( a );
+ g.setCoordinates(0,0,200,200);
+ HSSFPolygon p1 = g.createPolygon( new HSSFChildAnchor( 0, 0, 200, 200 ) );
+ p1.setPolygonDrawArea( 100, 100 );
+ p1.setPoints( new int[]{0, 90, 50}, new int[]{5, 5, 44} );
+ p1.setFillColor( 0, 255, 0 );
+ HSSFPolygon p2 = g.createPolygon( new HSSFChildAnchor( 20, 20, 200, 200 ) );
+ p2.setPolygonDrawArea( 200, 200 );
+ p2.setPoints( new int[]{120, 20, 150}, new int[]{105, 30, 195} );
+ p2.setFillColor( 255, 0, 0 );
+ }
+
+ private static void drawManyLines( HSSFPatriarch patriarch )
+ {
+ // Draw bunch of lines
+ int x1 = 100;
+ int y1 = 100;
+ int x2 = 800;
+ int y2 = 200;
+ int color = 0;
+ for (int i = 0; i < 10; i++)
+ {
+ HSSFClientAnchor a2 = new HSSFClientAnchor();
+ a2.setAnchor((short) 2, 2, x1, y1, (short) 2, 2, x2, y2);
+ HSSFSimpleShape shape2 = patriarch.createSimpleShape(a2);
+ shape2.setShapeType(HSSFSimpleShape.OBJECT_TYPE_LINE);
+ shape2.setLineStyleColor(color);
+ y1 -= 10;
+ y2 -= 10;
+ color += 30;
+ }
+ }
+
+ private static void drawGrid( HSSFPatriarch patriarch )
+ {
+ // This draws a grid of lines. Since the coordinates space fixed at
+ // 1024 by 256 we use a ratio to get a reasonably square grids.
+
+ double xRatio = 3.22;
+ double yRatio = 0.6711;
+
+ int x1 = 0;
+ int y1 = 0;
+ int x2 = 0;
+ int y2 = 200;
+ for (int i = 0; i < 20; i++)
+ {
+ HSSFClientAnchor a2 = new HSSFClientAnchor();
+ a2.setAnchor((short) 2, 2, (int) ( x1 * xRatio ), (int) ( y1 * yRatio ),
+ (short) 2, 2, (int) ( x2 * xRatio ), (int) ( y2 * yRatio ));
+ HSSFSimpleShape shape2 = patriarch.createSimpleShape(a2);
+ shape2.setShapeType(HSSFSimpleShape.OBJECT_TYPE_LINE);
+
+ x1 += 10;
+ x2 += 10;
+ }
+
+ x1 = 0;
+ y1 = 0;
+ x2 = 200;
+ y2 = 0;
+ for (int i = 0; i < 20; i++)
+ {
+ HSSFClientAnchor a2 = new HSSFClientAnchor();
+ a2.setAnchor((short) 2, 2, (int) ( x1 * xRatio ), (int) ( y1 * yRatio ),
+ (short) 2, 2, (int) ( x2 * xRatio ), (int) ( y2 * yRatio ));
+ HSSFSimpleShape shape2 = patriarch.createSimpleShape(a2);
+ shape2.setShapeType(HSSFSimpleShape.OBJECT_TYPE_LINE);
+
+ y1 += 10;
+ y2 += 10;
+ }
+ }
+
+ private static void drawLinesToCenter( HSSFPatriarch patriarch )
+ {
+ // Draw some lines from and to the corners
+ {
+ HSSFClientAnchor a1 = new HSSFClientAnchor();
+ a1.setAnchor( (short)2, 2, 0, 0, (short) 2, 2, 512, 128);
+ HSSFSimpleShape shape1 = patriarch.createSimpleShape(a1);
+ shape1.setShapeType(HSSFSimpleShape.OBJECT_TYPE_LINE);
+ }
+ {
+ HSSFClientAnchor a1 = new HSSFClientAnchor();
+ a1.setAnchor( (short)2, 2, 512, 128, (short) 2, 2, 1024, 0);
+ HSSFSimpleShape shape1 = patriarch.createSimpleShape(a1);
+ shape1.setShapeType(HSSFSimpleShape.OBJECT_TYPE_LINE);
+ }
+ {
+ HSSFClientAnchor a1 = new HSSFClientAnchor();
+ a1.setAnchor( (short)1, 1, 0, 0, (short) 1, 1, 512, 100);
+ HSSFSimpleShape shape1 = patriarch.createSimpleShape(a1);
+ shape1.setShapeType(HSSFSimpleShape.OBJECT_TYPE_LINE);
+ }
+ {
+ HSSFClientAnchor a1 = new HSSFClientAnchor();
+ a1.setAnchor( (short)1, 1, 512, 100, (short) 1, 1, 1024, 0);
+ HSSFSimpleShape shape1 = patriarch.createSimpleShape(a1);
+ shape1.setShapeType(HSSFSimpleShape.OBJECT_TYPE_LINE);
+ }
+
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/hssf/usermodel/OfficeDrawingWithGraphics.java b/src/examples/src/org/apache/poi/examples/hssf/usermodel/OfficeDrawingWithGraphics.java
new file mode 100644
index 0000000000..eca55d4378
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/hssf/usermodel/OfficeDrawingWithGraphics.java
@@ -0,0 +1,113 @@
+/* ====================================================================
+ 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.examples.hssf.usermodel;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.RenderingHints;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.poi.hssf.usermodel.EscherGraphics;
+import org.apache.poi.hssf.usermodel.EscherGraphics2d;
+import org.apache.poi.hssf.usermodel.HSSFClientAnchor;
+import org.apache.poi.hssf.usermodel.HSSFPatriarch;
+import org.apache.poi.hssf.usermodel.HSSFRow;
+import org.apache.poi.hssf.usermodel.HSSFShapeGroup;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+
+/**
+ * Demonstrates the use of the EscherGraphics2d library.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class OfficeDrawingWithGraphics {
+ public static void main( String[] args ) throws IOException {
+ // Create a workbook with one sheet and size the first three somewhat
+ // larger so we can fit the chemical structure diagram in.
+ try (HSSFWorkbook wb = new HSSFWorkbook()) {
+ HSSFSheet sheet = wb.createSheet("my drawing");
+ sheet.setColumnWidth(1, 256 * 27);
+ HSSFRow row1 = sheet.createRow(0);
+ row1.setHeightInPoints(10 * 15f);
+ HSSFRow row2 = sheet.createRow(1);
+ row2.setHeightInPoints(5 * 15f);
+ HSSFRow row3 = sheet.createRow(2);
+ row3.setHeightInPoints(10 * 15f);
+
+ // Add some cells so we can test that the anchoring works when we
+ // sort them.
+ row1.createCell(0).setCellValue("C");
+ row2.createCell(0).setCellValue("A");
+ row3.createCell(0).setCellValue("B");
+
+ // Create the top level drawing patriarch.
+ HSSFPatriarch patriarch = sheet.createDrawingPatriarch();
+
+ HSSFClientAnchor a;
+ HSSFShapeGroup group;
+ EscherGraphics g;
+ EscherGraphics2d g2d;
+ // Anchor entirely within one cell.
+ a = new HSSFClientAnchor(0, 0, 1023, 255, (short) 1, 0, (short) 1, 0);
+ group = patriarch.createGroup(a);
+ group.setCoordinates(0, 0, 320, 276);
+ float verticalPointsPerPixel = a.getAnchorHeightInPoints(sheet) / Math.abs(group.getY2() - group.getY1());
+ g = new EscherGraphics(group, wb, Color.black, verticalPointsPerPixel);
+ g2d = new EscherGraphics2d(g);
+ drawStar(g2d);
+
+ a = new HSSFClientAnchor(0, 0, 1023, 255, (short) 1, 1, (short) 1, 1);
+ group = patriarch.createGroup(a);
+ group.setCoordinates(0, 0, 640, 276);
+ verticalPointsPerPixel = a.getAnchorHeightInPoints(sheet) / Math.abs(group.getY2() - group.getY1());
+// verticalPixelsPerPoint = (float)Math.abs(group.getY2() - group.getY1()) / a.getAnchorHeightInPoints(sheet);
+ g = new EscherGraphics(group, wb, Color.black, verticalPointsPerPixel);
+ g2d = new EscherGraphics2d(g);
+ drawStar(g2d);
+
+ try (FileOutputStream out = new FileOutputStream("workbook.xls")) {
+ wb.write(out);
+ }
+ }
+ }
+
+ private static void drawStar( EscherGraphics2d g2d )
+ {
+ g2d.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
+ for (double i = 0; i < Math.PI; i += 0.1)
+ {
+ g2d.setColor( new Color((int)(i * 5343062d) ) );
+ int x1 = (int) ( Math.cos(i) * 160.0 ) + 160;
+ int y1 = (int) ( Math.sin(i) * 138.0 ) + 138;
+ int x2 = (int) ( -Math.cos(i) * 160.0 ) + 160;
+ int y2 = (int) ( -Math.sin(i) * 138.0 ) + 138;
+ g2d.setStroke(new BasicStroke(2));
+ g2d.drawLine(x1,y1,x2,y2);
+ }
+ g2d.setFont(new Font("SansSerif",Font.BOLD | Font.ITALIC, 20));
+ g2d.drawString("EscherGraphics2d",70,100);
+ g2d.setColor(Color.yellow);
+ g2d.fillOval( 160-20,138-20,40,40);
+ g2d.setColor(Color.black);
+ g2d.fillPolygon(new int[] {-10+160,0+160,10+160,0+160}, new int[] {0+138,10+138,0+138,-10+138}, 4);
+ g2d.drawPolygon(new int[] {-160+160,0+160,160+160,0+160}, new int[] {0+138,138+138,0+138,-138+138}, 4);
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/hssf/usermodel/Outlines.java b/src/examples/src/org/apache/poi/examples/hssf/usermodel/Outlines.java
new file mode 100644
index 0000000000..d059a3d392
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/hssf/usermodel/Outlines.java
@@ -0,0 +1,181 @@
+/* ====================================================================
+ 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.examples.hssf.usermodel;
+
+import java.io.Closeable;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+
+import org.apache.poi.hssf.usermodel.HSSFCell;
+import org.apache.poi.hssf.usermodel.HSSFRow;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.util.POILogFactory;
+import org.apache.poi.util.POILogger;
+
+/**
+ * Creates outlines.
+ */
+public class Outlines implements Closeable {
+ public static void main(String[] args)
+ throws IOException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
+ POILogger LOGGER = POILogFactory.getLogger(Outlines.class);
+ for (int i=1; i<=13; i++) {
+ try (Outlines o = new Outlines()) {
+ String log = (String) Outlines.class.getDeclaredMethod("test" + i).invoke(o);
+ String filename = "outline" + i + ".xls";
+ o.writeOut(filename);
+ LOGGER.log(POILogger.INFO, filename + " written. " + log);
+ }
+ }
+ }
+
+ private final HSSFWorkbook wb = new HSSFWorkbook();
+ private final HSSFSheet sheet1 = wb.createSheet("new sheet");
+
+ public void writeOut(String filename) throws IOException {
+ try (FileOutputStream fileOut = new FileOutputStream(filename)) {
+ wb.write(fileOut);
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ wb.close();
+ }
+
+ public String test1() {
+ sheet1.groupColumn(4, 7);
+
+ for (int row = 0; row < 200; row++) {
+ HSSFRow r = sheet1.createRow(row);
+ for (int column = 0; column < 200; column++) {
+ HSSFCell c = r.createCell(column);
+ c.setCellValue(column);
+ }
+ }
+ return "Two expanded groups.";
+ }
+
+ public String test2() {
+ sheet1.groupColumn(2, 10);
+ sheet1.groupColumn(4, 7);
+ sheet1.setColumnGroupCollapsed(4, true);
+ return "Two groups. Inner group collapsed.";
+ }
+
+ public String test3() {
+ sheet1.groupColumn(2, 10);
+ sheet1.groupColumn(4, 7);
+ sheet1.setColumnGroupCollapsed(4, true);
+ sheet1.setColumnGroupCollapsed(2, true);
+ return "Two groups. Both collapsed.";
+ }
+
+ public String test4() {
+ sheet1.groupColumn(2, 10);
+ sheet1.groupColumn(4, 7);
+ sheet1.setColumnGroupCollapsed(4, true);
+ sheet1.setColumnGroupCollapsed(2, true);
+
+ sheet1.setColumnGroupCollapsed(4, false);
+ return "Two groups. Collapsed then inner group expanded.";
+ }
+
+ public String test5() {
+ sheet1.groupColumn(2, 10);
+ sheet1.groupColumn(4, 7);
+ sheet1.setColumnGroupCollapsed(4, true);
+ sheet1.setColumnGroupCollapsed(2, true);
+
+ sheet1.setColumnGroupCollapsed(4, false);
+ sheet1.setColumnGroupCollapsed(3, false);
+ return "Two groups. Collapsed then reexpanded.";
+ }
+
+ public String test6() {
+ sheet1.groupColumn(2, 10);
+ sheet1.groupColumn(4, 10);
+ sheet1.setColumnGroupCollapsed(4, true);
+ sheet1.setColumnGroupCollapsed(2, true);
+
+ sheet1.setColumnGroupCollapsed(3, false);
+ return "Two groups with matching end points. Second group collapsed.";
+ }
+
+ public String test7() {
+ sheet1.groupRow(5, 14);
+ sheet1.groupRow(7, 10);
+ return "Row outlines.";
+ }
+
+ public String test8() {
+ sheet1.groupRow(5, 14);
+ sheet1.groupRow(7, 10);
+ sheet1.setRowGroupCollapsed(7, true);
+ return "Row outlines. Inner group collapsed.";
+ }
+
+ public String test9() {
+ sheet1.groupRow(5, 14);
+ sheet1.groupRow(7, 10);
+ sheet1.setRowGroupCollapsed(7, true);
+ sheet1.setRowGroupCollapsed(5, true);
+ return "Row outlines. Both collapsed.";
+ }
+
+ public String test10() {
+ sheet1.groupRow(5, 14);
+ sheet1.groupRow(7, 10);
+ sheet1.setRowGroupCollapsed(7, true);
+ sheet1.setRowGroupCollapsed(5, true);
+ sheet1.setRowGroupCollapsed(8, false);
+ return "Row outlines. Collapsed then inner group expanded.";
+ }
+
+ public String test11() {
+ sheet1.groupRow(5, 14);
+ sheet1.groupRow(7, 10);
+ sheet1.setRowGroupCollapsed(7, true);
+ sheet1.setRowGroupCollapsed(5, true);
+ sheet1.setRowGroupCollapsed(8, false);
+ sheet1.setRowGroupCollapsed(14, false);
+ return "Row outlines. Collapsed then expanded.";
+ }
+
+ public String test12() {
+ sheet1.groupRow(5, 14);
+ sheet1.groupRow(7, 14);
+ sheet1.setRowGroupCollapsed(7, true);
+ sheet1.setRowGroupCollapsed(5, true);
+ sheet1.setRowGroupCollapsed(6, false);
+ return "Row outlines. Two row groups with matching end points. Second group collapsed.";
+ }
+
+ public String test13() {
+ sheet1.groupRow(5, 14);
+ sheet1.groupRow(7, 14);
+ sheet1.groupRow(16, 19);
+
+ sheet1.groupColumn(4, 7);
+ sheet1.groupColumn(9, 12);
+ sheet1.groupColumn(10, 11);
+ return "Mixed bag.";
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/hssf/usermodel/ReadWriteWorkbook.java b/src/examples/src/org/apache/poi/examples/hssf/usermodel/ReadWriteWorkbook.java
new file mode 100644
index 0000000000..e36b7ad26c
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/hssf/usermodel/ReadWriteWorkbook.java
@@ -0,0 +1,56 @@
+/* ====================================================================
+ 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.examples.hssf.usermodel;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.poi.hssf.usermodel.HSSFCell;
+import org.apache.poi.hssf.usermodel.HSSFRow;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+
+/**
+ * This example demonstrates opening a workbook, modifying it and writing
+ * the results back out.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class ReadWriteWorkbook {
+ public static void main(String[] args) throws IOException {
+ try (FileInputStream fileIn = new FileInputStream("workbook.xls")) {
+ POIFSFileSystem fs = new POIFSFileSystem(fileIn);
+ HSSFWorkbook wb = new HSSFWorkbook(fs);
+ HSSFSheet sheet = wb.getSheetAt(0);
+ HSSFRow row = sheet.getRow(2);
+ if (row == null)
+ row = sheet.createRow(2);
+ HSSFCell cell = row.getCell(3);
+ if (cell == null)
+ cell = row.createCell(3);
+ cell.setCellValue("a test");
+
+ // Write the output to a file
+ try (FileOutputStream fileOut = new FileOutputStream("workbookout.xls")) {
+ wb.write(fileOut);
+ }
+ }
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/hssf/usermodel/RepeatingRowsAndColumns.java b/src/examples/src/org/apache/poi/examples/hssf/usermodel/RepeatingRowsAndColumns.java
new file mode 100644
index 0000000000..db737cd5bb
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/hssf/usermodel/RepeatingRowsAndColumns.java
@@ -0,0 +1,64 @@
+/* ====================================================================
+ 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.examples.hssf.usermodel;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.poi.hssf.usermodel.HSSFCell;
+import org.apache.poi.hssf.usermodel.HSSFCellStyle;
+import org.apache.poi.hssf.usermodel.HSSFFont;
+import org.apache.poi.hssf.usermodel.HSSFRow;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.util.CellRangeAddress;
+
+public class RepeatingRowsAndColumns {
+ public static void main(String[] args) throws IOException {
+ try (HSSFWorkbook wb = new HSSFWorkbook()) {
+ HSSFSheet sheet1 = wb.createSheet("first sheet");
+ HSSFSheet sheet2 = wb.createSheet("second sheet");
+ HSSFSheet sheet3 = wb.createSheet("third sheet");
+
+ HSSFFont boldFont = wb.createFont();
+ boldFont.setFontHeightInPoints((short) 22);
+ boldFont.setBold(true);
+
+ HSSFCellStyle boldStyle = wb.createCellStyle();
+ boldStyle.setFont(boldFont);
+
+ HSSFRow row = sheet1.createRow(1);
+ HSSFCell cell = row.createCell(0);
+ cell.setCellValue("This quick brown fox");
+ cell.setCellStyle(boldStyle);
+
+ // Set the columns to repeat from column 0 to 2 on the first sheet
+ sheet1.setRepeatingColumns(CellRangeAddress.valueOf("A:C"));
+ // Set the rows to repeat from row 0 to 2 on the second sheet.
+ sheet2.setRepeatingRows(CellRangeAddress.valueOf("1:3"));
+ // Set the the repeating rows and columns on the third sheet.
+ CellRangeAddress cra = CellRangeAddress.valueOf("D1:E2");
+ sheet3.setRepeatingColumns(cra);
+ sheet3.setRepeatingRows(cra);
+
+ try (FileOutputStream fileOut = new FileOutputStream("workbook.xls")) {
+ wb.write(fileOut);
+ }
+ }
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/hssf/usermodel/SplitAndFreezePanes.java b/src/examples/src/org/apache/poi/examples/hssf/usermodel/SplitAndFreezePanes.java
new file mode 100644
index 0000000000..54c3879e41
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/hssf/usermodel/SplitAndFreezePanes.java
@@ -0,0 +1,51 @@
+
+/* ====================================================================
+ 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.examples.hssf.usermodel;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.Sheet;
+
+public class SplitAndFreezePanes {
+ public static void main(String[] args) throws IOException {
+ try (HSSFWorkbook wb = new HSSFWorkbook()) {
+ HSSFSheet sheet1 = wb.createSheet("new sheet");
+ HSSFSheet sheet2 = wb.createSheet("second sheet");
+ HSSFSheet sheet3 = wb.createSheet("third sheet");
+ HSSFSheet sheet4 = wb.createSheet("fourth sheet");
+
+ // Freeze just one row
+ sheet1.createFreezePane(0, 1, 0, 1);
+ // Freeze just one column
+ sheet2.createFreezePane(1, 0, 1, 0);
+ // Freeze the columns and rows (forget about scrolling position of the lower right quadrant).
+ sheet3.createFreezePane(2, 2);
+ // Create a split with the lower left side being the active quadrant
+ sheet4.createSplitPane(2000, 2000, 0, 0, Sheet.PANE_LOWER_LEFT);
+
+ try (FileOutputStream fileOut = new FileOutputStream("workbook.xls")) {
+ wb.write(fileOut);
+ }
+ }
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/hssf/usermodel/WorkingWithFonts.java b/src/examples/src/org/apache/poi/examples/hssf/usermodel/WorkingWithFonts.java
new file mode 100644
index 0000000000..0c87a335b3
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/hssf/usermodel/WorkingWithFonts.java
@@ -0,0 +1,63 @@
+/* ====================================================================
+ 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.examples.hssf.usermodel;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.poi.hssf.usermodel.HSSFCell;
+import org.apache.poi.hssf.usermodel.HSSFCellStyle;
+import org.apache.poi.hssf.usermodel.HSSFFont;
+import org.apache.poi.hssf.usermodel.HSSFRow;
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+
+/**
+ * Demonstrates how to create and use fonts.
+ */
+public class WorkingWithFonts {
+ public static void main(String[] args) throws IOException {
+ try (HSSFWorkbook wb = new HSSFWorkbook()) {
+ HSSFSheet sheet = wb.createSheet("new sheet");
+
+ // Create a row and put some cells in it. Rows are 0 based.
+ HSSFRow row = sheet.createRow(1);
+
+ // Create a new font and alter it.
+ HSSFFont font = wb.createFont();
+ font.setFontHeightInPoints((short) 24);
+ font.setFontName("Courier New");
+ font.setItalic(true);
+ font.setStrikeout(true);
+
+ // Fonts are set into a style so create a new one to use.
+ HSSFCellStyle style = wb.createCellStyle();
+ style.setFont(font);
+
+ // Create a cell and put a value in it.
+ HSSFCell cell = row.createCell(1);
+ cell.setCellValue("This is a test of fonts");
+ cell.setCellStyle(style);
+
+ // Write the output to a file
+ try (FileOutputStream fileOut = new FileOutputStream("workbook.xls")) {
+ wb.write(fileOut);
+ }
+ }
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/hssf/usermodel/ZoomSheet.java b/src/examples/src/org/apache/poi/examples/hssf/usermodel/ZoomSheet.java
new file mode 100644
index 0000000000..e958489cf7
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/hssf/usermodel/ZoomSheet.java
@@ -0,0 +1,45 @@
+
+/* ====================================================================
+ 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.examples.hssf.usermodel;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+
+/**
+ * Sets the zoom magnication for a sheet.
+ *
+ * @author Glen Stampoultzis (glens at apache.org)
+ */
+public class ZoomSheet
+{
+ public static void main(String[] args) throws IOException {
+ try (HSSFWorkbook wb = new HSSFWorkbook()) {
+ HSSFSheet sheet1 = wb.createSheet("new sheet");
+ sheet1.setZoom(75); // 75 percent magnification
+
+ try (FileOutputStream fileOut = new FileOutputStream("workbook.xls")) {
+ wb.write(fileOut);
+ }
+ }
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/hwpf/Word2Forrest.java b/src/examples/src/org/apache/poi/examples/hwpf/Word2Forrest.java
new file mode 100644
index 0000000000..937da16a33
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/hwpf/Word2Forrest.java
@@ -0,0 +1,226 @@
+/* ====================================================================
+ 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.examples.hwpf;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.poi.hwpf.HWPFDocument;
+import org.apache.poi.hwpf.model.StyleDescription;
+import org.apache.poi.hwpf.model.StyleSheet;
+import org.apache.poi.hwpf.usermodel.CharacterRun;
+import org.apache.poi.hwpf.usermodel.Paragraph;
+import org.apache.poi.hwpf.usermodel.Range;
+
+@SuppressWarnings({"java:S106","java:S4823"})
+public final class Word2Forrest
+{
+ Writer _out;
+ HWPFDocument _doc;
+
+ @SuppressWarnings("unused")
+ public Word2Forrest(HWPFDocument doc, OutputStream stream) throws IOException
+ {
+ _out = new OutputStreamWriter (stream, StandardCharsets.UTF_8);
+ _doc = doc;
+
+ init ();
+ openDocument ();
+ openBody ();
+
+ Range r = doc.getRange ();
+ StyleSheet styleSheet = doc.getStyleSheet ();
+
+ int sectionLevel = 0;
+ int lenParagraph = r.numParagraphs ();
+ boolean inCode = false;
+ for (int x = 0; x < lenParagraph; x++)
+ {
+ Paragraph p = r.getParagraph (x);
+ String text = p.text ();
+ if (text.trim ().length () == 0)
+ {
+ continue;
+ }
+ StyleDescription paragraphStyle = styleSheet.getStyleDescription (p.
+ getStyleIndex ());
+ String styleName = paragraphStyle.getName();
+ if (styleName.startsWith ("Heading"))
+ {
+ if (inCode)
+ {
+ closeSource();
+ inCode = false;
+ }
+
+ int headerLevel = Integer.parseInt (styleName.substring (8));
+ if (headerLevel > sectionLevel)
+ {
+ openSection ();
+ }
+ else
+ {
+ for (int y = 0; y < (sectionLevel - headerLevel) + 1; y++)
+ {
+ closeSection ();
+ }
+ openSection ();
+ }
+ sectionLevel = headerLevel;
+ openTitle ();
+ writePlainText (text);
+ closeTitle ();
+ }
+ else
+ {
+ int cruns = p.numCharacterRuns ();
+ CharacterRun run = p.getCharacterRun (0);
+ String fontName = run.getFontName();
+ if (fontName.startsWith ("Courier"))
+ {
+ if (!inCode)
+ {
+ openSource ();
+ inCode = true;
+ }
+ writePlainText (p.text());
+ }
+ else
+ {
+ if (inCode)
+ {
+ inCode = false;
+ closeSource();
+ }
+ openParagraph();
+ writePlainText(p.text());
+ closeParagraph();
+ }
+ }
+ }
+ for (int x = 0; x < sectionLevel; x++)
+ {
+ closeSection();
+ }
+ closeBody();
+ closeDocument();
+ _out.flush();
+
+ }
+
+ public void init ()
+ throws IOException
+ {
+ _out.write ("\r\n");
+ _out.write ("\r\n");
+ }
+
+ public void openDocument ()
+ throws IOException
+ {
+ _out.write ("\r\n");
+ }
+ public void closeDocument ()
+ throws IOException
+ {
+ _out.write ("\r\n");
+ }
+
+
+ public void openBody ()
+ throws IOException
+ {
+ _out.write ("\r\n");
+ }
+
+ public void closeBody ()
+ throws IOException
+ {
+ _out.write ("\r\n");
+ }
+
+
+ public void openSection ()
+ throws IOException
+ {
+ _out.write ("");
+
+ }
+
+ public void closeSection ()
+ throws IOException
+ {
+ _out.write ("");
+
+ }
+
+ public void openTitle ()
+ throws IOException
+ {
+ _out.write ("");
+ }
+
+ public void closeTitle ()
+ throws IOException
+ {
+ _out.write ("");
+ }
+
+ public void writePlainText (String text)
+ throws IOException
+ {
+ _out.write (text);
+ }
+
+ public void openParagraph ()
+ throws IOException
+ {
+ _out.write ("
");
+ }
+
+ public void openSource ()
+ throws IOException
+ {
+ _out.write ("");
+ }
+
+
+ public static void main(String[] args) throws IOException {
+ try (InputStream is = new FileInputStream(args[0]);
+ OutputStream out = new FileOutputStream("test.xml")) {
+ new Word2Forrest(new HWPFDocument(is), out);
+ }
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/ss/AddDimensionedImage.java b/src/examples/src/org/apache/poi/examples/ss/AddDimensionedImage.java
new file mode 100644
index 0000000000..6e102038b6
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/ss/AddDimensionedImage.java
@@ -0,0 +1,1011 @@
+/* ====================================================================
+ 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.examples.ss;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Locale;
+
+import org.apache.poi.hssf.usermodel.HSSFSheet;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.ClientAnchor;
+import org.apache.poi.ss.usermodel.ClientAnchor.AnchorType;
+import org.apache.poi.ss.usermodel.Drawing;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.util.CellReference;
+import org.apache.poi.util.IOUtils;
+
+
+/**
+ * Demonstrates how to add an image to a worksheet and set that images size
+ * to a specific number of millimetres irrespective of the width of the columns
+ * or height of the rows. Overridden methods are provided so that the location
+ * of the image - the cells row and column coordinates that define the top
+ * left hand corners of the image - can be identified either in the familiar
+ * Excel manner - A1 for instance - or using POI's methodology of a column and
+ * row index where 0, 0 would indicate cell A1.
+ *
+ * The best way to make use of these techniques is to delay adding the image to
+ * the sheet until all other work has been completed. That way, the sizes of
+ * all rows and columns will have been adjusted - assuming that step was
+ * necessary. Even though the anchors type is set to prevent the image moving
+ * or re-sizing, this setting does not have any effect until the sheet is being
+ * viewed using the Excel application.
+ *
+ * The key to the process is the ClientAnchor class. It defines methods that allow
+ * us to define the location of an image by specifying the following;
+ *
+ * * How far - in terms of coordinate positions - the image should be inset
+ * from the left hand border of a cell.
+ * * How far - in terms of coordinate positions - the image should be inset
+ * from the from the top of the cell.
+ * * How far - in terms of coordinate positions - the right hand edge of
+ * the image should protrude into a cell (measured from the cells left hand
+ * edge to the images right hand edge).
+ * * How far - in terms of coordinate positions - the bottom edge of the
+ * image should protrude into a row (measured from the cells top edge to
+ * the images bottom edge).
+ * * The index of the column that contains the cell whose top left hand
+ * corner should be aligned with the top left hand corner of the image.
+ * * The index of the row that contains the cell whose top left hand corner
+ * should be aligned with the images top left hand corner.
+ * * The index of the column that contains the cell whose top left hand
+ * corner should be aligned with the images bottom right hand corner
+ * * The index number of the row that contains the cell whose top left
+ * hand corner should be aligned with the images bottom right hand corner.
+ *
+ * It can be used to add an image into cell A1, for example, in the following
+ * manner;
+ *
+ * ClientAnchor anchor = sheet.getWorkbook().getCreationHelper().createClientAnchor();
+ *
+ * anchor.setDx1(0);
+ * anchor.setDy1(0);
+ * anchor.setDx2(0);
+ * anchor.setDy2(0);
+ * anchor.setCol1(0);
+ * anchor.setRow1(0);
+ * anchor.setCol2(1);
+ * anchor.setRow2(1);
+ *
+ * Taken together, the first four methods define the locations of the top left
+ * and bottom right hand corners of the image if you imagine that the image is
+ * represented by a simple rectangle. The setDx1() and setDy1() methods locate
+ * the top left hand corner of the image while setDx2() and and Dy2() locate the
+ * bottom right hand corner of the image. An individual image can be inserted
+ * into a single cell or is can lie across many cells and the latter four methods
+ * are used to define just where the image should be positioned. They do this by
+ * again by identifying where the top left and bottom right hand corners of the
+ * image should be located but this time in terms of the indexes of the cells
+ * in which those corners should be located. The setCol1() and setRow1() methods
+ * together identify the cell that should contain the top left hand corner of
+ * the image while setCol2() and setRow2() do the same for the images bottom
+ * right hand corner.
+ *
+ * Knowing that, it is possible to look again at the example above and to see
+ * that the top left hand corner of the image will be located in cell A1 (0, 0)
+ * and it will be aligned with the very top left hand corner of the cell. Likewise,
+ * the bottom right hand corner of the image will be located in cell B2 (1, 1) and
+ * it will again be aligned with the top left hand corner of the cell. This has the
+ * effect of making the image seem to occupy the whole of cell A1. Interestingly, it
+ * also has an effect on the images resizing behaviour because testing has
+ * demonstrated that if the image is wholly contained within one cell and is not
+ * 'attached' for want of a better word, to a neighbouring cell, then that image
+ * will not increase in size in response to the user dragging the column wider
+ * or the cell higher.
+ *
+ * The following example demonstrates a slightly different way to insert an
+ * image into cell A1 and to ensure that it occupies the whole of the cell. This
+ * is accomplished by specifying the the images bottom right hand corner should be
+ * aligned with the bottom right hand corner of the cell. It is also a case
+ * where the image will not increase in size if the user increases the size of
+ * the enclosing cell - irrespective of the anchors type - but it will reduce in
+ * size if the cell is made smaller.
+ *
+ * ClientAnchor anchor = sheet.getWorkbook().getCreationHelper().createClientAnchor();
+ *
+ * anchor.setDx1(0);
+ * anchor.setDy1(0);
+ * anchor.setDx2(1023);
+ * anchor.setDy2(255);
+ * anchor.setCol1(0);
+ * anchor.setRow1(0);
+ * anchor.setCol2(0);
+ * anchor.setRow2(0);
+ *
+ * Note that the final four method calls all pass the same value and seem to
+ * indicate that the images top left hand corner is aligned with the top left
+ * hand corner of cell A1 and that it's bottom right hand corner is also
+ * aligned with the top left hand corner of cell A1. Yet, running this code
+ * would see the image fully occupying cell A1. That is the result of the
+ * values passed to parameters three and four; these I have referred to as
+ * determining the images coordinates within the cell. They indicate that the
+ * image should occupy - in order - the full width of the column and the full
+ * height of the row.
+ *
+ * The co-ordinate values shown are the maxima; and they are independent of
+ * row height/column width and of the font used. Passing 255 will always result
+ * in the image occupying the full height of the row and passing 1023 will
+ * always result in the image occupying the full width of the column. They help
+ * in situations where an image is larger than a column/row and must overlap
+ * into the next column/row. Using them does mean, however, that it is often
+ * necessary to perform conversions between Excels characters units, points,
+ * pixels and millimetres in order to establish how many rows/columns an image
+ * should occupy and just what the various insets ought to be.
+ *
+ * Note that the setDx1(int) and setDy1(int) methods of the ClientAchor class
+ * are not made use of in the code that follows. It would be fairly trivial
+ * however to extend this example further and provide methods that would centre
+ * an image within a cell or allow the user to specify that a plain border a
+ * fixed number of millimetres wide should wrap around the image. Those first
+ * two parameters would make this sort of functionality perfectly possible.
+ *
+ * Owing to the various conversions used, the actual size of the image may vary
+ * from that required; testing has so far found this to be in the region of
+ * plus or minus two millimetres. Most likely by modifying the way the
+ * calculations are performed - possibly using double(s) throughout and
+ * rounding the values at the correct point - it is likely that these errors
+ * could be reduced or removed.
+ *
+ * A note concerning Excels image resizing behaviour. The ClientAnchor
+ * class contains a method called setAnchorType(int) which can be used to
+ * determine how Excel will resize an image in response to the user increasing
+ * or decreasing the dimensions of the cell containing the image. There are
+ * three values that can be passed to this method; 0 = To move and size the
+ * image with the cell, 2 = To move but don't size the image with the cell,
+ * 3 = To prevent the image from moving or being resized along with the cell. If
+ * an image is inserted using this class and placed into a single cell then if
+ * the setAnchorType(int) method is called and a value of either 0 or 2 passed
+ * to it, the resultant resizing behaviour may be a surprise. The image will not
+ * grow in size of the column is made wider or the row higher but it will shrink
+ * if the columns width or rows height are reduced.
+ *
+ * @author Mark Beardsley [msb at apache.org] and Mark Southern [southern at scripps.edu]
+ * @version 1.00 5th August 2009.
+ * 2.00 26th February 2010.
+ * Ported to make use of the the SS usermodel classes.
+ * Ability to reuse the Drawing Patriarch so that multiple images
+ * can be inserted without unintentionally erasing earlier images.
+ * Check on image type added; i.e. jpg, jpeg or png.
+ * The String used to contain the files name is now converted
+ * into a URL.
+ * 2.10 17th May 2012
+ * Corrected gross error that occurred when using the code with
+ * XSSF or SXSSF workbooks. In short, the code did not correctly
+ * calculate the size of the image(s) owing to the use of EMUs
+ * within the OOXML file format. That problem has largely been
+ * corrected although it should be mentioned that images are not
+ * sized with the same level of accuracy. Discrepancies of up to
+ * 2mm have been noted in testing. Further investigation will
+ * continue to rectify this issue.
+ */
+@SuppressWarnings({"java:S106","java:S4823"})
+public class AddDimensionedImage {
+
+ // Four constants that determine how - and indeed whether - the rows
+ // and columns an image may overlie should be expanded to accommodate that
+ // image.
+ // Passing EXPAND_ROW will result in the height of a row being increased
+ // to accommodate the image if it is not already larger. The image will
+ // be layed across one or more columns.
+ // Passing EXPAND_COLUMN will result in the width of the column being
+ // increased to accommodate the image if it is not already larger. The image
+ // will be layed across one or many rows.
+ // Passing EXPAND_ROW_AND_COLUMN will result in the height of the row
+ // bing increased along with the width of the column to accomdate the
+ // image if either is not already larger.
+ // Passing OVERLAY_ROW_AND_COLUMN will result in the image being layed
+ // over one or more rows and columns. No row or column will be resized,
+ // instead, code will determine how many rows and columns the image should
+ // overlie.
+ public static final int EXPAND_ROW = 1;
+ public static final int EXPAND_COLUMN = 2;
+ public static final int EXPAND_ROW_AND_COLUMN = 3;
+ public static final int OVERLAY_ROW_AND_COLUMN = 7;
+
+ // Modified to support EMU - English Metric Units - used within the OOXML
+ // workbooks, this multoplier is used to convert between measurements in
+ // millimetres and in EMUs
+ private static final int EMU_PER_MM = 36000;
+
+ /**
+ * Add an image to a worksheet.
+ *
+ * @param cellNumber A String that contains the location of the cell whose
+ * top left hand corner should be aligned with the top
+ * left hand corner of the image; for example "A1", "A2"
+ * etc. This is to support the familiar Excel syntax.
+ * Whilst images are are not actually inserted into cells
+ * this provides a convenient method of indicating where
+ * the image should be positioned on the sheet.
+ * @param sheet A reference to the sheet that contains the cell referenced
+ * above.
+ * @param drawing An instance of the DrawingPatriarch class. This is now
+ * passed into the method where it was, previously, recovered
+ * from the sheet in order to allow multiple pictures be
+ * inserted. If the patriarch was not 'cached in this manner
+ * each time it was created any previously positioned images
+ * would be simply over-written.
+ * @param imageFile An instance of the URL class that encapsulates the name
+ * of and path to the image that is to be 'inserted into'
+ * the sheet.
+ * @param reqImageWidthMM A primitive double that contains the required
+ * width of the image in millimetres.
+ * @param reqImageHeightMM A primitive double that contains the required
+ * height of the image in millimetres.
+ * @param resizeBehaviour A primitive int whose value will determine how
+ * the code should react if the image is larger than
+ * the cell referenced by the cellNumber parameter.
+ * Four constants are provided to determine what
+ * should happen;
+ * AddDimensionedImage.EXPAND_ROW
+ * AddDimensionedImage.EXPAND_COLUMN
+ * AddDimensionedImage.EXPAND_ROW_AND_COLUMN
+ * AddDimensionedImage.OVERLAY_ROW_AND_COLUMN
+ * @throws java.io.FileNotFoundException If the file containing the image
+ * cannot be located.
+ * @throws java.io.IOException If a problem occurs whilst reading the file
+ * of image data.
+ * @throws java.lang.IllegalArgumentException If an invalid value is passed
+ * to the resizeBehaviour
+ * parameter.
+ */
+ public void addImageToSheet(String cellNumber, Sheet sheet, Drawing> drawing,
+ URL imageFile, double reqImageWidthMM, double reqImageHeightMM,
+ int resizeBehaviour) throws IOException, IllegalArgumentException {
+ // Convert the String into column and row indices then chain the
+ // call to the overridden addImageToSheet() method.
+ CellReference cellRef = new CellReference(cellNumber);
+ this.addImageToSheet(cellRef.getCol(), cellRef.getRow(), sheet, drawing,
+ imageFile, reqImageWidthMM, reqImageHeightMM,resizeBehaviour);
+ }
+
+ /**
+ * Add an image to a worksheet.
+ *
+ * @param colNumber A primitive int that contains the index number of a
+ * column on the worksheet; POI column indices are zero
+ * based. Together with the rowNumber parameter's value,
+ * this parameter identifies a cell on the worksheet. The
+ * images top left hand corner will be aligned with the
+ * top left hand corner of this cell.
+ * @param rowNumber A primitive int that contains the index number of a row
+ * on the worksheet; POI row indices are zero based.
+ * Together with the rowNumber parameter's value, this
+ * parameter identifies a cell on the worksheet. The
+ * images top left hand corner will be aligned with the
+ * top left hand corner of this cell.
+ * @param sheet A reference to the sheet that contains the cell identified
+ * by the two parameters above.
+ * @param drawing An instance of the DrawingPatriarch class. This is now
+ * passed into the method where it was, previously, recovered
+ * from the sheet in order to allow multiple pictures be
+ * inserted. If the patriarch was not 'cached in this manner
+ * each time it was created any previously positioned images
+ * would be simply over-written.
+ * @param imageFile An instance of the URL class that encapsulates the name
+ * of and path to the image that is to be 'inserted into'
+ * the sheet.
+ * @param reqImageWidthMM A primitive double that contains the required
+ * width of the image in millimetres.
+ * @param reqImageHeightMM A primitive double that contains the required
+ * height of the image in millimetres.
+ * @param resizeBehaviour A primitive int whose value will determine how
+ * the code should react if the image is larger than
+ * the cell referenced by the colNumber and
+ * rowNumber parameters. Four constants are provided
+ * to determine what should happen;
+ * AddDimensionedImage.EXPAND_ROW
+ * AddDimensionedImage.EXPAND_COLUMN
+ * AddDimensionedImage.EXPAND_ROW_AND_COLUMN
+ * AddDimensionedImage.OVERLAY_ROW_AND_COLUMN
+ * @throws java.io.FileNotFoundException If the file containing the image
+ * cannot be located.
+ * @throws java.io.IOException If a problem occurs whilst reading the file
+ * of image data.
+ * @throws java.lang.IllegalArgumentException If an invalid value is passed
+ * to the resizeBehaviour
+ * parameter or if the extension
+ * of the image file indicates that
+ * it is of a type that cannot
+ * currently be added to the worksheet.
+ */
+ public void addImageToSheet(int colNumber, int rowNumber, Sheet sheet, Drawing> drawing,
+ URL imageFile, double reqImageWidthMM, double reqImageHeightMM,
+ int resizeBehaviour) throws IOException,
+ IllegalArgumentException {
+ ClientAnchor anchor;
+ ClientAnchorDetail rowClientAnchorDetail;
+ ClientAnchorDetail colClientAnchorDetail;
+ int imageType;
+
+ // Validate the resizeBehaviour parameter.
+ if((resizeBehaviour != AddDimensionedImage.EXPAND_COLUMN) &&
+ (resizeBehaviour != AddDimensionedImage.EXPAND_ROW) &&
+ (resizeBehaviour != AddDimensionedImage.EXPAND_ROW_AND_COLUMN) &&
+ (resizeBehaviour != AddDimensionedImage.OVERLAY_ROW_AND_COLUMN)) {
+ throw new IllegalArgumentException("Invalid value passed to the " +
+ "resizeBehaviour parameter of AddDimensionedImage.addImageToSheet()");
+ }
+
+ // Call methods to calculate how the image and sheet should be
+ // manipulated to accommodate the image; columns and then rows.
+ colClientAnchorDetail = this.fitImageToColumns(sheet, colNumber,
+ reqImageWidthMM, resizeBehaviour);
+ rowClientAnchorDetail = this.fitImageToRows(sheet, rowNumber,
+ reqImageHeightMM, resizeBehaviour);
+
+ // Having determined if and how to resize the rows, columns and/or the
+ // image, create the ClientAnchor object to position the image on
+ // the worksheet. Note how the two ClientAnchorDetail records are
+ // interrogated to recover the row/column co-ordinates and any insets.
+ // The first two parameters are not used currently but could be if the
+ // need arose to extend the functionality of this code by adding the
+ // ability to specify that a clear 'border' be placed around the image.
+ anchor = sheet.getWorkbook().getCreationHelper().createClientAnchor();
+
+ anchor.setDx1(0);
+ anchor.setDy1(0);
+ if (colClientAnchorDetail != null) {
+ anchor.setDx2(colClientAnchorDetail.getInset());
+ anchor.setCol1(colClientAnchorDetail.getFromIndex());
+ anchor.setCol2(colClientAnchorDetail.getToIndex());
+ }
+ if (rowClientAnchorDetail != null) {
+ anchor.setDy2(rowClientAnchorDetail.getInset());
+ anchor.setRow1(rowClientAnchorDetail.getFromIndex());
+ anchor.setRow2(rowClientAnchorDetail.getToIndex());
+ }
+
+ // For now, set the anchor type to do not move or resize the
+ // image as the size of the row/column is adjusted. This could easily
+ // become another parameter passed to the method. Please read the note
+ // above regarding the behaviour of image resizing.
+ anchor.setAnchorType(AnchorType.MOVE_AND_RESIZE);
+
+ // Now, add the picture to the workbook. Note that unlike the similar
+ // method in the HSSF Examples section, the image type is checked. First,
+ // the image files location is identified by interrogating the URL passed
+ // to the method, the images type is identified before it is added to the
+ // sheet.
+ String sURL = imageFile.toString().toLowerCase(Locale.ROOT);
+ if( sURL.endsWith(".png") ) {
+ imageType = Workbook.PICTURE_TYPE_PNG;
+ }
+ else if( sURL.endsWith(".jpg") || sURL.endsWith(".jpeg") ) {
+ imageType = Workbook.PICTURE_TYPE_JPEG;
+ }
+ else {
+ throw new IllegalArgumentException("Invalid Image file : " +
+ sURL);
+ }
+ int index = sheet.getWorkbook().addPicture(
+ IOUtils.toByteArray(imageFile.openStream()), imageType);
+ drawing.createPicture(anchor, index);
+ }
+
+ /**
+ * Determines whether the sheets columns should be re-sized to accommodate
+ * the image, adjusts the columns width if necessary and creates then
+ * returns a ClientAnchorDetail object that facilitates construction of
+ * an ClientAnchor that will fix the image on the sheet and establish
+ * it's size.
+ *
+ * @param sheet A reference to the sheet that will 'contain' the image.
+ * @param colNumber A primitive int that contains the index number of a
+ * column on the sheet.
+ * @param reqImageWidthMM A primitive double that contains the required
+ * width of the image in millimetres
+ * @param resizeBehaviour A primitive int whose value will indicate how the
+ * width of the column should be adjusted if the
+ * required width of the image is greater than the
+ * width of the column.
+ * @return An instance of the ClientAnchorDetail class that will contain
+ * the index number of the column containing the cell whose top
+ * left hand corner also defines the top left hand corner of the
+ * image, the index number column containing the cell whose top
+ * left hand corner also defines the bottom right hand corner of
+ * the image and an inset that determines how far the right hand
+ * edge of the image can protrude into the next column - expressed
+ * as a specific number of coordinate positions.
+ */
+ private ClientAnchorDetail fitImageToColumns(Sheet sheet, int colNumber,
+ double reqImageWidthMM, int resizeBehaviour) {
+
+ double colWidthMM;
+ double colCoordinatesPerMM;
+ int pictureWidthCoordinates;
+ ClientAnchorDetail colClientAnchorDetail = null;
+
+ // Get the colum's width in millimetres
+ colWidthMM = ConvertImageUnits.widthUnits2Millimetres(
+ (short)sheet.getColumnWidth(colNumber));
+
+ // Check that the column's width will accommodate the image at the
+ // required dimension. If the width of the column is LESS than the
+ // required width of the image, decide how the application should
+ // respond - resize the column or overlay the image across one or more
+ // columns.
+ if(colWidthMM < reqImageWidthMM) {
+
+ // Should the column's width simply be expanded?
+ if((resizeBehaviour == AddDimensionedImage.EXPAND_COLUMN) ||
+ (resizeBehaviour == AddDimensionedImage.EXPAND_ROW_AND_COLUMN)) {
+ // Set the width of the column by converting the required image
+ // width from millimetres into Excel's column width units.
+ sheet.setColumnWidth(colNumber,
+ ConvertImageUnits.millimetres2WidthUnits(reqImageWidthMM));
+ // To make the image occupy the full width of the column, convert
+ // the required width of the image into co-ordinates. This value
+ // will become the inset for the ClientAnchorDetail class that
+ // is then instantiated.
+ if(sheet instanceof HSSFSheet) {
+ colWidthMM = reqImageWidthMM;
+ colCoordinatesPerMM = colWidthMM == 0 ? 0
+ : ConvertImageUnits.TOTAL_COLUMN_COORDINATE_POSITIONS / colWidthMM;
+ pictureWidthCoordinates = (int)(reqImageWidthMM * colCoordinatesPerMM);
+
+ }
+ else {
+ pictureWidthCoordinates = (int)reqImageWidthMM * AddDimensionedImage.EMU_PER_MM;
+ }
+ colClientAnchorDetail = new ClientAnchorDetail(colNumber,
+ colNumber, pictureWidthCoordinates);
+ }
+ // If the user has chosen to overlay both rows and columns or just
+ // to expand ONLY the size of the rows, then calculate how to lay
+ // the image out across one or more columns.
+ else if ((resizeBehaviour == AddDimensionedImage.OVERLAY_ROW_AND_COLUMN) ||
+ (resizeBehaviour == AddDimensionedImage.EXPAND_ROW)) {
+ colClientAnchorDetail = this.calculateColumnLocation(sheet,
+ colNumber, reqImageWidthMM);
+ }
+ }
+ // If the column is wider than the image.
+ else {
+ if(sheet instanceof HSSFSheet) {
+ // Mow many co-ordinate positions are there per millimetre?
+ colCoordinatesPerMM = ConvertImageUnits.TOTAL_COLUMN_COORDINATE_POSITIONS /
+ colWidthMM;
+ // Given the width of the image, what should be it's co-ordinate?
+ pictureWidthCoordinates = (int)(reqImageWidthMM * colCoordinatesPerMM);
+ }
+ else {
+ pictureWidthCoordinates = (int)reqImageWidthMM *
+ AddDimensionedImage.EMU_PER_MM;
+ }
+ colClientAnchorDetail = new ClientAnchorDetail(colNumber,
+ colNumber, pictureWidthCoordinates);
+ }
+ return(colClientAnchorDetail);
+ }
+
+ /**
+ * Determines whether the sheets row should be re-sized to accommodate
+ * the image, adjusts the rows height if necessary and creates then
+ * returns a ClientAnchorDetail object that facilitates construction of
+ * a ClientAnchor that will fix the image on the sheet and establish
+ * it's size.
+ *
+ * @param sheet A reference to the sheet that will 'contain' the image.
+ * @param rowNumber A primitive int that contains the index number of a
+ * row on the sheet.
+ * @param reqImageHeightMM A primitive double that contains the required
+ * height of the image in millimetres
+ * @param resizeBehaviour A primitive int whose value will indicate how the
+ * height of the row should be adjusted if the
+ * required height of the image is greater than the
+ * height of the row.
+ * @return An instance of the ClientAnchorDetail class that will contain
+ * the index number of the row containing the cell whose top
+ * left hand corner also defines the top left hand corner of the
+ * image, the index number of the row containing the cell whose
+ * top left hand corner also defines the bottom right hand
+ * corner of the image and an inset that determines how far the
+ * bottom edge of the image can protrude into the next (lower)
+ * row - expressed as a specific number of coordinate positions.
+ */
+ private ClientAnchorDetail fitImageToRows(Sheet sheet, int rowNumber,
+ double reqImageHeightMM, int resizeBehaviour) {
+ Row row;
+ double rowHeightMM;
+ double rowCoordinatesPerMM;
+ int pictureHeightCoordinates;
+ ClientAnchorDetail rowClientAnchorDetail = null;
+
+ // Get the row and it's height
+ row = sheet.getRow(rowNumber);
+ if(row == null) {
+ // Create row if it does not exist.
+ row = sheet.createRow(rowNumber);
+ }
+
+ // Get the row's height in millimetres
+ rowHeightMM = row.getHeightInPoints() / ConvertImageUnits.POINTS_PER_MILLIMETRE;
+
+ // Check that the row's height will accommodate the image at the required
+ // dimensions. If the height of the row is LESS than the required height
+ // of the image, decide how the application should respond - resize the
+ // row or overlay the image across a series of rows.
+ if(rowHeightMM < reqImageHeightMM) {
+ if((resizeBehaviour == AddDimensionedImage.EXPAND_ROW) ||
+ (resizeBehaviour == AddDimensionedImage.EXPAND_ROW_AND_COLUMN)) {
+ row.setHeightInPoints((float)(reqImageHeightMM *
+ ConvertImageUnits.POINTS_PER_MILLIMETRE));
+ if(sheet instanceof HSSFSheet) {
+ rowHeightMM = reqImageHeightMM;
+ rowCoordinatesPerMM = rowHeightMM == 0 ? 0
+ : ConvertImageUnits.TOTAL_ROW_COORDINATE_POSITIONS / rowHeightMM;
+ pictureHeightCoordinates = (int)(reqImageHeightMM *
+ rowCoordinatesPerMM);
+ }
+ else {
+ pictureHeightCoordinates = (int)(reqImageHeightMM *
+ AddDimensionedImage.EMU_PER_MM);
+ }
+ rowClientAnchorDetail = new ClientAnchorDetail(rowNumber,
+ rowNumber, pictureHeightCoordinates);
+ }
+ // If the user has chosen to overlay both rows and columns or just
+ // to expand ONLY the size of the columns, then calculate how to lay
+ // the image out ver one or more rows.
+ else if((resizeBehaviour == AddDimensionedImage.OVERLAY_ROW_AND_COLUMN) ||
+ (resizeBehaviour == AddDimensionedImage.EXPAND_COLUMN)) {
+ rowClientAnchorDetail = this.calculateRowLocation(sheet,
+ rowNumber, reqImageHeightMM);
+ }
+ }
+ // Else, if the image is smaller than the space available
+ else {
+ if(sheet instanceof HSSFSheet) {
+ rowCoordinatesPerMM = ConvertImageUnits.TOTAL_ROW_COORDINATE_POSITIONS /
+ rowHeightMM;
+ pictureHeightCoordinates = (int)(reqImageHeightMM * rowCoordinatesPerMM);
+ }
+ else {
+ pictureHeightCoordinates = (int)(reqImageHeightMM *
+ AddDimensionedImage.EMU_PER_MM);
+ }
+ rowClientAnchorDetail = new ClientAnchorDetail(rowNumber,
+ rowNumber, pictureHeightCoordinates);
+ }
+ return(rowClientAnchorDetail);
+ }
+
+ /**
+ * If the image is to overlie more than one column, calculations need to be
+ * performed to determine how many columns and whether the image will
+ * overlie just a part of one column in order to be presented at the
+ * required size.
+ *
+ * @param sheet The sheet that will 'contain' the image.
+ * @param startingColumn A primitive int whose value is the index of the
+ * column that contains the cell whose top left hand
+ * corner should be aligned with the top left hand
+ * corner of the image.
+ * @param reqImageWidthMM A primitive double whose value will indicate the
+ * required width of the image in millimetres.
+ * @return An instance of the ClientAnchorDetail class that will contain
+ * the index number of the column containing the cell whose top
+ * left hand corner also defines the top left hand corner of the
+ * image, the index number column containing the cell whose top
+ * left hand corner also defines the bottom right hand corner of
+ * the image and an inset that determines how far the right hand
+ * edge of the image can protrude into the next column - expressed
+ * as a specific number of coordinate positions.
+ */
+ private ClientAnchorDetail calculateColumnLocation(Sheet sheet,
+ int startingColumn,
+ double reqImageWidthMM) {
+ ClientAnchorDetail anchorDetail;
+ double totalWidthMM = 0.0D;
+ double colWidthMM = 0.0D;
+ double overlapMM;
+ double coordinatePositionsPerMM;
+ int toColumn = startingColumn;
+ int inset;
+
+ // Calculate how many columns the image will have to
+ // span in order to be presented at the required size.
+ while(totalWidthMM < reqImageWidthMM) {
+ colWidthMM = ConvertImageUnits.widthUnits2Millimetres(
+ (short)(sheet.getColumnWidth(toColumn)));
+ // Note use of the cell border width constant. Testing with an image
+ // declared to fit exactly into one column demonstrated that it's
+ // width was greater than the width of the column the POI returned.
+ // Further, this difference was a constant value that I am assuming
+ // related to the cell's borders. Either way, that difference needs
+ // to be allowed for in this calculation.
+ totalWidthMM += (colWidthMM + ConvertImageUnits.CELL_BORDER_WIDTH_MILLIMETRES);
+ toColumn++;
+ }
+ // De-crement by one the last column value.
+ toColumn--;
+ // Highly unlikely that this will be true but, if the width of a series
+ // of columns is exactly equal to the required width of the image, then
+ // simply build a ClientAnchorDetail object with an inset equal to the
+ // total number of co-ordinate positions available in a column, a
+ // from column co-ordinate (top left hand corner) equal to the value
+ // of the startingColumn parameter and a to column co-ordinate equal
+ // to the toColumn variable.
+ //
+ // Convert both values to ints to perform the test.
+ if((int)totalWidthMM == (int)reqImageWidthMM) {
+ // A problem could occur if the image is sized to fit into one or
+ // more columns. If that occurs, the value in the toColumn variable
+ // will be in error. To overcome this, there are two options, to
+ // ibcrement the toColumn variable's value by one or to pass the
+ // total number of co-ordinate positions to the third paramater
+ // of the ClientAnchorDetail constructor. For no sepcific reason,
+ // the latter option is used below.
+ if(sheet instanceof HSSFSheet) {
+ anchorDetail = new ClientAnchorDetail(startingColumn,
+ toColumn, ConvertImageUnits.TOTAL_COLUMN_COORDINATE_POSITIONS);
+ }
+ else {
+ anchorDetail = new ClientAnchorDetail(startingColumn,
+ toColumn, (int)reqImageWidthMM * AddDimensionedImage.EMU_PER_MM);
+ }
+ }
+ // In this case, the image will overlap part of another column and it is
+ // necessary to calculate just how much - this will become the inset
+ // for the ClientAnchorDetail object.
+ else {
+ // Firstly, claculate how much of the image should overlap into
+ // the next column.
+ overlapMM = reqImageWidthMM - (totalWidthMM - colWidthMM);
+
+ // When the required size is very close indded to the column size,
+ // the calcaulation above can produce a negative value. To prevent
+ // problems occuring in later caculations, this is simply removed
+ // be setting the overlapMM value to zero.
+ if(overlapMM < 0) {
+ overlapMM = 0.0D;
+ }
+
+ if(sheet instanceof HSSFSheet) {
+ // Next, from the columns width, calculate how many co-ordinate
+ // positons there are per millimetre
+ coordinatePositionsPerMM = (colWidthMM == 0) ? 0
+ : ConvertImageUnits.TOTAL_COLUMN_COORDINATE_POSITIONS / colWidthMM;
+ // From this figure, determine how many co-ordinat positions to
+ // inset the left hand or bottom edge of the image.
+ inset = (int)(coordinatePositionsPerMM * overlapMM);
+ }
+ else {
+ inset = (int)overlapMM * AddDimensionedImage.EMU_PER_MM;
+ }
+
+ // Now create the ClientAnchorDetail object, setting the from and to
+ // columns and the inset.
+ anchorDetail = new ClientAnchorDetail(startingColumn, toColumn, inset);
+ }
+ return(anchorDetail);
+ }
+
+ /**
+ * If the image is to overlie more than one rows, calculations need to be
+ * performed to determine how many rows and whether the image will
+ * overlie just a part of one row in order to be presented at the
+ * required size.
+ *
+ * @param sheet The sheet that will 'contain' the image.
+ * @param startingRow A primitive int whose value is the index of the row
+ * that contains the cell whose top left hand corner
+ * should be aligned with the top left hand corner of
+ * the image.
+ * @param reqImageHeightMM A primitive double whose value will indicate the
+ * required height of the image in millimetres.
+ * @return An instance of the ClientAnchorDetail class that will contain
+ * the index number of the row containing the cell whose top
+ * left hand corner also defines the top left hand corner of the
+ * image, the index number of the row containing the cell whose top
+ * left hand corner also defines the bottom right hand corner of
+ * the image and an inset that determines how far the bottom edge
+ * can protrude into the next (lower) row - expressed as a specific
+ * number of co-ordinate positions.
+ */
+ private ClientAnchorDetail calculateRowLocation(Sheet sheet,
+ int startingRow, double reqImageHeightMM) {
+ ClientAnchorDetail clientAnchorDetail;
+ Row row;
+ double rowHeightMM = 0.0D;
+ double totalRowHeightMM = 0.0D;
+ double overlapMM;
+ double rowCoordinatesPerMM;
+ int toRow = startingRow;
+ int inset;
+
+ // Step through the rows in the sheet and accumulate a total of their
+ // heights.
+ while(totalRowHeightMM < reqImageHeightMM) {
+ row = sheet.getRow(toRow);
+ // Note, if the row does not already exist on the sheet then create
+ // it here.
+ if(row == null) {
+ row = sheet.createRow(toRow);
+ }
+ // Get the row's height in millimetres and add to the running total.
+ rowHeightMM = row.getHeightInPoints() /
+ ConvertImageUnits.POINTS_PER_MILLIMETRE;
+ totalRowHeightMM += rowHeightMM;
+ toRow++;
+ }
+ // Owing to the way the loop above works, the rowNumber will have been
+ // incremented one row too far. Undo that here.
+ toRow--;
+ // Check to see whether the image should occupy an exact number of
+ // rows. If so, build the ClientAnchorDetail record to point
+ // to those rows and with an inset of the total number of co-ordinate
+ // position in the row.
+ //
+ // To overcome problems that can occur with comparing double values for
+ // equality, cast both to int(s) to truncate the value; VERY crude and
+ // I do not really like it!!
+ if((int)totalRowHeightMM == (int)reqImageHeightMM) {
+ if(sheet instanceof HSSFSheet) {
+ clientAnchorDetail = new ClientAnchorDetail(startingRow, toRow,
+ ConvertImageUnits.TOTAL_ROW_COORDINATE_POSITIONS);
+ }
+ else {
+ clientAnchorDetail = new ClientAnchorDetail(startingRow, toRow,
+ (int)reqImageHeightMM * AddDimensionedImage.EMU_PER_MM);
+ }
+ }
+ else {
+ // Calculate how far the image will project into the next row. Note
+ // that the height of the last row assessed is subtracted from the
+ // total height of all rows assessed so far.
+ overlapMM = reqImageHeightMM - (totalRowHeightMM - rowHeightMM);
+
+ // To prevent an exception being thrown when the required width of
+ // the image is very close indeed to the column size.
+ if(overlapMM < 0) {
+ overlapMM = 0.0D;
+ }
+
+ if(sheet instanceof HSSFSheet) {
+ rowCoordinatesPerMM = (rowHeightMM == 0) ? 0
+ : ConvertImageUnits.TOTAL_ROW_COORDINATE_POSITIONS / rowHeightMM;
+ inset = (int)(overlapMM * rowCoordinatesPerMM);
+ }
+ else {
+ inset = (int)overlapMM * AddDimensionedImage.EMU_PER_MM;
+ }
+ clientAnchorDetail = new ClientAnchorDetail(startingRow,
+ toRow, inset);
+ }
+ return(clientAnchorDetail);
+ }
+
+ /**
+ * The main entry point to the program. It contains code that demonstrates
+ * one way to use the program.
+ *
+ * Note, the code is not restricted to use on new workbooks only. If an
+ * image is to be inserted into an existing workbook. just open that
+ * workbook, gat a reference to a sheet and pass that;
+ *
+ * AddDimensionedImage addImage = new AddDimensionedImage();
+ *
+ * File file = new File("....... Existing Workbook .......");
+ * FileInputStream fis = new FileInputStream(file);
+ * Workbook workbook = new HSSFWorkbook(fis);
+ * HSSFSheet sheet = workbook.getSheetAt(0);
+ * addImage.addImageToSheet("C3", sheet, "image.jpg", 30, 20,
+ * AddDimensionedImage.EXPAND.ROW);
+ *
+ * @param args the command line arguments
+ */
+ public static void main(String[] args) throws IOException {
+ if(args.length < 2){
+ System.err.println("Usage: AddDimensionedImage imageFile outputFile");
+ return;
+ }
+
+ final String imageFile = args[0];
+ final String outputFile = args[1];
+
+ try (final Workbook workbook = new HSSFWorkbook();
+ final FileOutputStream fos = new FileOutputStream(outputFile)) { // OR XSSFWorkbook
+ Sheet sheet = workbook.createSheet("Picture Test");
+ new AddDimensionedImage().addImageToSheet("B5", sheet, sheet.createDrawingPatriarch(),
+ new File(imageFile).toURI().toURL(), 100, 40,
+ AddDimensionedImage.EXPAND_ROW_AND_COLUMN);
+ workbook.write(fos);
+ }
+ }
+
+ /**
+ * The HSSFClientAnchor class accepts eight arguments. In order, these are;
+ *
+ * * How far the left hand edge of the image is inset from the left hand
+ * edge of the cell
+ * * How far the top edge of the image is inset from the top of the cell
+ * * How far the right hand edge of the image is inset from the left
+ * hand edge of the cell
+ * * How far the bottom edge of the image is inset from the top of the
+ * cell.
+ * * Together, arguments five and six determine the column and row
+ * coordinates of the cell whose top left hand corner will be aligned
+ * with the images top left hand corner.
+ * * Together, arguments seven and eight determine the column and row
+ * coordinates of the cell whose top left hand corner will be aligned
+ * with the images bottom right hand corner.
+ *
+ * An instance of the ClientAnchorDetail class provides three of the eight
+ * parameters, one of the coordinates for the images top left hand corner,
+ * one of the coordinates for the images bottom right hand corner and
+ * either how far the image should be inset from the top or the left hand
+ * edge of the cell.
+ *
+ * @author Mark Beardsley [msb at apache.org]
+ * @version 1.00 5th August 2009.
+ */
+ public class ClientAnchorDetail {
+
+ private int fromIndex;
+ private int toIndex;
+ private int inset;
+
+ /**
+ * Create a new instance of the ClientAnchorDetail class using the
+ * following parameters.
+ *
+ * @param fromIndex A primitive int that contains one of the
+ * coordinates (row or column index) for the top left
+ * hand corner of the image.
+ * @param toIndex A primitive int that contains one of the
+ * coordinates (row or column index) for the bottom
+ * right hand corner of the image.
+ * @param inset A primitive int that contains a value which indicates
+ * how far the image should be inset from the top or the
+ * left hand edge of a cell.
+ */
+ public ClientAnchorDetail(int fromIndex, int toIndex, int inset) {
+ this.fromIndex = fromIndex;
+ this.toIndex = toIndex;
+ this.inset = inset;
+ }
+
+ /**
+ * Get one of the number of the column or row that contains the cell
+ * whose top left hand corner will be aligned with the top left hand
+ * corner of the image.
+ *
+ * @return The value - row or column index - for one of the coordinates
+ * of the top left hand corner of the image.
+ */
+ public int getFromIndex() {
+ return(this.fromIndex);
+ }
+
+ /**
+ * Get one of the number of the column or row that contains the cell
+ * whose top left hand corner will be aligned with the bottom right hand
+ * corner of the image.
+ *
+ * @return The value - row or column index - for one of the coordinates
+ * of the bottom right hand corner of the image.
+ */
+ public int getToIndex() {
+ return(this.toIndex);
+ }
+
+ /**
+ * Get the images offset from the edge of a cell.
+ *
+ * @return How far either the right hand or bottom edge of the image is
+ * inset from the left hand or top edge of a cell.
+ */
+ public int getInset() {
+ return(this.inset);
+ }
+ }
+
+ /**
+ * Utility methods used to convert Excels character based column and row
+ * size measurements into pixels and/or millimetres. The class also contains
+ * various constants that are required in other calculations.
+ *
+ * @author xio[darjino@hotmail.com]
+ * @version 1.01 30th July 2009.
+ * Added by Mark Beardsley [msb at apache.org].
+ * Additional constants.
+ * widthUnits2Millimetres() and millimetres2Units() methods.
+ */
+ public static class ConvertImageUnits {
+
+ // Each cell conatins a fixed number of co-ordinate points; this number
+ // does not vary with row height or column width or with font. These two
+ // constants are defined below.
+ public static final int TOTAL_COLUMN_COORDINATE_POSITIONS = 1023;
+ public static final int TOTAL_ROW_COORDINATE_POSITIONS = 255;
+ // The resoultion of an image can be expressed as a specific number
+ // of pixels per inch. Displays and printers differ but 96 pixels per
+ // inch is an acceptable standard to beging with.
+ public static final int PIXELS_PER_INCH = 96;
+ // Cnstants that defines how many pixels and points there are in a
+ // millimetre. These values are required for the conversion algorithm.
+ public static final double PIXELS_PER_MILLIMETRES = 3.78;
+ public static final double POINTS_PER_MILLIMETRE = 2.83;
+ // The column width returned by HSSF and the width of a picture when
+ // positioned to exactly cover one cell are different by almost exactly
+ // 2mm - give or take rounding errors. This constant allows that
+ // additional amount to be accounted for when calculating how many
+ // celles the image ought to overlie.
+ public static final double CELL_BORDER_WIDTH_MILLIMETRES = 2.0D;
+ public static final short EXCEL_COLUMN_WIDTH_FACTOR = 256;
+ public static final int UNIT_OFFSET_LENGTH = 7;
+ private static final int[] UNIT_OFFSET_MAP = { 0, 36, 73, 109, 146, 182, 219 };
+
+ /**
+ * pixel units to excel width units(units of 1/256th of a character width)
+ */
+ public static short pixel2WidthUnits(int pxs) {
+ short widthUnits = (short) (EXCEL_COLUMN_WIDTH_FACTOR *
+ (pxs / UNIT_OFFSET_LENGTH));
+ widthUnits += UNIT_OFFSET_MAP[(pxs % UNIT_OFFSET_LENGTH)];
+ return widthUnits;
+ }
+
+ /**
+ * excel width units(units of 1/256th of a character width) to pixel
+ * units.
+ */
+ public static int widthUnits2Pixel(short widthUnits) {
+ int pixels = (widthUnits / EXCEL_COLUMN_WIDTH_FACTOR)
+ * UNIT_OFFSET_LENGTH;
+ int offsetWidthUnits = widthUnits % EXCEL_COLUMN_WIDTH_FACTOR;
+ pixels += Math.round(offsetWidthUnits /
+ ((float) EXCEL_COLUMN_WIDTH_FACTOR / UNIT_OFFSET_LENGTH));
+ return pixels;
+ }
+
+ /**
+ * Convert Excels width units into millimetres.
+ *
+ * @param widthUnits The width of the column or the height of the
+ * row in Excels units.
+ * @return A primitive double that contains the columns width or rows
+ * height in millimetres.
+ */
+ public static double widthUnits2Millimetres(short widthUnits) {
+ return(ConvertImageUnits.widthUnits2Pixel(widthUnits) /
+ ConvertImageUnits.PIXELS_PER_MILLIMETRES);
+ }
+
+ /**
+ * Convert into millimetres Excels width units..
+ *
+ * @param millimetres A primitive double that contains the columns
+ * width or rows height in millimetres.
+ * @return A primitive int that contains the columns width or rows
+ * height in Excels units.
+ */
+ public static int millimetres2WidthUnits(double millimetres) {
+ return(ConvertImageUnits.pixel2WidthUnits((int)(millimetres *
+ ConvertImageUnits.PIXELS_PER_MILLIMETRES)));
+ }
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/ss/AligningCells.java b/src/examples/src/org/apache/poi/examples/ss/AligningCells.java
new file mode 100644
index 0000000000..af86efc724
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/ss/AligningCells.java
@@ -0,0 +1,81 @@
+/* ====================================================================
+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.examples.ss;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellStyle;
+import org.apache.poi.ss.usermodel.CreationHelper;
+import org.apache.poi.ss.usermodel.HorizontalAlignment;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.VerticalAlignment;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+/**
+ * Shows how various alignment options work.
+ */
+public class AligningCells {
+
+ public static void main(String[] args) throws IOException {
+ try (Workbook wb = new XSSFWorkbook()) { //or new HSSFWorkbook();
+
+ Sheet sheet = wb.createSheet();
+ Row row = sheet.createRow(2);
+ row.setHeightInPoints(30);
+ for (int i = 0; i < 8; i++) {
+ //column width is set in units of 1/256th of a character width
+ sheet.setColumnWidth(i, 256 * 15);
+ }
+
+ createCell(wb, row, 0, HorizontalAlignment.CENTER, VerticalAlignment.BOTTOM);
+ createCell(wb, row, 1, HorizontalAlignment.CENTER_SELECTION, VerticalAlignment.BOTTOM);
+ createCell(wb, row, 2, HorizontalAlignment.FILL, VerticalAlignment.CENTER);
+ createCell(wb, row, 3, HorizontalAlignment.GENERAL, VerticalAlignment.CENTER);
+ createCell(wb, row, 4, HorizontalAlignment.JUSTIFY, VerticalAlignment.JUSTIFY);
+ createCell(wb, row, 5, HorizontalAlignment.LEFT, VerticalAlignment.TOP);
+ createCell(wb, row, 6, HorizontalAlignment.RIGHT, VerticalAlignment.TOP);
+
+ // Write the output to a file
+ try (OutputStream fileOut = new FileOutputStream("ss-example-align.xlsx")) {
+ wb.write(fileOut);
+ }
+ }
+ }
+
+ /**
+ * Creates a cell and aligns it a certain way.
+ *
+ * @param wb the workbook
+ * @param row the row to create the cell in
+ * @param column the column number to create the cell in
+ * @param halign the horizontal alignment for the cell.
+ */
+ private static void createCell(Workbook wb, Row row, int column, HorizontalAlignment halign, VerticalAlignment valign) {
+ CreationHelper ch = wb.getCreationHelper();
+ Cell cell = row.createCell(column);
+ cell.setCellValue(ch.createRichTextString("Align It"));
+ CellStyle cellStyle = wb.createCellStyle();
+ cellStyle.setAlignment(halign);
+ cellStyle.setVerticalAlignment(valign);
+ cell.setCellStyle(cellStyle);
+ }
+}
\ No newline at end of file
diff --git a/src/examples/src/org/apache/poi/examples/ss/BusinessPlan.java b/src/examples/src/org/apache/poi/examples/ss/BusinessPlan.java
new file mode 100644
index 0000000000..59ccc6f337
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/ss/BusinessPlan.java
@@ -0,0 +1,344 @@
+/* ====================================================================
+ 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.examples.ss;
+
+import java.io.FileOutputStream;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.BorderStyle;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellStyle;
+import org.apache.poi.ss.usermodel.DataFormat;
+import org.apache.poi.ss.usermodel.FillPatternType;
+import org.apache.poi.ss.usermodel.Font;
+import org.apache.poi.ss.usermodel.HorizontalAlignment;
+import org.apache.poi.ss.usermodel.IndexedColors;
+import org.apache.poi.ss.usermodel.PrintSetup;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+/**
+ * A business plan demo
+ * Usage:
+ * BusinessPlan -xls|xlsx
+ *
+ * @author Yegor Kozlov
+ */
+@SuppressWarnings({"java:S106","java:S4823","java:S1192"})
+public final class BusinessPlan {
+
+ private static final String[] titles = {
+ "ID", "Project Name", "Owner", "Days", "Start", "End"};
+
+ //sample data to fill the sheet.
+ private static final String[][] data = {
+ {"1.0", "Marketing Research Tactical Plan", "J. Dow", "70", "9-Jul", null,
+ "x", "x", "x", "x", "x", "x", "x", "x", "x", "x", "x"},
+ null,
+ {"1.1", "Scope Definition Phase", "J. Dow", "10", "9-Jul", null,
+ "x", "x", null, null, null, null, null, null, null, null, null},
+ {"1.1.1", "Define research objectives", "J. Dow", "3", "9-Jul", null,
+ "x", null, null, null, null, null, null, null, null, null, null},
+ {"1.1.2", "Define research requirements", "S. Jones", "7", "10-Jul", null,
+ "x", "x", null, null, null, null, null, null, null, null, null},
+ {"1.1.3", "Determine in-house resource or hire vendor", "J. Dow", "2", "15-Jul", null,
+ "x", "x", null, null, null, null, null, null, null, null, null},
+ null,
+ {"1.2", "Vendor Selection Phase", "J. Dow", "19", "19-Jul", null,
+ null, "x", "x", "x", "x", null, null, null, null, null, null},
+ {"1.2.1", "Define vendor selection criteria", "J. Dow", "3", "19-Jul", null,
+ null, "x", null, null, null, null, null, null, null, null, null},
+ {"1.2.2", "Develop vendor selection questionnaire", "S. Jones, T. Wates", "2", "22-Jul", null,
+ null, "x", "x", null, null, null, null, null, null, null, null},
+ {"1.2.3", "Develop Statement of Work", "S. Jones", "4", "26-Jul", null,
+ null, null, "x", "x", null, null, null, null, null, null, null},
+ {"1.2.4", "Evaluate proposal", "J. Dow, S. Jones", "4", "2-Aug", null,
+ null, null, null, "x", "x", null, null, null, null, null, null},
+ {"1.2.5", "Select vendor", "J. Dow", "1", "6-Aug", null,
+ null, null, null, null, "x", null, null, null, null, null, null},
+ null,
+ {"1.3", "Research Phase", "G. Lee", "47", "9-Aug", null,
+ null, null, null, null, "x", "x", "x", "x", "x", "x", "x"},
+ {"1.3.1", "Develop market research information needs questionnaire", "G. Lee", "2", "9-Aug", null,
+ null, null, null, null, "x", null, null, null, null, null, null},
+ {"1.3.2", "Interview marketing group for market research needs", "G. Lee", "2", "11-Aug", null,
+ null, null, null, null, "x", "x", null, null, null, null, null},
+ {"1.3.3", "Document information needs", "G. Lee, S. Jones", "1", "13-Aug", null,
+ null, null, null, null, null, "x", null, null, null, null, null},
+ };
+
+ private BusinessPlan() {}
+
+ public static void main(String[] args) throws Exception {
+ Workbook wb;
+
+ if(args.length > 0 && args[0].equals("-xls")) wb = new HSSFWorkbook();
+ else wb = new XSSFWorkbook();
+
+ final SimpleDateFormat fmt = new SimpleDateFormat("dd-MMM");
+
+ Map styles = createStyles(wb);
+
+ Sheet sheet = wb.createSheet("Business Plan");
+
+ //turn off gridlines
+ sheet.setDisplayGridlines(false);
+ sheet.setPrintGridlines(false);
+ sheet.setFitToPage(true);
+ sheet.setHorizontallyCenter(true);
+ PrintSetup printSetup = sheet.getPrintSetup();
+ printSetup.setLandscape(true);
+
+ //the following three statements are required only for HSSF
+ sheet.setAutobreaks(true);
+ printSetup.setFitHeight((short)1);
+ printSetup.setFitWidth((short)1);
+
+ //the header row: centered text in 48pt font
+ Row headerRow = sheet.createRow(0);
+ headerRow.setHeightInPoints(12.75f);
+ for (int i = 0; i < titles.length; i++) {
+ Cell cell = headerRow.createCell(i);
+ cell.setCellValue(titles[i]);
+ cell.setCellStyle(styles.get("header"));
+ }
+ //columns for 11 weeks starting from 9-Jul
+ Calendar calendar = Calendar.getInstance();
+ int year = calendar.get(Calendar.YEAR);
+
+ calendar.setTime(fmt.parse("9-Jul"));
+ calendar.set(Calendar.YEAR, year);
+ for (int i = 0; i < 11; i++) {
+ Cell cell = headerRow.createCell(titles.length + i);
+ cell.setCellValue(calendar);
+ cell.setCellStyle(styles.get("header_date"));
+ calendar.roll(Calendar.WEEK_OF_YEAR, true);
+ }
+ //freeze the first row
+ sheet.createFreezePane(0, 1);
+
+ Row row;
+ Cell cell;
+ int rownum = 1;
+ for (int i = 0; i < data.length; i++, rownum++) {
+ row = sheet.createRow(rownum);
+ if(data[i] == null) continue;
+
+ for (int j = 0; j < data[i].length; j++) {
+ cell = row.createCell(j);
+ String styleName;
+ boolean isHeader = i == 0 || data[i-1] == null;
+ switch(j){
+ case 0:
+ if(isHeader) {
+ styleName = "cell_b";
+ cell.setCellValue(Double.parseDouble(data[i][j]));
+ } else {
+ styleName = "cell_normal";
+ cell.setCellValue(data[i][j]);
+ }
+ break;
+ case 1:
+ if(isHeader) {
+ styleName = i == 0 ? "cell_h" : "cell_bb";
+ } else {
+ styleName = "cell_indented";
+ }
+ cell.setCellValue(data[i][j]);
+ break;
+ case 2:
+ styleName = isHeader ? "cell_b" : "cell_normal";
+ cell.setCellValue(data[i][j]);
+ break;
+ case 3:
+ styleName = isHeader ? "cell_b_centered" : "cell_normal_centered";
+ cell.setCellValue(Integer.parseInt(data[i][j]));
+ break;
+ case 4: {
+ calendar.setTime(fmt.parse(data[i][j]));
+ calendar.set(Calendar.YEAR, year);
+ cell.setCellValue(calendar);
+ styleName = isHeader ? "cell_b_date" : "cell_normal_date";
+ break;
+ }
+ case 5: {
+ int r = rownum + 1;
+ String fmla = "IF(AND(D"+r+",E"+r+"),E"+r+"+D"+r+",\"\")";
+ cell.setCellFormula(fmla);
+ styleName = isHeader ? "cell_bg" : "cell_g";
+ break;
+ }
+ default:
+ styleName = data[i][j] != null ? "cell_blue" : "cell_normal";
+ }
+
+ cell.setCellStyle(styles.get(styleName));
+ }
+ }
+
+ //group rows for each phase, row numbers are 0-based
+ sheet.groupRow(4, 6);
+ sheet.groupRow(9, 13);
+ sheet.groupRow(16, 18);
+
+ //set column widths, the width is measured in units of 1/256th of a character width
+ sheet.setColumnWidth(0, 256*6);
+ sheet.setColumnWidth(1, 256*33);
+ sheet.setColumnWidth(2, 256*20);
+ sheet.setZoom(75); //75% scale
+
+
+ // Write the output to a file
+ String file = "businessplan.xls";
+ if(wb instanceof XSSFWorkbook) file += "x";
+ FileOutputStream out = new FileOutputStream(file);
+ wb.write(out);
+ out.close();
+
+ wb.close();
+ }
+
+ /**
+ * create a library of cell styles
+ */
+ private static Map createStyles(Workbook wb){
+ Map styles = new HashMap<>();
+ DataFormat df = wb.createDataFormat();
+
+ CellStyle style;
+ Font headerFont = wb.createFont();
+ headerFont.setBold(true);
+ style = createBorderedStyle(wb);
+ style.setAlignment(HorizontalAlignment.CENTER);
+ style.setFillForegroundColor(IndexedColors.LIGHT_CORNFLOWER_BLUE.getIndex());
+ style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+ style.setFont(headerFont);
+ styles.put("header", style);
+
+ style = createBorderedStyle(wb);
+ style.setAlignment(HorizontalAlignment.CENTER);
+ style.setFillForegroundColor(IndexedColors.LIGHT_CORNFLOWER_BLUE.getIndex());
+ style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+ style.setFont(headerFont);
+ style.setDataFormat(df.getFormat("d-mmm"));
+ styles.put("header_date", style);
+
+ Font font1 = wb.createFont();
+ font1.setBold(true);
+ style = createBorderedStyle(wb);
+ style.setAlignment(HorizontalAlignment.LEFT);
+ style.setFont(font1);
+ styles.put("cell_b", style);
+
+ style = createBorderedStyle(wb);
+ style.setAlignment(HorizontalAlignment.CENTER);
+ style.setFont(font1);
+ styles.put("cell_b_centered", style);
+
+ style = createBorderedStyle(wb);
+ style.setAlignment(HorizontalAlignment.RIGHT);
+ style.setFont(font1);
+ style.setDataFormat(df.getFormat("d-mmm"));
+ styles.put("cell_b_date", style);
+
+ style = createBorderedStyle(wb);
+ style.setAlignment(HorizontalAlignment.RIGHT);
+ style.setFont(font1);
+ style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
+ style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+ style.setDataFormat(df.getFormat("d-mmm"));
+ styles.put("cell_g", style);
+
+ Font font2 = wb.createFont();
+ font2.setColor(IndexedColors.BLUE.getIndex());
+ font2.setBold(true);
+ style = createBorderedStyle(wb);
+ style.setAlignment(HorizontalAlignment.LEFT);
+ style.setFont(font2);
+ styles.put("cell_bb", style);
+
+ style = createBorderedStyle(wb);
+ style.setAlignment(HorizontalAlignment.RIGHT);
+ style.setFont(font1);
+ style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
+ style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+ style.setDataFormat(df.getFormat("d-mmm"));
+ styles.put("cell_bg", style);
+
+ Font font3 = wb.createFont();
+ font3.setFontHeightInPoints((short)14);
+ font3.setColor(IndexedColors.DARK_BLUE.getIndex());
+ font3.setBold(true);
+ style = createBorderedStyle(wb);
+ style.setAlignment(HorizontalAlignment.LEFT);
+ style.setFont(font3);
+ style.setWrapText(true);
+ styles.put("cell_h", style);
+
+ style = createBorderedStyle(wb);
+ style.setAlignment(HorizontalAlignment.LEFT);
+ style.setWrapText(true);
+ styles.put("cell_normal", style);
+
+ style = createBorderedStyle(wb);
+ style.setAlignment(HorizontalAlignment.CENTER);
+ style.setWrapText(true);
+ styles.put("cell_normal_centered", style);
+
+ style = createBorderedStyle(wb);
+ style.setAlignment(HorizontalAlignment.RIGHT);
+ style.setWrapText(true);
+ style.setDataFormat(df.getFormat("d-mmm"));
+ styles.put("cell_normal_date", style);
+
+ style = createBorderedStyle(wb);
+ style.setAlignment(HorizontalAlignment.LEFT);
+ style.setIndention((short)1);
+ style.setWrapText(true);
+ styles.put("cell_indented", style);
+
+ style = createBorderedStyle(wb);
+ style.setFillForegroundColor(IndexedColors.BLUE.getIndex());
+ style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+ styles.put("cell_blue", style);
+
+ return styles;
+ }
+
+ private static CellStyle createBorderedStyle(Workbook wb){
+ BorderStyle thin = BorderStyle.THIN;
+ short black = IndexedColors.BLACK.getIndex();
+
+ CellStyle style = wb.createCellStyle();
+ style.setBorderRight(thin);
+ style.setRightBorderColor(black);
+ style.setBorderBottom(thin);
+ style.setBottomBorderColor(black);
+ style.setBorderLeft(thin);
+ style.setLeftBorderColor(black);
+ style.setBorderTop(thin);
+ style.setTopBorderColor(black);
+ return style;
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/ss/CalendarDemo.java b/src/examples/src/org/apache/poi/examples/ss/CalendarDemo.java
new file mode 100644
index 0000000000..f11c1e0227
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/ss/CalendarDemo.java
@@ -0,0 +1,258 @@
+/* ====================================================================
+ 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.examples.ss;
+
+import java.io.FileOutputStream;
+import java.util.Calendar;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.BorderStyle;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellStyle;
+import org.apache.poi.ss.usermodel.FillPatternType;
+import org.apache.poi.ss.usermodel.Font;
+import org.apache.poi.ss.usermodel.HorizontalAlignment;
+import org.apache.poi.ss.usermodel.IndexedColors;
+import org.apache.poi.ss.usermodel.PrintSetup;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.VerticalAlignment;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+/**
+ * A monthly calendar created using Apache POI. Each month is on a separate sheet.
+ *
+ * Usage:
+ * CalendarDemo -xls|xlsx
+ *
+ *
+ * @author Yegor Kozlov
+ */
+@SuppressWarnings({"java:S106","java:S4823","java:S1192"})
+public final class CalendarDemo {
+
+ private static final String[] days = {
+ "Sunday", "Monday", "Tuesday",
+ "Wednesday", "Thursday", "Friday", "Saturday"};
+
+ private static final String[] months = {
+ "January", "February", "March","April", "May", "June","July", "August",
+ "September","October", "November", "December"};
+
+ private CalendarDemo() {}
+
+ public static void main(String[] args) throws Exception {
+
+ Calendar calendar = Calendar.getInstance();
+ boolean xlsx = true;
+ for (String arg : args) {
+ if (arg.charAt(0) == '-') {
+ xlsx = arg.equals("-xlsx");
+ } else {
+ calendar.set(Calendar.YEAR, Integer.parseInt(arg));
+ }
+ }
+ int year = calendar.get(Calendar.YEAR);
+
+ try (Workbook wb = xlsx ? new XSSFWorkbook() : new HSSFWorkbook()) {
+
+ Map styles = createStyles(wb);
+
+ for (int month = 0; month < 12; month++) {
+ calendar.set(Calendar.MONTH, month);
+ calendar.set(Calendar.DAY_OF_MONTH, 1);
+ //create a sheet for each month
+ Sheet sheet = wb.createSheet(months[month]);
+
+ //turn off gridlines
+ sheet.setDisplayGridlines(false);
+ sheet.setPrintGridlines(false);
+ sheet.setFitToPage(true);
+ sheet.setHorizontallyCenter(true);
+ PrintSetup printSetup = sheet.getPrintSetup();
+ printSetup.setLandscape(true);
+
+ //the following three statements are required only for HSSF
+ sheet.setAutobreaks(true);
+ printSetup.setFitHeight((short) 1);
+ printSetup.setFitWidth((short) 1);
+
+ //the header row: centered text in 48pt font
+ Row headerRow = sheet.createRow(0);
+ headerRow.setHeightInPoints(80);
+ Cell titleCell = headerRow.createCell(0);
+ titleCell.setCellValue(months[month] + " " + year);
+ titleCell.setCellStyle(styles.get("title"));
+ sheet.addMergedRegion(CellRangeAddress.valueOf("$A$1:$N$1"));
+
+ //header with month titles
+ Row monthRow = sheet.createRow(1);
+ for (int i = 0; i < days.length; i++) {
+ //set column widths, the width is measured in units of 1/256th of a character width
+ sheet.setColumnWidth(i * 2, 5 * 256); //the column is 5 characters wide
+ sheet.setColumnWidth(i * 2 + 1, 13 * 256); //the column is 13 characters wide
+ sheet.addMergedRegion(new CellRangeAddress(1, 1, i * 2, i * 2 + 1));
+ Cell monthCell = monthRow.createCell(i * 2);
+ monthCell.setCellValue(days[i]);
+ monthCell.setCellStyle(styles.get("month"));
+ }
+
+ int cnt = 1, day = 1;
+ int rownum = 2;
+ for (int j = 0; j < 6; j++) {
+ Row row = sheet.createRow(rownum++);
+ row.setHeightInPoints(100);
+ for (int i = 0; i < days.length; i++) {
+ Cell dayCell_1 = row.createCell(i * 2);
+ Cell dayCell_2 = row.createCell(i * 2 + 1);
+
+ int day_of_week = calendar.get(Calendar.DAY_OF_WEEK);
+ if (cnt >= day_of_week && calendar.get(Calendar.MONTH) == month) {
+ dayCell_1.setCellValue(day);
+ calendar.set(Calendar.DAY_OF_MONTH, ++day);
+
+ if (i == 0 || i == days.length - 1) {
+ dayCell_1.setCellStyle(styles.get("weekend_left"));
+ dayCell_2.setCellStyle(styles.get("weekend_right"));
+ } else {
+ dayCell_1.setCellStyle(styles.get("workday_left"));
+ dayCell_2.setCellStyle(styles.get("workday_right"));
+ }
+ } else {
+ dayCell_1.setCellStyle(styles.get("grey_left"));
+ dayCell_2.setCellStyle(styles.get("grey_right"));
+ }
+ cnt++;
+ }
+ if (calendar.get(Calendar.MONTH) > month) break;
+ }
+ }
+
+ // Write the output to a file
+ String file = "calendar.xls";
+ if (wb instanceof XSSFWorkbook) file += "x";
+
+ try (FileOutputStream out = new FileOutputStream(file)) {
+ wb.write(out);
+ }
+ }
+ }
+
+ /**
+ * cell styles used for formatting calendar sheets
+ */
+ private static Map createStyles(Workbook wb){
+ Map styles = new HashMap<>();
+
+ short borderColor = IndexedColors.GREY_50_PERCENT.getIndex();
+
+ CellStyle style;
+ Font titleFont = wb.createFont();
+ titleFont.setFontHeightInPoints((short)48);
+ titleFont.setColor(IndexedColors.DARK_BLUE.getIndex());
+ style = wb.createCellStyle();
+ style.setAlignment(HorizontalAlignment.CENTER);
+ style.setVerticalAlignment(VerticalAlignment.CENTER);
+ style.setFont(titleFont);
+ styles.put("title", style);
+
+ Font monthFont = wb.createFont();
+ monthFont.setFontHeightInPoints((short)12);
+ monthFont.setColor(IndexedColors.WHITE.getIndex());
+ monthFont.setBold(true);
+ style = wb.createCellStyle();
+ style.setAlignment(HorizontalAlignment.CENTER);
+ style.setVerticalAlignment(VerticalAlignment.CENTER);
+ style.setFillForegroundColor(IndexedColors.DARK_BLUE.getIndex());
+ style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+ style.setFont(monthFont);
+ styles.put("month", style);
+
+ Font dayFont = wb.createFont();
+ dayFont.setFontHeightInPoints((short)14);
+ dayFont.setBold(true);
+ style = wb.createCellStyle();
+ style.setAlignment(HorizontalAlignment.LEFT);
+ style.setVerticalAlignment(VerticalAlignment.TOP);
+ style.setFillForegroundColor(IndexedColors.LIGHT_CORNFLOWER_BLUE.getIndex());
+ style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+ style.setBorderLeft(BorderStyle.THIN);
+ style.setLeftBorderColor(borderColor);
+ style.setBorderBottom(BorderStyle.THIN);
+ style.setBottomBorderColor(borderColor);
+ style.setFont(dayFont);
+ styles.put("weekend_left", style);
+
+ style = wb.createCellStyle();
+ style.setAlignment(HorizontalAlignment.CENTER);
+ style.setVerticalAlignment(VerticalAlignment.TOP);
+ style.setFillForegroundColor(IndexedColors.LIGHT_CORNFLOWER_BLUE.getIndex());
+ style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+ style.setBorderRight(BorderStyle.THIN);
+ style.setRightBorderColor(borderColor);
+ style.setBorderBottom(BorderStyle.THIN);
+ style.setBottomBorderColor(borderColor);
+ styles.put("weekend_right", style);
+
+ style = wb.createCellStyle();
+ style.setAlignment(HorizontalAlignment.LEFT);
+ style.setVerticalAlignment(VerticalAlignment.TOP);
+ style.setBorderLeft(BorderStyle.THIN);
+ style.setFillForegroundColor(IndexedColors.WHITE.getIndex());
+ style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+ style.setLeftBorderColor(borderColor);
+ style.setBorderBottom(BorderStyle.THIN);
+ style.setBottomBorderColor(borderColor);
+ style.setFont(dayFont);
+ styles.put("workday_left", style);
+
+ style = wb.createCellStyle();
+ style.setAlignment(HorizontalAlignment.CENTER);
+ style.setVerticalAlignment(VerticalAlignment.TOP);
+ style.setFillForegroundColor(IndexedColors.WHITE.getIndex());
+ style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+ style.setBorderRight(BorderStyle.THIN);
+ style.setRightBorderColor(borderColor);
+ style.setBorderBottom(BorderStyle.THIN);
+ style.setBottomBorderColor(borderColor);
+ styles.put("workday_right", style);
+
+ style = wb.createCellStyle();
+ style.setBorderLeft(BorderStyle.THIN);
+ style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
+ style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+ style.setBorderBottom(BorderStyle.THIN);
+ style.setBottomBorderColor(borderColor);
+ styles.put("grey_left", style);
+
+ style = wb.createCellStyle();
+ style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
+ style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+ style.setBorderRight(BorderStyle.THIN);
+ style.setRightBorderColor(borderColor);
+ style.setBorderBottom(BorderStyle.THIN);
+ style.setBottomBorderColor(borderColor);
+ styles.put("grey_right", style);
+
+ return styles;
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/ss/CellStyleDetails.java b/src/examples/src/org/apache/poi/examples/ss/CellStyleDetails.java
new file mode 100644
index 0000000000..5081bfcd52
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/ss/CellStyleDetails.java
@@ -0,0 +1,98 @@
+/* ====================================================================
+ 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.examples.ss;
+
+import java.io.File;
+
+import org.apache.poi.hssf.usermodel.HSSFFont;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.hssf.util.HSSFColor;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellStyle;
+import org.apache.poi.ss.usermodel.Color;
+import org.apache.poi.ss.usermodel.DataFormatter;
+import org.apache.poi.ss.usermodel.Font;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.usermodel.WorkbookFactory;
+import org.apache.poi.ss.util.CellReference;
+import org.apache.poi.xssf.usermodel.XSSFColor;
+import org.apache.poi.xssf.usermodel.XSSFFont;
+
+/**
+ * Demonstrates how to read excel styles for cells
+ */
+@SuppressWarnings({"java:S106","java:S4823"})
+public final class CellStyleDetails {
+ private CellStyleDetails() {}
+
+ public static void main(String[] args) throws Exception {
+ if(args.length == 0) {
+ throw new IllegalArgumentException("Filename must be given");
+ }
+
+ try (Workbook wb = WorkbookFactory.create(new File(args[0]))) {
+ DataFormatter formatter = new DataFormatter();
+
+ for (int sn = 0; sn < wb.getNumberOfSheets(); sn++) {
+ Sheet sheet = wb.getSheetAt(sn);
+ System.out.println("Sheet #" + sn + " : " + sheet.getSheetName());
+
+ for (Row row : sheet) {
+ System.out.println(" Row " + row.getRowNum());
+
+ for (Cell cell : row) {
+ CellReference ref = new CellReference(cell);
+ System.out.print(" " + ref.formatAsString());
+ System.out.print(" (" + cell.getColumnIndex() + ") ");
+
+ CellStyle style = cell.getCellStyle();
+ System.out.print("Format=" + style.getDataFormatString() + " ");
+ System.out.print("FG=" + renderColor(style.getFillForegroundColorColor()) + " ");
+ System.out.print("BG=" + renderColor(style.getFillBackgroundColorColor()) + " ");
+
+ Font font = wb.getFontAt(style.getFontIndexAsInt());
+ System.out.print("Font=" + font.getFontName() + " ");
+ System.out.print("FontColor=");
+ if (font instanceof HSSFFont) {
+ System.out.print(renderColor(((HSSFFont) font).getHSSFColor((HSSFWorkbook) wb)));
+ }
+ if (font instanceof XSSFFont) {
+ System.out.print(renderColor(((XSSFFont) font).getXSSFColor()));
+ }
+
+ System.out.println();
+ System.out.println(" " + formatter.formatCellValue(cell));
+ }
+ }
+
+ System.out.println();
+ }
+ }
+ }
+
+ private static String renderColor(Color color) {
+ if(color instanceof HSSFColor) {
+ return ((HSSFColor)color).getHexString();
+ } else if(color instanceof XSSFColor) {
+ return ((XSSFColor)color).getARGBHex();
+ } else {
+ return "(none)";
+ }
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/ss/ConditionalFormats.java b/src/examples/src/org/apache/poi/examples/ss/ConditionalFormats.java
new file mode 100644
index 0000000000..22eec9633a
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/ss/ConditionalFormats.java
@@ -0,0 +1,732 @@
+/*
+ * ====================================================================
+ * 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.examples.ss;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.List;
+
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.formula.ConditionalFormattingEvaluator;
+import org.apache.poi.ss.formula.EvaluationConditionalFormatRule;
+import org.apache.poi.ss.formula.WorkbookEvaluatorProvider;
+import org.apache.poi.ss.usermodel.BuiltinFormats;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellStyle;
+import org.apache.poi.ss.usermodel.ColorScaleFormatting;
+import org.apache.poi.ss.usermodel.ComparisonOperator;
+import org.apache.poi.ss.usermodel.ConditionalFormattingRule;
+import org.apache.poi.ss.usermodel.ConditionalFormattingThreshold.RangeType;
+import org.apache.poi.ss.usermodel.DataBarFormatting;
+import org.apache.poi.ss.usermodel.ExtendedColor;
+import org.apache.poi.ss.usermodel.FontFormatting;
+import org.apache.poi.ss.usermodel.IconMultiStateFormatting;
+import org.apache.poi.ss.usermodel.IconMultiStateFormatting.IconSet;
+import org.apache.poi.ss.usermodel.IndexedColors;
+import org.apache.poi.ss.usermodel.PatternFormatting;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.SheetConditionalFormatting;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.ss.util.CellReference;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+/**
+ * Excel Conditional Formatting -- Examples
+ *
+ *
+ * Partly based on the code snippets from
+ * http://www.contextures.com/xlcondformat03.html
+ *
+ */
+@SuppressWarnings({"java:S106","java:S4823","java:S1192"})
+public final class ConditionalFormats {
+
+ private ConditionalFormats() {}
+
+ /**
+ * generates a sample workbook with conditional formatting,
+ * and prints out a summary of applied formats for one sheet
+ * @param args pass "-xls" to generate an HSSF workbook, default is XSSF
+ */
+ public static void main(String[] args) throws IOException {
+ final boolean isHSSF = args.length > 0 && args[0].equals("-xls");
+ try (Workbook wb = isHSSF ? new HSSFWorkbook() : new XSSFWorkbook()) {
+
+ sameCell(wb.createSheet("Same Cell"));
+ multiCell(wb.createSheet("MultiCell"));
+ overlapping(wb.createSheet("Overlapping"));
+ errors(wb.createSheet("Errors"));
+ hideDupplicates(wb.createSheet("Hide Dups"));
+ formatDuplicates(wb.createSheet("Duplicates"));
+ inList(wb.createSheet("In List"));
+ expiry(wb.createSheet("Expiry"));
+ shadeAlt(wb.createSheet("Shade Alt"));
+ shadeBands(wb.createSheet("Shade Bands"));
+ iconSets(wb.createSheet("Icon Sets"));
+ colourScales(wb.createSheet("Colour Scales"));
+ dataBars(wb.createSheet("Data Bars"));
+
+ // print overlapping rule results
+ evaluateRules(wb, "Overlapping");
+
+ // Write the output to a file
+ String file = "cf-poi.xls";
+ if (wb instanceof XSSFWorkbook) {
+ file += "x";
+ }
+ try (FileOutputStream out = new FileOutputStream(file)) {
+ wb.write(out);
+ }
+ System.out.println("Generated: " + file);
+ }
+ }
+
+ /**
+ * Highlight cells based on their values
+ */
+ static void sameCell(Sheet sheet) {
+ sheet.createRow(0).createCell(0).setCellValue(84);
+ sheet.createRow(1).createCell(0).setCellValue(74);
+ sheet.createRow(2).createCell(0).setCellValue(50);
+ sheet.createRow(3).createCell(0).setCellValue(51);
+ sheet.createRow(4).createCell(0).setCellValue(49);
+ sheet.createRow(5).createCell(0).setCellValue(41);
+
+ SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
+
+ // Condition 1: Cell Value Is greater than 70 (Blue Fill)
+ ConditionalFormattingRule rule1 = sheetCF.createConditionalFormattingRule(ComparisonOperator.GT, "70");
+ PatternFormatting fill1 = rule1.createPatternFormatting();
+ fill1.setFillBackgroundColor(IndexedColors.BLUE.index);
+ fill1.setFillPattern(PatternFormatting.SOLID_FOREGROUND);
+
+ // Condition 2: Cell Value Is less than 50 (Green Fill)
+ ConditionalFormattingRule rule2 = sheetCF.createConditionalFormattingRule(ComparisonOperator.LT, "50");
+ PatternFormatting fill2 = rule2.createPatternFormatting();
+ fill2.setFillBackgroundColor(IndexedColors.GREEN.index);
+ fill2.setFillPattern(PatternFormatting.SOLID_FOREGROUND);
+
+ CellRangeAddress[] regions = {
+ CellRangeAddress.valueOf("A1:A6")
+ };
+
+ sheetCF.addConditionalFormatting(regions, rule1, rule2);
+
+ sheet.getRow(0).createCell(2).setCellValue("<== Condition 1: Cell Value Is greater than 70 (Blue Fill)");
+ sheet.getRow(4).createCell(2).setCellValue("<== Condition 2: Cell Value Is less than 50 (Green Fill)");
+ }
+
+ /**
+ * Highlight multiple cells based on a formula
+ */
+ static void multiCell(Sheet sheet) {
+ // header row
+ Row row0 = sheet.createRow(0);
+ row0.createCell(0).setCellValue("Units");
+ row0.createCell(1).setCellValue("Cost");
+ row0.createCell(2).setCellValue("Total");
+
+ Row row1 = sheet.createRow(1);
+ row1.createCell(0).setCellValue(71);
+ row1.createCell(1).setCellValue(29);
+ row1.createCell(2).setCellValue(2059);
+
+ Row row2 = sheet.createRow(2);
+ row2.createCell(0).setCellValue(85);
+ row2.createCell(1).setCellValue(29);
+ row2.createCell(2).setCellValue(2059);
+
+ Row row3 = sheet.createRow(3);
+ row3.createCell(0).setCellValue(71);
+ row3.createCell(1).setCellValue(29);
+ row3.createCell(2).setCellValue(2059);
+
+ SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
+
+ // Condition 1: Formula Is =$B2>75 (Blue Fill)
+ ConditionalFormattingRule rule1 = sheetCF.createConditionalFormattingRule("$A2>75");
+ PatternFormatting fill1 = rule1.createPatternFormatting();
+ fill1.setFillBackgroundColor(IndexedColors.BLUE.index);
+ fill1.setFillPattern(PatternFormatting.SOLID_FOREGROUND);
+
+ CellRangeAddress[] regions = {
+ CellRangeAddress.valueOf("A2:C4")
+ };
+
+ sheetCF.addConditionalFormatting(regions, rule1);
+
+ sheet.getRow(2).createCell(4).setCellValue("<== Condition 1: Formula Is =$B2>75 (Blue Fill)");
+ }
+
+ /**
+ * Multiple conditional formatting rules can apply to
+ * one cell, some combining, some beating others.
+ * Done in order of the rules added to the
+ * SheetConditionalFormatting object
+ */
+ static void overlapping(Sheet sheet) {
+ for (int i=0; i<40; i++) {
+ int rn = i+1;
+ Row r = sheet.createRow(i);
+ r.createCell(0).setCellValue("This is row " + rn + " (" + i + ")");
+ String str = "";
+ if (rn%2 == 0) {
+ str = str + "even ";
+ }
+ if (rn%3 == 0) {
+ str = str + "x3 ";
+ }
+ if (rn%5 == 0) {
+ str = str + "x5 ";
+ }
+ if (rn%10 == 0) {
+ str = str + "x10 ";
+ }
+ if (str.length() == 0) {
+ str = "nothing special...";
+ }
+ r.createCell(1).setCellValue("It is " + str);
+ }
+ sheet.autoSizeColumn(0);
+ sheet.autoSizeColumn(1);
+
+ sheet.getRow(1).createCell(3).setCellValue("Even rows are blue");
+ sheet.getRow(2).createCell(3).setCellValue("Multiples of 3 have a grey background");
+ sheet.getRow(4).createCell(3).setCellValue("Multiples of 5 are bold");
+ sheet.getRow(9).createCell(3).setCellValue("Multiples of 10 are red (beats even)");
+
+ SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
+
+ // Condition 1: Row divides by 10, red (will beat #1)
+ ConditionalFormattingRule rule1 =
+ sheetCF.createConditionalFormattingRule("MOD(ROW(),10)=0");
+ FontFormatting font1 = rule1.createFontFormatting();
+ font1.setFontColorIndex(IndexedColors.RED.index);
+
+ // Condition 2: Row is even, blue
+ ConditionalFormattingRule rule2 =
+ sheetCF.createConditionalFormattingRule("MOD(ROW(),2)=0");
+ FontFormatting font2 = rule2.createFontFormatting();
+ font2.setFontColorIndex(IndexedColors.BLUE.index);
+
+ // Condition 3: Row divides by 5, bold
+ ConditionalFormattingRule rule3 =
+ sheetCF.createConditionalFormattingRule("MOD(ROW(),5)=0");
+ FontFormatting font3 = rule3.createFontFormatting();
+ font3.setFontStyle(false, true);
+
+ // Condition 4: Row divides by 3, grey background
+ ConditionalFormattingRule rule4 =
+ sheetCF.createConditionalFormattingRule("MOD(ROW(),3)=0");
+ PatternFormatting fill4 = rule4.createPatternFormatting();
+ fill4.setFillBackgroundColor(IndexedColors.GREY_25_PERCENT.index);
+ fill4.setFillPattern(PatternFormatting.SOLID_FOREGROUND);
+
+ // Apply
+ CellRangeAddress[] regions = {
+ CellRangeAddress.valueOf("A1:F41")
+ };
+
+ sheetCF.addConditionalFormatting(regions, rule1);
+ sheetCF.addConditionalFormatting(regions, rule2);
+ sheetCF.addConditionalFormatting(regions, rule3);
+ sheetCF.addConditionalFormatting(regions, rule4);
+ }
+
+ /**
+ * Use Excel conditional formatting to check for errors,
+ * and change the font colour to match the cell colour.
+ * In this example, if formula result is #DIV/0! then it will have white font colour.
+ */
+ static void errors(Sheet sheet) {
+ sheet.createRow(0).createCell(0).setCellValue(84);
+ sheet.createRow(1).createCell(0).setCellValue(0);
+ sheet.createRow(2).createCell(0).setCellFormula("ROUND(A1/A2,0)");
+ sheet.createRow(3).createCell(0).setCellValue(0);
+ sheet.createRow(4).createCell(0).setCellFormula("ROUND(A6/A4,0)");
+ sheet.createRow(5).createCell(0).setCellValue(41);
+
+ SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
+
+ // Condition 1: Formula Is =ISERROR(C2) (White Font)
+ ConditionalFormattingRule rule1 = sheetCF.createConditionalFormattingRule("ISERROR(A1)");
+ FontFormatting font = rule1.createFontFormatting();
+ font.setFontColorIndex(IndexedColors.WHITE.index);
+
+ CellRangeAddress[] regions = {
+ CellRangeAddress.valueOf("A1:A6")
+ };
+
+ sheetCF.addConditionalFormatting(regions, rule1);
+
+ sheet.getRow(2).createCell(1).setCellValue("<== The error in this cell is hidden. Condition: Formula Is =ISERROR(C2) (White Font)");
+ sheet.getRow(4).createCell(1).setCellValue("<== The error in this cell is hidden. Condition: Formula Is =ISERROR(C2) (White Font)");
+ }
+
+ /**
+ * Use Excel conditional formatting to hide the duplicate values,
+ * and make the list easier to read. In this example, when the table is sorted by Region,
+ * the second (and subsequent) occurences of each region name will have white font colour.
+ */
+ static void hideDupplicates(Sheet sheet) {
+ sheet.createRow(0).createCell(0).setCellValue("City");
+ sheet.createRow(1).createCell(0).setCellValue("Boston");
+ sheet.createRow(2).createCell(0).setCellValue("Boston");
+ sheet.createRow(3).createCell(0).setCellValue("Chicago");
+ sheet.createRow(4).createCell(0).setCellValue("Chicago");
+ sheet.createRow(5).createCell(0).setCellValue("New York");
+
+ SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
+
+ // Condition 1: Formula Is =A2=A1 (White Font)
+ ConditionalFormattingRule rule1 = sheetCF.createConditionalFormattingRule("A2=A1");
+ FontFormatting font = rule1.createFontFormatting();
+ font.setFontColorIndex(IndexedColors.WHITE.index);
+
+ CellRangeAddress[] regions = {
+ CellRangeAddress.valueOf("A2:A6")
+ };
+
+ sheetCF.addConditionalFormatting(regions, rule1);
+
+ sheet.getRow(1).createCell(1).setCellValue("<== the second (and subsequent) " +
+ "occurences of each region name will have white font colour. " +
+ "Condition: Formula Is =A2=A1 (White Font)");
+ }
+
+ /**
+ * Use Excel conditional formatting to highlight duplicate entries in a column.
+ */
+ static void formatDuplicates(Sheet sheet) {
+ sheet.createRow(0).createCell(0).setCellValue("Code");
+ sheet.createRow(1).createCell(0).setCellValue(4);
+ sheet.createRow(2).createCell(0).setCellValue(3);
+ sheet.createRow(3).createCell(0).setCellValue(6);
+ sheet.createRow(4).createCell(0).setCellValue(3);
+ sheet.createRow(5).createCell(0).setCellValue(5);
+ sheet.createRow(6).createCell(0).setCellValue(8);
+ sheet.createRow(7).createCell(0).setCellValue(0);
+ sheet.createRow(8).createCell(0).setCellValue(2);
+ sheet.createRow(9).createCell(0).setCellValue(8);
+ sheet.createRow(10).createCell(0).setCellValue(6);
+
+ SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
+
+ // Condition 1: Formula Is =A2=A1 (White Font)
+ ConditionalFormattingRule rule1 = sheetCF.createConditionalFormattingRule("COUNTIF($A$2:$A$11,A2)>1");
+ FontFormatting font = rule1.createFontFormatting();
+ font.setFontStyle(false, true);
+ font.setFontColorIndex(IndexedColors.BLUE.index);
+
+ CellRangeAddress[] regions = {
+ CellRangeAddress.valueOf("A2:A11")
+ };
+
+ sheetCF.addConditionalFormatting(regions, rule1);
+
+ sheet.getRow(2).createCell(1).setCellValue("<== Duplicates numbers in the column are highlighted. " +
+ "Condition: Formula Is =COUNTIF($A$2:$A$11,A2)>1 (Blue Font)");
+ }
+
+ /**
+ * Use Excel conditional formatting to highlight items that are in a list on the worksheet.
+ */
+ static void inList(Sheet sheet) {
+ sheet.createRow(0).createCell(0).setCellValue("Codes");
+ sheet.createRow(1).createCell(0).setCellValue("AA");
+ sheet.createRow(2).createCell(0).setCellValue("BB");
+ sheet.createRow(3).createCell(0).setCellValue("GG");
+ sheet.createRow(4).createCell(0).setCellValue("AA");
+ sheet.createRow(5).createCell(0).setCellValue("FF");
+ sheet.createRow(6).createCell(0).setCellValue("XX");
+ sheet.createRow(7).createCell(0).setCellValue("CC");
+
+ sheet.getRow(0).createCell(2).setCellValue("Valid");
+ sheet.getRow(1).createCell(2).setCellValue("AA");
+ sheet.getRow(2).createCell(2).setCellValue("BB");
+ sheet.getRow(3).createCell(2).setCellValue("CC");
+
+ SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
+
+ // Condition 1: Formula Is =A2=A1 (White Font)
+ ConditionalFormattingRule rule1 = sheetCF.createConditionalFormattingRule("COUNTIF($C$2:$C$4,A2)");
+ PatternFormatting fill1 = rule1.createPatternFormatting();
+ fill1.setFillBackgroundColor(IndexedColors.LIGHT_BLUE.index);
+ fill1.setFillPattern(PatternFormatting.SOLID_FOREGROUND);
+
+ CellRangeAddress[] regions = {
+ CellRangeAddress.valueOf("A2:A8")
+ };
+
+ sheetCF.addConditionalFormatting(regions, rule1);
+
+ sheet.getRow(2).createCell(3).setCellValue("<== Use Excel conditional formatting to highlight items that are in a list on the worksheet");
+ }
+
+ /**
+ * Use Excel conditional formatting to highlight payments that are due in the next thirty days.
+ * In this example, Due dates are entered in cells A2:A4.
+ */
+ static void expiry(Sheet sheet) {
+ CellStyle style = sheet.getWorkbook().createCellStyle();
+ style.setDataFormat((short)BuiltinFormats.getBuiltinFormat("d-mmm"));
+
+ sheet.createRow(0).createCell(0).setCellValue("Date");
+ sheet.createRow(1).createCell(0).setCellFormula("TODAY()+29");
+ sheet.createRow(2).createCell(0).setCellFormula("A2+1");
+ sheet.createRow(3).createCell(0).setCellFormula("A3+1");
+
+ for(int rownum = 1; rownum <= 3; rownum++) {
+ sheet.getRow(rownum).getCell(0).setCellStyle(style);
+ }
+
+ SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
+
+ // Condition 1: Formula Is =A2=A1 (White Font)
+ ConditionalFormattingRule rule1 = sheetCF.createConditionalFormattingRule("AND(A2-TODAY()>=0,A2-TODAY()<=30)");
+ FontFormatting font = rule1.createFontFormatting();
+ font.setFontStyle(false, true);
+ font.setFontColorIndex(IndexedColors.BLUE.index);
+
+ CellRangeAddress[] regions = {
+ CellRangeAddress.valueOf("A2:A4")
+ };
+
+ sheetCF.addConditionalFormatting(regions, rule1);
+
+ sheet.getRow(0).createCell(1).setCellValue("Dates within the next 30 days are highlighted");
+ }
+
+ /**
+ * Use Excel conditional formatting to shade alternating rows on the worksheet
+ */
+ static void shadeAlt(Sheet sheet) {
+ SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
+
+ // Condition 1: Formula Is =A2=A1 (White Font)
+ ConditionalFormattingRule rule1 = sheetCF.createConditionalFormattingRule("MOD(ROW(),2)");
+ PatternFormatting fill1 = rule1.createPatternFormatting();
+ fill1.setFillBackgroundColor(IndexedColors.LIGHT_GREEN.index);
+ fill1.setFillPattern(PatternFormatting.SOLID_FOREGROUND);
+
+ CellRangeAddress[] regions = {
+ CellRangeAddress.valueOf("A1:Z100")
+ };
+
+ sheetCF.addConditionalFormatting(regions, rule1);
+
+ sheet.createRow(0).createCell(1).setCellValue("Shade Alternating Rows");
+ sheet.createRow(1).createCell(1).setCellValue("Condition: Formula Is =MOD(ROW(),2) (Light Green Fill)");
+ }
+
+ /**
+ * You can use Excel conditional formatting to shade bands of rows on the worksheet.
+ * In this example, 3 rows are shaded light grey, and 3 are left with no shading.
+ * In the MOD function, the total number of rows in the set of banded rows (6) is entered.
+ */
+ static void shadeBands(Sheet sheet) {
+ SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
+
+ ConditionalFormattingRule rule1 = sheetCF.createConditionalFormattingRule("MOD(ROW(),6)<3");
+ PatternFormatting fill1 = rule1.createPatternFormatting();
+ fill1.setFillBackgroundColor(IndexedColors.GREY_25_PERCENT.index);
+ fill1.setFillPattern(PatternFormatting.SOLID_FOREGROUND);
+
+ CellRangeAddress[] regions = {
+ CellRangeAddress.valueOf("A1:Z100")
+ };
+
+ sheetCF.addConditionalFormatting(regions, rule1);
+
+ sheet.createRow(0).createCell(1).setCellValue("Shade Bands of Rows");
+ sheet.createRow(1).createCell(1).setCellValue("Condition: Formula Is =MOD(ROW(),6)<2 (Light Grey Fill)");
+ }
+
+ /**
+ * Icon Sets / Multi-States allow you to have icons shown which vary
+ * based on the values, eg Red traffic light / Yellow traffic light /
+ * Green traffic light
+ */
+ static void iconSets(Sheet sheet) {
+ sheet.createRow(0).createCell(0).setCellValue("Icon Sets");
+ Row r = sheet.createRow(1);
+ r.createCell(0).setCellValue("Reds");
+ r.createCell(1).setCellValue(0);
+ r.createCell(2).setCellValue(0);
+ r.createCell(3).setCellValue(0);
+ r = sheet.createRow(2);
+ r.createCell(0).setCellValue("Yellows");
+ r.createCell(1).setCellValue(5);
+ r.createCell(2).setCellValue(5);
+ r.createCell(3).setCellValue(5);
+ r = sheet.createRow(3);
+ r.createCell(0).setCellValue("Greens");
+ r.createCell(1).setCellValue(10);
+ r.createCell(2).setCellValue(10);
+ r.createCell(3).setCellValue(10);
+
+ SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
+
+ CellRangeAddress[] regions = { CellRangeAddress.valueOf("B1:B4") };
+ ConditionalFormattingRule rule1 =
+ sheetCF.createConditionalFormattingRule(IconSet.GYR_3_TRAFFIC_LIGHTS);
+ IconMultiStateFormatting im1 = rule1.getMultiStateFormatting();
+ im1.getThresholds()[0].setRangeType(RangeType.MIN);
+ im1.getThresholds()[1].setRangeType(RangeType.PERCENT);
+ im1.getThresholds()[1].setValue(33d);
+ im1.getThresholds()[2].setRangeType(RangeType.MAX);
+ sheetCF.addConditionalFormatting(regions, rule1);
+
+ regions = new CellRangeAddress[] { CellRangeAddress.valueOf("C1:C4") };
+ ConditionalFormattingRule rule2 =
+ sheetCF.createConditionalFormattingRule(IconSet.GYR_3_FLAGS);
+ IconMultiStateFormatting im2 = rule1.getMultiStateFormatting();
+ im2.getThresholds()[0].setRangeType(RangeType.PERCENT);
+ im2.getThresholds()[0].setValue(0d);
+ im2.getThresholds()[1].setRangeType(RangeType.PERCENT);
+ im2.getThresholds()[1].setValue(33d);
+ im2.getThresholds()[2].setRangeType(RangeType.PERCENT);
+ im2.getThresholds()[2].setValue(67d);
+ sheetCF.addConditionalFormatting(regions, rule2);
+
+ regions = new CellRangeAddress[] { CellRangeAddress.valueOf("D1:D4") };
+ ConditionalFormattingRule rule3 =
+ sheetCF.createConditionalFormattingRule(IconSet.GYR_3_SYMBOLS_CIRCLE);
+ IconMultiStateFormatting im3 = rule1.getMultiStateFormatting();
+ im3.setIconOnly(true);
+ im3.getThresholds()[0].setRangeType(RangeType.MIN);
+ im3.getThresholds()[1].setRangeType(RangeType.NUMBER);
+ im3.getThresholds()[1].setValue(3d);
+ im3.getThresholds()[2].setRangeType(RangeType.NUMBER);
+ im3.getThresholds()[2].setValue(7d);
+ sheetCF.addConditionalFormatting(regions, rule3);
+ }
+
+ /**
+ * Color Scales / Colour Scales / Colour Gradients allow you shade the
+ * background colour of the cell based on the values, eg from Red to
+ * Yellow to Green.
+ */
+ static void colourScales(Sheet sheet) {
+ sheet.createRow(0).createCell(0).setCellValue("Colour Scales");
+ Row r = sheet.createRow(1);
+ r.createCell(0).setCellValue("Red-Yellow-Green");
+ for (int i=1; i<=7; i++) {
+ r.createCell(i).setCellValue((i-1)*5.0);
+ }
+ r = sheet.createRow(2);
+ r.createCell(0).setCellValue("Red-White-Blue");
+ for (int i=1; i<=9; i++) {
+ r.createCell(i).setCellValue((i-1)*5.0);
+ }
+ r = sheet.createRow(3);
+ r.createCell(0).setCellValue("Blue-Green");
+ for (int i=1; i<=16; i++) {
+ r.createCell(i).setCellValue((i-1));
+ }
+ sheet.setColumnWidth(0, 5000);
+
+ SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
+
+ CellRangeAddress[] regions = { CellRangeAddress.valueOf("B2:H2") };
+ ConditionalFormattingRule rule1 =
+ sheetCF.createConditionalFormattingColorScaleRule();
+ ColorScaleFormatting cs1 = rule1.getColorScaleFormatting();
+ cs1.getThresholds()[0].setRangeType(RangeType.MIN);
+ cs1.getThresholds()[1].setRangeType(RangeType.PERCENTILE);
+ cs1.getThresholds()[1].setValue(50d);
+ cs1.getThresholds()[2].setRangeType(RangeType.MAX);
+ ((ExtendedColor)cs1.getColors()[0]).setARGBHex("FFF8696B");
+ ((ExtendedColor)cs1.getColors()[1]).setARGBHex("FFFFEB84");
+ ((ExtendedColor)cs1.getColors()[2]).setARGBHex("FF63BE7B");
+ sheetCF.addConditionalFormatting(regions, rule1);
+
+ regions = new CellRangeAddress[] { CellRangeAddress.valueOf("B3:J3") };
+ ConditionalFormattingRule rule2 =
+ sheetCF.createConditionalFormattingColorScaleRule();
+ ColorScaleFormatting cs2 = rule2.getColorScaleFormatting();
+ cs2.getThresholds()[0].setRangeType(RangeType.MIN);
+ cs2.getThresholds()[1].setRangeType(RangeType.PERCENTILE);
+ cs2.getThresholds()[1].setValue(50d);
+ cs2.getThresholds()[2].setRangeType(RangeType.MAX);
+ ((ExtendedColor)cs2.getColors()[0]).setARGBHex("FFF8696B");
+ ((ExtendedColor)cs2.getColors()[1]).setARGBHex("FFFCFCFF");
+ ((ExtendedColor)cs2.getColors()[2]).setARGBHex("FF5A8AC6");
+ sheetCF.addConditionalFormatting(regions, rule2);
+
+ regions = new CellRangeAddress[] { CellRangeAddress.valueOf("B4:Q4") };
+ ConditionalFormattingRule rule3=
+ sheetCF.createConditionalFormattingColorScaleRule();
+ ColorScaleFormatting cs3 = rule3.getColorScaleFormatting();
+ cs3.setNumControlPoints(2);
+ cs3.getThresholds()[0].setRangeType(RangeType.MIN);
+ cs3.getThresholds()[1].setRangeType(RangeType.MAX);
+ ((ExtendedColor)cs3.getColors()[0]).setARGBHex("FF5A8AC6");
+ ((ExtendedColor)cs3.getColors()[1]).setARGBHex("FF63BE7B");
+ sheetCF.addConditionalFormatting(regions, rule3);
+ }
+
+ /**
+ * DataBars / Data-Bars allow you to have bars shown vary
+ * based on the values, from full to empty
+ */
+ static void dataBars(Sheet sheet) {
+ sheet.createRow(0).createCell(0).setCellValue("Data Bars");
+ Row r = sheet.createRow(1);
+ r.createCell(1).setCellValue("Green Positive");
+ r.createCell(2).setCellValue("Blue Mix");
+ r.createCell(3).setCellValue("Red Negative");
+ r = sheet.createRow(2);
+ r.createCell(1).setCellValue(0);
+ r.createCell(2).setCellValue(0);
+ r.createCell(3).setCellValue(0);
+ r = sheet.createRow(3);
+ r.createCell(1).setCellValue(5);
+ r.createCell(2).setCellValue(-5);
+ r.createCell(3).setCellValue(-5);
+ r = sheet.createRow(4);
+ r.createCell(1).setCellValue(10);
+ r.createCell(2).setCellValue(10);
+ r.createCell(3).setCellValue(-10);
+ r = sheet.createRow(5);
+ r.createCell(1).setCellValue(5);
+ r.createCell(2).setCellValue(5);
+ r.createCell(3).setCellValue(-5);
+ r = sheet.createRow(6);
+ r.createCell(1).setCellValue(20);
+ r.createCell(2).setCellValue(-10);
+ r.createCell(3).setCellValue(-20);
+ sheet.setColumnWidth(0, 3000);
+ sheet.setColumnWidth(1, 5000);
+ sheet.setColumnWidth(2, 5000);
+ sheet.setColumnWidth(3, 5000);
+
+ SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
+
+ ExtendedColor color = sheet.getWorkbook().getCreationHelper().createExtendedColor();
+ color.setARGBHex("FF63BE7B");
+ CellRangeAddress[] regions = { CellRangeAddress.valueOf("B2:B7") };
+ ConditionalFormattingRule rule1 = sheetCF.createConditionalFormattingRule(color);
+ DataBarFormatting db1 = rule1.getDataBarFormatting();
+ db1.getMinThreshold().setRangeType(RangeType.MIN);
+ db1.getMaxThreshold().setRangeType(RangeType.MAX);
+ sheetCF.addConditionalFormatting(regions, rule1);
+
+ color = sheet.getWorkbook().getCreationHelper().createExtendedColor();
+ color.setARGBHex("FF5A8AC6");
+ regions = new CellRangeAddress[] { CellRangeAddress.valueOf("C2:C7") };
+ ConditionalFormattingRule rule2 = sheetCF.createConditionalFormattingRule(color);
+ DataBarFormatting db2 = rule2.getDataBarFormatting();
+ db2.getMinThreshold().setRangeType(RangeType.MIN);
+ db2.getMaxThreshold().setRangeType(RangeType.MAX);
+ sheetCF.addConditionalFormatting(regions, rule2);
+
+ color = sheet.getWorkbook().getCreationHelper().createExtendedColor();
+ color.setARGBHex("FFF8696B");
+ regions = new CellRangeAddress[] { CellRangeAddress.valueOf("D2:D7") };
+ ConditionalFormattingRule rule3 = sheetCF.createConditionalFormattingRule(color);
+ DataBarFormatting db3 = rule3.getDataBarFormatting();
+ db3.getMinThreshold().setRangeType(RangeType.MIN);
+ db3.getMaxThreshold().setRangeType(RangeType.MAX);
+ sheetCF.addConditionalFormatting(regions, rule3);
+ }
+
+ /**
+ * Print out a summary of the conditional formatting rules applied to cells on the given sheet.
+ * Only cells with a matching rule are printed, and for those, all matching rules are sumarized.
+ */
+ static void evaluateRules(Workbook wb, String sheetName) {
+ final WorkbookEvaluatorProvider wbEvalProv = (WorkbookEvaluatorProvider) wb.getCreationHelper().createFormulaEvaluator();
+ final ConditionalFormattingEvaluator cfEval = new ConditionalFormattingEvaluator(wb, wbEvalProv);
+ // if cell values have changed, clear cached format results
+ cfEval.clearAllCachedValues();
+
+ final Sheet sheet = wb.getSheet(sheetName);
+ for (Row r : sheet) {
+ for (Cell c : r) {
+ final List rules = cfEval.getConditionalFormattingForCell(c);
+ // check rules list for null, although current implementation will return an empty list, not null, then do what you want with results
+ if (rules == null || rules.isEmpty()) {
+ continue;
+ }
+ final CellReference ref = ConditionalFormattingEvaluator.getRef(c);
+ if (rules.isEmpty()) {
+ continue;
+ }
+
+ System.out.println("\n"
+ + ref.formatAsString()
+ + " has conditional formatting.");
+
+ for (EvaluationConditionalFormatRule rule : rules) {
+ ConditionalFormattingRule cf = rule.getRule();
+
+ StringBuilder b = new StringBuilder();
+ b.append("\tRule ")
+ .append(rule.getFormattingIndex())
+ .append(": ");
+
+ // check for color scale
+ if (cf.getColorScaleFormatting() != null) {
+ b.append("\n\t\tcolor scale (caller must calculate bucket)");
+ }
+ // check for data bar
+ if (cf.getDataBarFormatting() != null) {
+ b.append("\n\t\tdata bar (caller must calculate bucket)");
+ }
+ // check for icon set
+ if (cf.getMultiStateFormatting() != null) {
+ b.append("\n\t\ticon set (caller must calculate icon bucket)");
+ }
+ // check for fill
+ if (cf.getPatternFormatting() != null) {
+ final PatternFormatting fill = cf.getPatternFormatting();
+ b.append("\n\t\tfill pattern ")
+ .append(fill.getFillPattern())
+ .append(" color index ")
+ .append(fill.getFillBackgroundColor());
+ }
+ // font stuff
+ if (cf.getFontFormatting() != null) {
+ final FontFormatting ff = cf.getFontFormatting();
+ b.append("\n\t\tfont format ")
+ .append("color index ")
+ .append(ff.getFontColorIndex());
+ if (ff.isBold()) {
+ b.append(" bold");
+ }
+ if (ff.isItalic()) {
+ b.append(" italic");
+ }
+ if (ff.isStruckout()) {
+ b.append(" strikeout");
+ }
+ b.append(" underline index ")
+ .append(ff.getUnderlineType());
+ }
+
+ System.out.println(b);
+ }
+ }
+ }
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/ss/DrawingBorders.java b/src/examples/src/org/apache/poi/examples/ss/DrawingBorders.java
new file mode 100644
index 0000000000..f98d64b2c6
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/ss/DrawingBorders.java
@@ -0,0 +1,107 @@
+/*
+ * ====================================================================
+ * 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.examples.ss;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.BorderExtent;
+import org.apache.poi.ss.usermodel.BorderStyle;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.IndexedColors;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.ss.util.PropertyTemplate;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+/**
+ * Excel Border Drawing - examples
+ *
+ *
+ * Partly based on the code snippets from
+ * org.apache.poi.ss.examples.ConditionalFormats
+ *
+ */
+@SuppressWarnings({"java:S106","java:S4823"})
+public final class DrawingBorders {
+
+ private DrawingBorders() {}
+
+ public static void main(String[] args) throws IOException {
+ try (Workbook wb = (args.length > 0 && args[0].equals("-xls"))
+ ? new HSSFWorkbook() : new XSSFWorkbook()) {
+ // add a sheet, and put some values into it
+ Sheet sh1 = wb.createSheet("Sheet1");
+ Row r = sh1.createRow(0);
+ Cell c = r.createCell(1);
+ c.setCellValue("All Borders Medium Width");
+ r = sh1.createRow(4);
+ c = r.createCell(1);
+ c.setCellValue("Medium Outside / Thin Inside Borders");
+ r = sh1.createRow(8);
+ c = r.createCell(1);
+ c.setCellValue("Colored Borders");
+
+ // draw borders (three 3x3 grids)
+ PropertyTemplate pt = new PropertyTemplate();
+ // #1) these borders will all be medium in default color
+ pt.drawBorders(new CellRangeAddress(1, 3, 1, 3),
+ BorderStyle.MEDIUM, BorderExtent.ALL);
+ // #2) these cells will have medium outside borders and thin inside borders
+ pt.drawBorders(new CellRangeAddress(5, 7, 1, 3),
+ BorderStyle.MEDIUM, BorderExtent.OUTSIDE);
+ pt.drawBorders(new CellRangeAddress(5, 7, 1, 3), BorderStyle.THIN,
+ BorderExtent.INSIDE);
+ // #3) these cells will all be medium weight with different colors for the
+ // outside, inside horizontal, and inside vertical borders. The center
+ // cell will have no borders.
+ pt.drawBorders(new CellRangeAddress(9, 11, 1, 3),
+ BorderStyle.MEDIUM, IndexedColors.RED.getIndex(),
+ BorderExtent.OUTSIDE);
+ pt.drawBorders(new CellRangeAddress(9, 11, 1, 3),
+ BorderStyle.MEDIUM, IndexedColors.BLUE.getIndex(),
+ BorderExtent.INSIDE_VERTICAL);
+ pt.drawBorders(new CellRangeAddress(9, 11, 1, 3),
+ BorderStyle.MEDIUM, IndexedColors.GREEN.getIndex(),
+ BorderExtent.INSIDE_HORIZONTAL);
+ pt.drawBorders(new CellRangeAddress(10, 10, 2, 2),
+ BorderStyle.NONE,
+ BorderExtent.ALL);
+
+ // apply borders to sheet
+ pt.applyBorders(sh1);
+
+ // add another sheet and apply the borders to it
+ Sheet sh2 = wb.createSheet("Sheet2");
+ pt.applyBorders(sh2);
+
+ // Write the output to a file
+ String file = "db-poi.xls" + (wb instanceof XSSFWorkbook ? "x" : "");
+ try (FileOutputStream out = new FileOutputStream(file)) {
+ wb.write(out);
+ }
+ System.out.println("Generated: " + file);
+ }
+ }
+
+}
diff --git a/src/examples/src/org/apache/poi/examples/ss/ExcelComparator.java b/src/examples/src/org/apache/poi/examples/ss/ExcelComparator.java
new file mode 100644
index 0000000000..825b72b0c2
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/ss/ExcelComparator.java
@@ -0,0 +1,685 @@
+/* ====================================================================
+ 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.examples.ss;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+
+import org.apache.poi.ss.usermodel.BorderStyle;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellType;
+import org.apache.poi.ss.usermodel.Color;
+import org.apache.poi.ss.usermodel.DateUtil;
+import org.apache.poi.ss.usermodel.FillPatternType;
+import org.apache.poi.ss.usermodel.HorizontalAlignment;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.usermodel.WorkbookFactory;
+import org.apache.poi.ss.util.CellReference;
+import org.apache.poi.xssf.usermodel.XSSFCell;
+import org.apache.poi.xssf.usermodel.XSSFCellStyle;
+import org.apache.poi.xssf.usermodel.XSSFColor;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+
+/**
+ * Utility to compare Excel File Contents cell by cell for all sheets.
+ *
+ *
This utility will be used to compare Excel File Contents cell by cell for all sheets programmatically.
+ *
+ *
Below are the list of Attribute comparison supported in this version.
+ *
+ *
+ *
Cell Alignment
+ *
Cell Border Attributes
+ *
Cell Data
+ *
Cell Data-Type
+ *
Cell Fill Color
+ *
Cell Fill pattern
+ *
Cell Font Attributes
+ *
Cell Font Family
+ *
Cell Font Size
+ *
Cell Protection
+ *
Name of the sheets
+ *
Number of Columns
+ *
Number of Rows
+ *
Number of Sheet
+ *
+ *
+ *
(Some of the above attribute comparison only work for *.xlsx format currently. In future it can be enhanced.)
+ */
+@SuppressWarnings({"java:S106","java:S4823","java:S1192"})
+public class ExcelComparator {
+
+ private static final String CELL_DATA_DOES_NOT_MATCH = "Cell Data does not Match ::";
+ private static final String CELL_FONT_ATTRIBUTES_DOES_NOT_MATCH = "Cell Font Attributes does not Match ::";
+
+ private static class Locator {
+ Workbook workbook;
+ Sheet sheet;
+ Row row;
+ Cell cell;
+ }
+
+ List listOfDifferences = new ArrayList<>();
+
+ public static void main(String[] args) throws Exception {
+ if (args.length != 2 || !(new File(args[0]).exists()) || !(new File(args[1]).exists())) {
+ System.err.println("java -cp "+ExcelComparator.class.getCanonicalName()+" compare(Workbook wb1, Workbook wb2) {
+ Locator loc1 = new Locator();
+ Locator loc2 = new Locator();
+ loc1.workbook = wb1;
+ loc2.workbook = wb2;
+
+ ExcelComparator excelComparator = new ExcelComparator();
+ excelComparator.compareNumberOfSheets(loc1, loc2 );
+ excelComparator.compareSheetNames(loc1, loc2);
+ excelComparator.compareSheetData(loc1, loc2);
+
+ return excelComparator.listOfDifferences;
+ }
+
+ /**
+ * Compare data in all sheets.
+ */
+ private void compareDataInAllSheets(Locator loc1, Locator loc2) {
+ for (int i = 0; i < loc1.workbook.getNumberOfSheets(); i++) {
+ if (loc2.workbook.getNumberOfSheets() <= i) {
+ return;
+ }
+
+ loc1.sheet = loc1.workbook.getSheetAt(i);
+ loc2.sheet = loc2.workbook.getSheetAt(i);
+
+ compareDataInSheet(loc1, loc2);
+ }
+ }
+
+ private void compareDataInSheet(Locator loc1, Locator loc2) {
+ for (int j = 0; j <= loc1.sheet.getLastRowNum(); j++) {
+ if (loc2.sheet.getLastRowNum() <= j) {
+ return;
+ }
+
+ loc1.row = loc1.sheet.getRow(j);
+ loc2.row = loc2.sheet.getRow(j);
+
+ if ((loc1.row == null) || (loc2.row == null)) {
+ continue;
+ }
+
+ compareDataInRow(loc1, loc2);
+ }
+ }
+
+ private void compareDataInRow(Locator loc1, Locator loc2) {
+ for (int k = 0; k <= loc1.row.getLastCellNum(); k++) {
+ if (loc2.row.getLastCellNum() <= k) {
+ return;
+ }
+
+ loc1.cell = loc1.row.getCell(k);
+ loc2.cell = loc2.row.getCell(k);
+
+ if ((loc1.cell == null) || (loc2.cell == null)) {
+ continue;
+ }
+
+ compareDataInCell(loc1, loc2);
+ }
+ }
+
+ private void compareDataInCell(Locator loc1, Locator loc2) {
+ if (isCellTypeMatches(loc1, loc2)) {
+ final CellType loc1cellType = loc1.cell.getCellType();
+ switch(loc1cellType) {
+ case BLANK:
+ case STRING:
+ case ERROR:
+ isCellContentMatches(loc1,loc2);
+ break;
+ case BOOLEAN:
+ isCellContentMatchesForBoolean(loc1,loc2);
+ break;
+ case FORMULA:
+ isCellContentMatchesForFormula(loc1,loc2);
+ break;
+ case NUMERIC:
+ if (DateUtil.isCellDateFormatted(loc1.cell)) {
+ isCellContentMatchesForDate(loc1,loc2);
+ } else {
+ isCellContentMatchesForNumeric(loc1,loc2);
+ }
+ break;
+ default:
+ throw new IllegalStateException("Unexpected cell type: " + loc1cellType);
+ }
+ }
+
+ isCellFillPatternMatches(loc1,loc2);
+ isCellAlignmentMatches(loc1,loc2);
+ isCellHiddenMatches(loc1,loc2);
+ isCellLockedMatches(loc1,loc2);
+ isCellFontFamilyMatches(loc1,loc2);
+ isCellFontSizeMatches(loc1,loc2);
+ isCellFontBoldMatches(loc1,loc2);
+ isCellUnderLineMatches(loc1,loc2);
+ isCellFontItalicsMatches(loc1,loc2);
+ isCellBorderMatches(loc1,loc2,'t');
+ isCellBorderMatches(loc1,loc2,'l');
+ isCellBorderMatches(loc1,loc2,'b');
+ isCellBorderMatches(loc1,loc2,'r');
+ isCellFillBackGroundMatches(loc1,loc2);
+ }
+
+ /**
+ * Compare number of columns in sheets.
+ */
+ private void compareNumberOfColumnsInSheets(Locator loc1, Locator loc2) {
+ for (int i = 0; i < loc1.workbook.getNumberOfSheets(); i++) {
+ if (loc2.workbook.getNumberOfSheets() <= i) {
+ return;
+ }
+
+ loc1.sheet = loc1.workbook.getSheetAt(i);
+ loc2.sheet = loc2.workbook.getSheetAt(i);
+
+ Iterator ri1 = loc1.sheet.rowIterator();
+ Iterator ri2 = loc2.sheet.rowIterator();
+
+ int num1 = (ri1.hasNext()) ? ri1.next().getPhysicalNumberOfCells() : 0;
+ int num2 = (ri2.hasNext()) ? ri2.next().getPhysicalNumberOfCells() : 0;
+
+ if (num1 != num2) {
+ String str = String.format(Locale.ROOT, "%s\nworkbook1 -> %s [%d] != workbook2 -> %s [%d]",
+ "Number Of Columns does not Match ::",
+ loc1.sheet.getSheetName(), num1,
+ loc2.sheet.getSheetName(), num2
+ );
+ listOfDifferences.add(str);
+ }
+ }
+ }
+
+ /**
+ * Compare number of rows in sheets.
+ */
+ private void compareNumberOfRowsInSheets(Locator loc1, Locator loc2) {
+ for (int i = 0; i < loc1.workbook.getNumberOfSheets(); i++) {
+ if (loc2.workbook.getNumberOfSheets() <= i) {
+ return;
+ }
+
+ loc1.sheet = loc1.workbook.getSheetAt(i);
+ loc2.sheet = loc2.workbook.getSheetAt(i);
+
+ int num1 = loc1.sheet.getPhysicalNumberOfRows();
+ int num2 = loc2.sheet.getPhysicalNumberOfRows();
+
+ if (num1 != num2) {
+ String str = String.format(Locale.ROOT, "%s\nworkbook1 -> %s [%d] != workbook2 -> %s [%d]",
+ "Number Of Rows does not Match ::",
+ loc1.sheet.getSheetName(), num1,
+ loc2.sheet.getSheetName(), num2
+ );
+ listOfDifferences.add(str);
+ }
+ }
+
+ }
+
+ /**
+ * Compare number of sheets.
+ */
+ private void compareNumberOfSheets(Locator loc1, Locator loc2) {
+ int num1 = loc1.workbook.getNumberOfSheets();
+ int num2 = loc2.workbook.getNumberOfSheets();
+ if (num1 != num2) {
+ String str = String.format(Locale.ROOT, "%s\nworkbook1 [%d] != workbook2 [%d]",
+ "Number of Sheets do not match ::",
+ num1, num2
+ );
+
+ listOfDifferences.add(str);
+
+ }
+ }
+
+ /**
+ * Compare sheet data.
+ */
+ private void compareSheetData(Locator loc1, Locator loc2) {
+ compareNumberOfRowsInSheets(loc1, loc2);
+ compareNumberOfColumnsInSheets(loc1, loc2);
+ compareDataInAllSheets(loc1, loc2);
+
+ }
+
+ /**
+ * Compare sheet names.
+ */
+ private void compareSheetNames(Locator loc1, Locator loc2) {
+ for (int i = 0; i < loc1.workbook.getNumberOfSheets(); i++) {
+ String name1 = loc1.workbook.getSheetName(i);
+ String name2 = (loc2.workbook.getNumberOfSheets() > i) ? loc2.workbook.getSheetName(i) : "";
+
+ if (!name1.equals(name2)) {
+ String str = String.format(Locale.ROOT, "%s\nworkbook1 -> %s [%d] != workbook2 -> %s [%d]",
+ "Name of the sheets do not match ::", name1, i+1, name2, i+1
+ );
+ listOfDifferences.add(str);
+ }
+ }
+ }
+
+ /**
+ * Formats the message.
+ */
+ private void addMessage(Locator loc1, Locator loc2, String messageStart, String value1, String value2) {
+ String str =
+ String.format(Locale.ROOT, "%s\nworkbook1 -> %s -> %s [%s] != workbook2 -> %s -> %s [%s]",
+ messageStart,
+ loc1.sheet.getSheetName(), new CellReference(loc1.cell).formatAsString(), value1,
+ loc2.sheet.getSheetName(), new CellReference(loc2.cell).formatAsString(), value2
+ );
+ listOfDifferences.add(str);
+ }
+
+ /**
+ * Checks if cell alignment matches.
+ */
+ private void isCellAlignmentMatches(Locator loc1, Locator loc2) {
+ if(loc1.cell.getCellStyle() == null || loc2.cell.getCellStyle() == null) {
+ return;
+ }
+
+ HorizontalAlignment align1 = loc1.cell.getCellStyle().getAlignment();
+ HorizontalAlignment align2 = loc2.cell.getCellStyle().getAlignment();
+ if (align1 != align2) {
+ addMessage(loc1, loc2,
+ "Cell Alignment does not Match ::",
+ align1.name(),
+ align2.name()
+ );
+ }
+ }
+
+ /**
+ * Checks if cell border bottom matches.
+ */
+ private void isCellBorderMatches(Locator loc1, Locator loc2, char borderSide) {
+ if (!(loc1.cell instanceof XSSFCell) ||
+ loc1.cell.getCellStyle() == null || loc2.cell.getCellStyle() == null) {
+ return;
+ }
+
+ XSSFCellStyle style1 = ((XSSFCell)loc1.cell).getCellStyle();
+ XSSFCellStyle style2 = ((XSSFCell)loc2.cell).getCellStyle();
+ boolean b1, b2;
+ String borderName;
+ switch (borderSide) {
+ case 't': default:
+ b1 = style1.getBorderTop() == BorderStyle.THIN;
+ b2 = style2.getBorderTop() == BorderStyle.THIN;
+ borderName = "TOP";
+ break;
+ case 'b':
+ b1 = style1.getBorderBottom() == BorderStyle.THIN;
+ b2 = style2.getBorderBottom() == BorderStyle.THIN;
+ borderName = "BOTTOM";
+ break;
+ case 'l':
+ b1 = style1.getBorderLeft() == BorderStyle.THIN;
+ b2 = style2.getBorderLeft() == BorderStyle.THIN;
+ borderName = "LEFT";
+ break;
+ case 'r':
+ b1 = style1.getBorderRight() == BorderStyle.THIN;
+ b2 = style2.getBorderRight() == BorderStyle.THIN;
+ borderName = "RIGHT";
+ break;
+ }
+ if (b1 != b2) {
+ addMessage(loc1, loc2,
+ "Cell Border Attributes does not Match ::",
+ (b1 ? "" : "NOT ")+borderName+" BORDER",
+ (b2 ? "" : "NOT ")+borderName+" BORDER"
+ );
+ }
+ }
+
+ /**
+ * Checks if cell content matches.
+ */
+ private void isCellContentMatches(Locator loc1, Locator loc2) {
+ String str1 = loc1.cell.toString();
+ String str2 = loc2.cell.toString();
+ if (!str1.equals(str2)) {
+ addMessage(loc1,loc2,CELL_DATA_DOES_NOT_MATCH,str1,str2);
+ }
+ }
+
+ /**
+ * Checks if cell content matches for boolean.
+ */
+ private void isCellContentMatchesForBoolean(Locator loc1, Locator loc2) {
+ boolean b1 = loc1.cell.getBooleanCellValue();
+ boolean b2 = loc2.cell.getBooleanCellValue();
+ if (b1 != b2) {
+ addMessage(loc1,loc2,CELL_DATA_DOES_NOT_MATCH,Boolean.toString(b1),Boolean.toString(b2));
+ }
+ }
+
+ /**
+ * Checks if cell content matches for date.
+ */
+ private void isCellContentMatchesForDate(Locator loc1, Locator loc2) {
+ Date date1 = loc1.cell.getDateCellValue();
+ Date date2 = loc2.cell.getDateCellValue();
+ if (!date1.equals(date2)) {
+ addMessage(loc1, loc2, CELL_DATA_DOES_NOT_MATCH, date1.toString(), date2.toString());
+ }
+ }
+
+
+ /**
+ * Checks if cell content matches for formula.
+ */
+ private void isCellContentMatchesForFormula(Locator loc1, Locator loc2) {
+ // TODO: actually evaluate the formula / NPE checks
+ String form1 = loc1.cell.getCellFormula();
+ String form2 = loc2.cell.getCellFormula();
+ if (!form1.equals(form2)) {
+ addMessage(loc1, loc2, CELL_DATA_DOES_NOT_MATCH, form1, form2);
+ }
+ }
+
+ /**
+ * Checks if cell content matches for numeric.
+ */
+ private void isCellContentMatchesForNumeric(Locator loc1, Locator loc2) {
+ // TODO: Check for NaN
+ double num1 = loc1.cell.getNumericCellValue();
+ double num2 = loc2.cell.getNumericCellValue();
+ if (num1 != num2) {
+ addMessage(loc1, loc2, CELL_DATA_DOES_NOT_MATCH, Double.toString(num1), Double.toString(num2));
+ }
+ }
+
+ private String getCellFillBackground(Locator loc) {
+ Color col = loc.cell.getCellStyle().getFillForegroundColorColor();
+ return (col instanceof XSSFColor) ? ((XSSFColor)col).getARGBHex() : "NO COLOR";
+ }
+
+ /**
+ * Checks if cell file back ground matches.
+ */
+ private void isCellFillBackGroundMatches(Locator loc1, Locator loc2) {
+ if(loc1.cell.getCellStyle() == null || loc2.cell.getCellStyle() == null) {
+ return;
+ }
+
+ String col1 = getCellFillBackground(loc1);
+ String col2 = getCellFillBackground(loc2);
+ if (!col1.equals(col2)) {
+ addMessage(loc1, loc2, "Cell Fill Color does not Match ::", col1, col2);
+ }
+ }
+ /**
+ * Checks if cell fill pattern matches.
+ */
+ private void isCellFillPatternMatches(Locator loc1, Locator loc2) {
+ if(loc1.cell.getCellStyle() == null || loc2.cell.getCellStyle() == null) {
+ return;
+ }
+
+ FillPatternType fill1 = loc1.cell.getCellStyle().getFillPattern();
+ FillPatternType fill2 = loc2.cell.getCellStyle().getFillPattern();
+ if (fill1 != fill2) {
+ addMessage(loc1, loc2,
+ "Cell Fill pattern does not Match ::",
+ fill1.name(),
+ fill2.name()
+ );
+ }
+ }
+
+ /**
+ * Checks if cell font bold matches.
+ */
+ private void isCellFontBoldMatches(Locator loc1, Locator loc2) {
+ if (!(loc1.cell instanceof XSSFCell) ||
+ loc1.cell.getCellStyle() == null || loc2.cell.getCellStyle() == null) {
+ return;
+ }
+
+ if(hasInvalidFontIndex(loc1, loc2)) {
+ return;
+ }
+
+ boolean b1 = ((XSSFCell)loc1.cell).getCellStyle().getFont().getBold();
+ boolean b2 = ((XSSFCell)loc2.cell).getCellStyle().getFont().getBold();
+ if (b1 != b2) {
+ addMessage(loc1, loc2,
+ CELL_FONT_ATTRIBUTES_DOES_NOT_MATCH,
+ (b1 ? "" : "NOT ")+"BOLD",
+ (b2 ? "" : "NOT ")+"BOLD"
+ );
+ }
+ }
+
+ /**
+ * Checks if cell font family matches.
+ */
+ private void isCellFontFamilyMatches(Locator loc1, Locator loc2) {
+ if (!(loc1.cell instanceof XSSFCell) ||
+ loc1.cell.getCellStyle() == null || loc2.cell.getCellStyle() == null) {
+ return;
+ }
+
+ if(hasInvalidFontIndex(loc1, loc2)) {
+ return;
+ }
+
+ String family1 = ((XSSFCell)loc1.cell).getCellStyle().getFont().getFontName();
+ String family2 = ((XSSFCell)loc2.cell).getCellStyle().getFont().getFontName();
+ if (!family1.equals(family2)) {
+ addMessage(loc1, loc2, "Cell Font Family does not Match ::", family1, family2);
+ }
+ }
+
+ private boolean hasInvalidFontIndex(Locator loc1, Locator loc2) {
+ int fontIdx1 = loc1.cell.getCellStyle().getFontIndexAsInt();
+ int fontCount1 = ((XSSFWorkbook)loc1.workbook).getStylesSource().getFonts().size();
+ int fontIdx2 = loc2.cell.getCellStyle().getFontIndexAsInt();
+ int fontCount2 = ((XSSFWorkbook)loc2.workbook).getStylesSource().getFonts().size();
+
+ if(fontIdx1 >= fontCount1 || fontIdx2 >= fontCount2) {
+ addMessage(loc1, loc2, "Corrupted file, cell style references a font which is not defined", Integer.toString(fontIdx1), Integer.toString(fontIdx2));
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks if cell font italics matches.
+ */
+ private void isCellFontItalicsMatches(Locator loc1, Locator loc2) {
+ if (!(loc1.cell instanceof XSSFCell) ||
+ loc1.cell.getCellStyle() == null || loc2.cell.getCellStyle() == null) {
+ return;
+ }
+
+ if(hasInvalidFontIndex(loc1, loc2)) {
+ return;
+ }
+
+ boolean b1 = ((XSSFCell)loc1.cell).getCellStyle().getFont().getItalic();
+ boolean b2 = ((XSSFCell)loc2.cell).getCellStyle().getFont().getItalic();
+ if (b1 != b2) {
+ addMessage(loc1, loc2,
+ CELL_FONT_ATTRIBUTES_DOES_NOT_MATCH,
+ (b1 ? "" : "NOT ")+"ITALICS",
+ (b2 ? "" : "NOT ")+"ITALICS"
+ );
+ }
+ }
+
+ /**
+ * Checks if cell font size matches.
+ */
+ private void isCellFontSizeMatches(Locator loc1, Locator loc2) {
+ if (!(loc1.cell instanceof XSSFCell) ||
+ loc1.cell.getCellStyle() == null || loc2.cell.getCellStyle() == null) {
+ return;
+ }
+
+ if(hasInvalidFontIndex(loc1, loc2)) {
+ return;
+ }
+
+ short size1 = ((XSSFCell)loc1.cell).getCellStyle().getFont().getFontHeightInPoints();
+ short size2 = ((XSSFCell)loc2.cell).getCellStyle().getFont().getFontHeightInPoints();
+ if (size1 != size2) {
+ addMessage(loc1, loc2,
+ "Cell Font Size does not Match ::",
+ Short.toString(size1),
+ Short.toString(size2)
+ );
+ }
+ }
+
+ /**
+ * Checks if cell hidden matches.
+ */
+ private void isCellHiddenMatches(Locator loc1, Locator loc2) {
+ if (loc1.cell.getCellStyle() == null || loc2.cell.getCellStyle() == null) {
+ return;
+ }
+
+ boolean b1 = loc1.cell.getCellStyle().getHidden();
+ boolean b2 = loc1.cell.getCellStyle().getHidden();
+ if (b1 != b2) {
+ addMessage(loc1, loc2,
+ "Cell Visibility does not Match ::",
+ (b1 ? "" : "NOT ")+"HIDDEN",
+ (b2 ? "" : "NOT ")+"HIDDEN"
+ );
+ }
+ }
+
+ /**
+ * Checks if cell locked matches.
+ */
+ private void isCellLockedMatches(Locator loc1, Locator loc2) {
+ if (loc1.cell.getCellStyle() == null || loc2.cell.getCellStyle() == null) {
+ return;
+ }
+
+ boolean b1 = loc1.cell.getCellStyle().getLocked();
+ boolean b2 = loc1.cell.getCellStyle().getLocked();
+ if (b1 != b2) {
+ addMessage(loc1, loc2,
+ "Cell Protection does not Match ::",
+ (b1 ? "" : "NOT ")+"LOCKED",
+ (b2 ? "" : "NOT ")+"LOCKED"
+ );
+ }
+ }
+
+ /**
+ * Checks if cell type matches.
+ */
+ private boolean isCellTypeMatches(Locator loc1, Locator loc2) {
+ CellType type1 = loc1.cell.getCellType();
+ CellType type2 = loc2.cell.getCellType();
+ if (type1 == type2) {
+ return true;
+ }
+
+ addMessage(loc1, loc2,
+ "Cell Data-Type does not Match in :: ",
+ type1.name(), type2.name()
+ );
+ return false;
+ }
+
+ /**
+ * Checks if cell under line matches.
+ */
+ private void isCellUnderLineMatches(Locator loc1, Locator loc2) {
+ // TODO: distinguish underline type
+
+ if (!(loc1.cell instanceof XSSFCell) ||
+ loc1.cell.getCellStyle() == null || loc2.cell.getCellStyle() == null) {
+ return;
+ }
+
+ if(hasInvalidFontIndex(loc1, loc2)) {
+ return;
+ }
+
+ byte b1 = ((XSSFCell)loc1.cell).getCellStyle().getFont().getUnderline();
+ byte b2 = ((XSSFCell)loc2.cell).getCellStyle().getFont().getUnderline();
+ if (b1 != b2) {
+ addMessage(loc1, loc2,
+ CELL_FONT_ATTRIBUTES_DOES_NOT_MATCH,
+ (b1 == 1 ? "" : "NOT ")+"UNDERLINE",
+ (b2 == 1 ? "" : "NOT ")+"UNDERLINE"
+ );
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/examples/src/org/apache/poi/examples/ss/LinkedDropDownLists.java b/src/examples/src/org/apache/poi/examples/ss/LinkedDropDownLists.java
new file mode 100644
index 0000000000..5cbc63be60
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/ss/LinkedDropDownLists.java
@@ -0,0 +1,206 @@
+ /* ====================================================================
+ 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.examples.ss;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.DataValidation;
+import org.apache.poi.ss.usermodel.DataValidationConstraint;
+import org.apache.poi.ss.usermodel.DataValidationHelper;
+import org.apache.poi.ss.usermodel.Name;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.util.CellRangeAddressList;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+/**
+ * Demonstrates one technique that may be used to create linked or dependent
+ * drop down lists. This refers to a situation in which the selection made
+ * in one drop down list affects the options that are displayed in the second
+ * or subsequent drop down list(s). In this example, the value the user selects
+ * from the down list in cell A1 will affect the values displayed in the linked
+ * drop down list in cell B1. For the sake of simplicity, the data for the drop
+ * down lists is included on the same worksheet but this does not have to be the
+ * case; the data could appear on a separate sheet. If this were done, then the
+ * names for the regions would have to be different, they would have to include
+ * the name of the sheet.
+ *
+ * There are two keys to this technique. The first is the use of named area or
+ * regions of cells to hold the data for the drop down lists and the second is
+ * making use of the INDIRECT() function to convert a name into the addresses
+ * of the cells it refers to.
+ *
+ * Note that whilst this class builds just two linked drop down lists, there is
+ * nothing to prevent more being created. Quite simply, use the value selected
+ * by the user in one drop down list to determine what is shown in another and the
+ * value selected in that drop down list to determine what is shown in a third,
+ * and so on. Also, note that the data for the drop down lists is contained on
+ * contained on the same sheet as the validations themselves. This is done simply
+ * for simplicity and there is nothing to prevent a separate sheet being created
+ * and used to hold the data. If this is done then problems may be encountered
+ * if the sheet is opened with OpenOffice Calc. To prevent these problems, it is
+ * better to include the name of the sheet when calling the setRefersToFormula()
+ * method.
+ *
+ * @author Mark Beardsley [msb at apache.org]
+ * @version 1.00 30th March 2012
+ */
+public class LinkedDropDownLists {
+
+ LinkedDropDownLists(String workbookName) throws IOException {
+ // Using the ss.usermodel allows this class to support both binary
+ // and xml based workbooks. The choice of which one to create is
+ // made by checking the file extension.
+ try (Workbook workbook = workbookName.endsWith(".xlsx") ? new XSSFWorkbook() : new HSSFWorkbook()) {
+
+ // Build the sheet that will hold the data for the validations. This
+ // must be done first as it will create names that are referenced
+ // later.
+ Sheet sheet = workbook.createSheet("Linked Validations");
+ LinkedDropDownLists.buildDataSheet(sheet);
+
+ // Build the first data validation to occupy cell A1. Note
+ // that it retrieves it's data from the named area or region called
+ // CHOICES. Further information about this can be found in the
+ // static buildDataSheet() method below.
+ CellRangeAddressList addressList = new CellRangeAddressList(0, 0, 0, 0);
+ DataValidationHelper dvHelper = sheet.getDataValidationHelper();
+ DataValidationConstraint dvConstraint = dvHelper.createFormulaListConstraint("CHOICES");
+ DataValidation validation = dvHelper.createValidation(dvConstraint, addressList);
+ sheet.addValidationData(validation);
+
+ // Now, build the linked or dependent drop down list that will
+ // occupy cell B1. The key to the whole process is the use of the
+ // INDIRECT() function. In the buildDataSheet(0 method, a series of
+ // named regions are created and the names of three of them mirror
+ // the options available to the user in the first drop down list
+ // (in cell A1). Using the INDIRECT() function makes it possible
+ // to convert the selection the user makes in that first drop down
+ // into the addresses of a named region of cells and then to use
+ // those cells to populate the second drop down list.
+ addressList = new CellRangeAddressList(0, 0, 1, 1);
+ dvConstraint = dvHelper.createFormulaListConstraint(
+ "INDIRECT(UPPER($A$1))");
+ validation = dvHelper.createValidation(dvConstraint, addressList);
+ sheet.addValidationData(validation);
+
+ try (FileOutputStream fos = new FileOutputStream(workbookName)) {
+ workbook.write(fos);
+ }
+ }
+ }
+
+ /**
+ * Called to populate the named areas/regions. The contents of the cells on
+ * row one will be used to populate the first drop down list. The contents of
+ * the cells on rows two, three and four will be used to populate the second
+ * drop down list, just which row will be determined by the choice the user
+ * makes in the first drop down list.
+ *
+ * In all cases, the approach is to create a row, create and populate cells
+ * with data and then specify a name that identifies those cells. With the
+ * exception of the first range, the names that are chosen for each range
+ * of cells are quite important. In short, each of the options the user
+ * could select in the first drop down list is used as the name for another
+ * range of cells. Thus, in this example, the user can select either
+ * 'Animal', 'Vegetable' or 'Mineral' in the first drop down and so the
+ * sheet contains ranges named 'ANIMAL', 'VEGETABLE' and 'MINERAL'.
+ *
+ * @param dataSheet An instance of a class that implements the Sheet Sheet
+ * interface (HSSFSheet or XSSFSheet).
+ */
+ private static void buildDataSheet(Sheet dataSheet) {
+ Row row = null;
+ Cell cell = null;
+ Name name = null;
+
+ // The first row will hold the data for the first validation.
+ row = dataSheet.createRow(10);
+ cell = row.createCell(0);
+ cell.setCellValue("Animal");
+ cell = row.createCell(1);
+ cell.setCellValue("Vegetable");
+ cell = row.createCell(2);
+ cell.setCellValue("Mineral");
+ name = dataSheet.getWorkbook().createName();
+ name.setRefersToFormula("$A$11:$C$11");
+ name.setNameName("CHOICES");
+
+ // The next three rows will hold the data that will be used to
+ // populate the second, or linked, drop down list.
+ row = dataSheet.createRow(11);
+ cell = row.createCell(0);
+ cell.setCellValue("Lion");
+ cell = row.createCell(1);
+ cell.setCellValue("Tiger");
+ cell = row.createCell(2);
+ cell.setCellValue("Leopard");
+ cell = row.createCell(3);
+ cell.setCellValue("Elephant");
+ cell = row.createCell(4);
+ cell.setCellValue("Eagle");
+ cell = row.createCell(5);
+ cell.setCellValue("Horse");
+ cell = row.createCell(6);
+ cell.setCellValue("Zebra");
+ name = dataSheet.getWorkbook().createName();
+ name.setRefersToFormula("$A$12:$G$12");
+ name.setNameName("ANIMAL");
+
+ row = dataSheet.createRow(12);
+ cell = row.createCell(0);
+ cell.setCellValue("Cabbage");
+ cell = row.createCell(1);
+ cell.setCellValue("Cauliflower");
+ cell = row.createCell(2);
+ cell.setCellValue("Potato");
+ cell = row.createCell(3);
+ cell.setCellValue("Onion");
+ cell = row.createCell(4);
+ cell.setCellValue("Beetroot");
+ cell = row.createCell(5);
+ cell.setCellValue("Asparagus");
+ cell = row.createCell(6);
+ cell.setCellValue("Spinach");
+ cell = row.createCell(7);
+ cell.setCellValue("Chard");
+ name = dataSheet.getWorkbook().createName();
+ name.setRefersToFormula("$A$13:$H$13");
+ name.setNameName("VEGETABLE");
+
+ row = dataSheet.createRow(13);
+ cell = row.createCell(0);
+ cell.setCellValue("Bauxite");
+ cell = row.createCell(1);
+ cell.setCellValue("Quartz");
+ cell = row.createCell(2);
+ cell.setCellValue("Feldspar");
+ cell = row.createCell(3);
+ cell.setCellValue("Shist");
+ cell = row.createCell(4);
+ cell.setCellValue("Shale");
+ cell = row.createCell(5);
+ cell.setCellValue("Mica");
+ name = dataSheet.getWorkbook().createName();
+ name.setRefersToFormula("$A$14:$F$14");
+ name.setNameName("MINERAL");
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/ss/LoadEmbedded.java b/src/examples/src/org/apache/poi/examples/ss/LoadEmbedded.java
new file mode 100644
index 0000000000..3c52ecf253
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/ss/LoadEmbedded.java
@@ -0,0 +1,134 @@
+/* ====================================================================
+ 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.examples.ss;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.poi.EncryptedDocumentException;
+import org.apache.poi.hslf.usermodel.HSLFSlideShow;
+import org.apache.poi.hssf.usermodel.HSSFObjectData;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.hwpf.HWPFDocument;
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.openxml4j.opc.PackagePart;
+import org.apache.poi.poifs.filesystem.DirectoryNode;
+import org.apache.poi.poifs.filesystem.Entry;
+import org.apache.poi.sl.usermodel.SlideShow;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.usermodel.WorkbookFactory;
+import org.apache.poi.xslf.usermodel.XMLSlideShow;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.apache.poi.xwpf.usermodel.XWPFDocument;
+import org.apache.xmlbeans.XmlException;
+
+/**
+ * Loads embedded resources from Workbooks. Code taken from the website:
+ * https://poi.apache.org/spreadsheet/quick-guide.html#Embedded
+ */
+@SuppressWarnings({"java:S106","java:S4823"})
+public final class LoadEmbedded {
+ private LoadEmbedded() {}
+
+ public static void main(String[] args) throws IOException, EncryptedDocumentException, OpenXML4JException, XmlException {
+ Workbook wb = WorkbookFactory.create(new File(args[0]));
+ loadEmbedded(wb);
+ }
+
+ public static void loadEmbedded(Workbook wb) throws IOException, InvalidFormatException, OpenXML4JException, XmlException {
+ if (wb instanceof HSSFWorkbook) {
+ loadEmbedded((HSSFWorkbook)wb);
+ }
+ else if (wb instanceof XSSFWorkbook) {
+ loadEmbedded((XSSFWorkbook)wb);
+ }
+ else {
+ throw new IllegalArgumentException(wb.getClass().getName());
+ }
+ }
+
+ public static void loadEmbedded(HSSFWorkbook workbook) throws IOException {
+ for (HSSFObjectData obj : workbook.getAllEmbeddedObjects()) {
+ //the OLE2 Class Name of the object
+ String oleName = obj.getOLE2ClassName();
+ if (oleName.equals("Worksheet")) {
+ DirectoryNode dn = (DirectoryNode) obj.getDirectory();
+ HSSFWorkbook embeddedWorkbook = new HSSFWorkbook(dn, false);
+ embeddedWorkbook.close();
+ } else if (oleName.equals("Document")) {
+ DirectoryNode dn = (DirectoryNode) obj.getDirectory();
+ HWPFDocument embeddedWordDocument = new HWPFDocument(dn);
+ embeddedWordDocument.close();
+ } else if (oleName.equals("Presentation")) {
+ DirectoryNode dn = (DirectoryNode) obj.getDirectory();
+ SlideShow,?> embeddedSlieShow = new HSLFSlideShow(dn);
+ embeddedSlieShow.close();
+ } else {
+ if(obj.hasDirectoryEntry()){
+ // The DirectoryEntry is a DocumentNode. Examine its entries to find out what it is
+ DirectoryNode dn = (DirectoryNode) obj.getDirectory();
+ for (Entry entry : dn) {
+ //System.out.println(oleName + "." + entry.getName());
+ }
+ } else {
+ // There is no DirectoryEntry
+ // Recover the object's data from the HSSFObjectData instance.
+ byte[] objectData = obj.getObjectData();
+ }
+ }
+ }
+ }
+
+ public static void loadEmbedded(XSSFWorkbook workbook) throws IOException, InvalidFormatException, OpenXML4JException, XmlException {
+ for (PackagePart pPart : workbook.getAllEmbeddedParts()) {
+ String contentType = pPart.getContentType();
+ if (contentType.equals("application/vnd.ms-excel")) {
+ // Excel Workbook - either binary or OpenXML
+ HSSFWorkbook embeddedWorkbook = new HSSFWorkbook(pPart.getInputStream());
+ embeddedWorkbook.close();
+ } else if (contentType.equals("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")) {
+ // Excel Workbook - OpenXML file format
+ XSSFWorkbook embeddedWorkbook = new XSSFWorkbook(pPart.getInputStream());
+ embeddedWorkbook.close();
+ } else if (contentType.equals("application/msword")) {
+ // Word Document - binary (OLE2CDF) file format
+ HWPFDocument document = new HWPFDocument(pPart.getInputStream());
+ document.close();
+ } else if (contentType.equals("application/vnd.openxmlformats-officedocument.wordprocessingml.document")) {
+ // Word Document - OpenXML file format
+ XWPFDocument document = new XWPFDocument(pPart.getInputStream());
+ document.close();
+ } else if (contentType.equals("application/vnd.ms-powerpoint")) {
+ // PowerPoint Document - binary file format
+ HSLFSlideShow slideShow = new HSLFSlideShow(pPart.getInputStream());
+ slideShow.close();
+ } else if (contentType.equals("application/vnd.openxmlformats-officedocument.presentationml.presentation")) {
+ // PowerPoint Document - OpenXML file format
+ XMLSlideShow slideShow = new XMLSlideShow(pPart.getInputStream());
+ slideShow.close();
+ } else {
+ // Any other type of embedded object.
+ System.out.println("Unknown Embedded Document: " + contentType);
+ InputStream inputStream = pPart.getInputStream();
+ inputStream.close();
+ }
+ }
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/ss/LoanCalculator.java b/src/examples/src/org/apache/poi/examples/ss/LoanCalculator.java
new file mode 100644
index 0000000000..28d1c648f0
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/ss/LoanCalculator.java
@@ -0,0 +1,319 @@
+/* ====================================================================
+ 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.examples.ss;
+
+import java.io.FileOutputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.BorderStyle;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellStyle;
+import org.apache.poi.ss.usermodel.FillPatternType;
+import org.apache.poi.ss.usermodel.Font;
+import org.apache.poi.ss.usermodel.HorizontalAlignment;
+import org.apache.poi.ss.usermodel.IndexedColors;
+import org.apache.poi.ss.usermodel.Name;
+import org.apache.poi.ss.usermodel.PrintSetup;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+/**
+ * Simple Loan Calculator. Demonstrates advance usage of cell formulas and named ranges.
+ *
+ * Usage:
+ * LoanCalculator -xls|xlsx
+ *
+ * @author Yegor Kozlov
+ */
+@SuppressWarnings({"java:S106","java:S4823","java:S1192"})
+public final class LoanCalculator {
+
+ private LoanCalculator() {}
+
+ public static void main(String[] args) throws Exception {
+ Workbook wb;
+
+ if(args.length > 0 && args[0].equals("-xls")) wb = new HSSFWorkbook();
+ else wb = new XSSFWorkbook();
+
+ Map styles = createStyles(wb);
+ Sheet sheet = wb.createSheet("Loan Calculator");
+ sheet.setPrintGridlines(false);
+ sheet.setDisplayGridlines(false);
+
+ PrintSetup printSetup = sheet.getPrintSetup();
+ printSetup.setLandscape(true);
+ sheet.setFitToPage(true);
+ sheet.setHorizontallyCenter(true);
+
+ sheet.setColumnWidth(0, 3*256);
+ sheet.setColumnWidth(1, 3*256);
+ sheet.setColumnWidth(2, 11*256);
+ sheet.setColumnWidth(3, 14*256);
+ sheet.setColumnWidth(4, 14*256);
+ sheet.setColumnWidth(5, 14*256);
+ sheet.setColumnWidth(6, 14*256);
+
+ createNames(wb);
+
+ Row titleRow = sheet.createRow(0);
+ titleRow.setHeightInPoints(35);
+ for (int i = 1; i <= 7; i++) {
+ titleRow.createCell(i).setCellStyle(styles.get("title"));
+ }
+ Cell titleCell = titleRow.getCell(2);
+ titleCell.setCellValue("Simple Loan Calculator");
+ sheet.addMergedRegion(CellRangeAddress.valueOf("$C$1:$H$1"));
+
+ Row row = sheet.createRow(2);
+ Cell cell = row.createCell(4);
+ cell.setCellValue("Enter values");
+ cell.setCellStyle(styles.get("item_right"));
+
+ row = sheet.createRow(3);
+ cell = row.createCell(2);
+ cell.setCellValue("Loan amount");
+ cell.setCellStyle(styles.get("item_left"));
+ cell = row.createCell(4);
+ cell.setCellStyle(styles.get("input_$"));
+ cell.setAsActiveCell();
+
+ row = sheet.createRow(4);
+ cell = row.createCell(2);
+ cell.setCellValue("Annual interest rate");
+ cell.setCellStyle(styles.get("item_left"));
+ cell = row.createCell(4);
+ cell.setCellStyle(styles.get("input_%"));
+
+ row = sheet.createRow(5);
+ cell = row.createCell(2);
+ cell.setCellValue("Loan period in years");
+ cell.setCellStyle(styles.get("item_left"));
+ cell = row.createCell(4);
+ cell.setCellStyle(styles.get("input_i"));
+
+ row = sheet.createRow(6);
+ cell = row.createCell(2);
+ cell.setCellValue("Start date of loan");
+ cell.setCellStyle(styles.get("item_left"));
+ cell = row.createCell(4);
+ cell.setCellStyle(styles.get("input_d"));
+
+ row = sheet.createRow(8);
+ cell = row.createCell(2);
+ cell.setCellValue("Monthly payment");
+ cell.setCellStyle(styles.get("item_left"));
+ cell = row.createCell(4);
+ cell.setCellFormula("IF(Values_Entered,Monthly_Payment,\"\")");
+ cell.setCellStyle(styles.get("formula_$"));
+
+ row = sheet.createRow(9);
+ cell = row.createCell(2);
+ cell.setCellValue("Number of payments");
+ cell.setCellStyle(styles.get("item_left"));
+ cell = row.createCell(4);
+ cell.setCellFormula("IF(Values_Entered,Loan_Years*12,\"\")");
+ cell.setCellStyle(styles.get("formula_i"));
+
+ row = sheet.createRow(10);
+ cell = row.createCell(2);
+ cell.setCellValue("Total interest");
+ cell.setCellStyle(styles.get("item_left"));
+ cell = row.createCell(4);
+ cell.setCellFormula("IF(Values_Entered,Total_Cost-Loan_Amount,\"\")");
+ cell.setCellStyle(styles.get("formula_$"));
+
+ row = sheet.createRow(11);
+ cell = row.createCell(2);
+ cell.setCellValue("Total cost of loan");
+ cell.setCellStyle(styles.get("item_left"));
+ cell = row.createCell(4);
+ cell.setCellFormula("IF(Values_Entered,Monthly_Payment*Number_of_Payments,\"\")");
+ cell.setCellStyle(styles.get("formula_$"));
+
+
+ // Write the output to a file
+ String file = "loan-calculator.xls";
+ if(wb instanceof XSSFWorkbook) file += "x";
+ FileOutputStream out = new FileOutputStream(file);
+ wb.write(out);
+ out.close();
+ }
+
+ /**
+ * cell styles used for formatting calendar sheets
+ */
+ private static Map createStyles(Workbook wb){
+ Map styles = new HashMap<>();
+
+ CellStyle style;
+ Font titleFont = wb.createFont();
+ titleFont.setFontHeightInPoints((short)14);
+ titleFont.setFontName("Trebuchet MS");
+ style = wb.createCellStyle();
+ style.setFont(titleFont);
+ style.setBorderBottom(BorderStyle.DOTTED);
+ style.setBottomBorderColor(IndexedColors.GREY_40_PERCENT.getIndex());
+ styles.put("title", style);
+
+ Font itemFont = wb.createFont();
+ itemFont.setFontHeightInPoints((short)9);
+ itemFont.setFontName("Trebuchet MS");
+ style = wb.createCellStyle();
+ style.setAlignment(HorizontalAlignment.LEFT);
+ style.setFont(itemFont);
+ styles.put("item_left", style);
+
+ style = wb.createCellStyle();
+ style.setAlignment(HorizontalAlignment.RIGHT);
+ style.setFont(itemFont);
+ styles.put("item_right", style);
+
+ style = wb.createCellStyle();
+ style.setAlignment(HorizontalAlignment.RIGHT);
+ style.setFont(itemFont);
+ style.setBorderRight(BorderStyle.DOTTED);
+ style.setRightBorderColor(IndexedColors.GREY_40_PERCENT.getIndex());
+ style.setBorderBottom(BorderStyle.DOTTED);
+ style.setBottomBorderColor(IndexedColors.GREY_40_PERCENT.getIndex());
+ style.setBorderLeft(BorderStyle.DOTTED);
+ style.setLeftBorderColor(IndexedColors.GREY_40_PERCENT.getIndex());
+ style.setBorderTop(BorderStyle.DOTTED);
+ style.setTopBorderColor(IndexedColors.GREY_40_PERCENT.getIndex());
+ style.setDataFormat(wb.createDataFormat().getFormat("_($* #,##0.00_);_($* (#,##0.00);_($* \"-\"??_);_(@_)"));
+ styles.put("input_$", style);
+
+ style = wb.createCellStyle();
+ style.setAlignment(HorizontalAlignment.RIGHT);
+ style.setFont(itemFont);
+ style.setBorderRight(BorderStyle.DOTTED);
+ style.setRightBorderColor(IndexedColors.GREY_40_PERCENT.getIndex());
+ style.setBorderBottom(BorderStyle.DOTTED);
+ style.setBottomBorderColor(IndexedColors.GREY_40_PERCENT.getIndex());
+ style.setBorderLeft(BorderStyle.DOTTED);
+ style.setLeftBorderColor(IndexedColors.GREY_40_PERCENT.getIndex());
+ style.setBorderTop(BorderStyle.DOTTED);
+ style.setTopBorderColor(IndexedColors.GREY_40_PERCENT.getIndex());
+ style.setDataFormat(wb.createDataFormat().getFormat("0.000%"));
+ styles.put("input_%", style);
+
+ style = wb.createCellStyle();
+ style.setAlignment(HorizontalAlignment.RIGHT);
+ style.setFont(itemFont);
+ style.setBorderRight(BorderStyle.DOTTED);
+ style.setRightBorderColor(IndexedColors.GREY_40_PERCENT.getIndex());
+ style.setBorderBottom(BorderStyle.DOTTED);
+ style.setBottomBorderColor(IndexedColors.GREY_40_PERCENT.getIndex());
+ style.setBorderLeft(BorderStyle.DOTTED);
+ style.setLeftBorderColor(IndexedColors.GREY_40_PERCENT.getIndex());
+ style.setBorderTop(BorderStyle.DOTTED);
+ style.setTopBorderColor(IndexedColors.GREY_40_PERCENT.getIndex());
+ style.setDataFormat(wb.createDataFormat().getFormat("0"));
+ styles.put("input_i", style);
+
+ style = wb.createCellStyle();
+ style.setAlignment(HorizontalAlignment.CENTER);
+ style.setFont(itemFont);
+ style.setDataFormat(wb.createDataFormat().getFormat("m/d/yy"));
+ styles.put("input_d", style);
+
+ style = wb.createCellStyle();
+ style.setAlignment(HorizontalAlignment.RIGHT);
+ style.setFont(itemFont);
+ style.setBorderRight(BorderStyle.DOTTED);
+ style.setRightBorderColor(IndexedColors.GREY_40_PERCENT.getIndex());
+ style.setBorderBottom(BorderStyle.DOTTED);
+ style.setBottomBorderColor(IndexedColors.GREY_40_PERCENT.getIndex());
+ style.setBorderLeft(BorderStyle.DOTTED);
+ style.setLeftBorderColor(IndexedColors.GREY_40_PERCENT.getIndex());
+ style.setBorderTop(BorderStyle.DOTTED);
+ style.setTopBorderColor(IndexedColors.GREY_40_PERCENT.getIndex());
+ style.setDataFormat(wb.createDataFormat().getFormat("$##,##0.00"));
+ style.setBorderBottom(BorderStyle.DOTTED);
+ style.setBottomBorderColor(IndexedColors.GREY_40_PERCENT.getIndex());
+ style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
+ style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+ styles.put("formula_$", style);
+
+ style = wb.createCellStyle();
+ style.setAlignment(HorizontalAlignment.RIGHT);
+ style.setFont(itemFont);
+ style.setBorderRight(BorderStyle.DOTTED);
+ style.setRightBorderColor(IndexedColors.GREY_40_PERCENT.getIndex());
+ style.setBorderBottom(BorderStyle.DOTTED);
+ style.setBottomBorderColor(IndexedColors.GREY_40_PERCENT.getIndex());
+ style.setBorderLeft(BorderStyle.DOTTED);
+ style.setLeftBorderColor(IndexedColors.GREY_40_PERCENT.getIndex());
+ style.setBorderTop(BorderStyle.DOTTED);
+ style.setTopBorderColor(IndexedColors.GREY_40_PERCENT.getIndex());
+ style.setDataFormat(wb.createDataFormat().getFormat("0"));
+ style.setBorderBottom(BorderStyle.DOTTED);
+ style.setBottomBorderColor(IndexedColors.GREY_40_PERCENT.getIndex());
+ style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
+ style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+ styles.put("formula_i", style);
+
+ return styles;
+ }
+
+ //define named ranges for the inputs and formulas
+ public static void createNames(Workbook wb){
+ Name name;
+
+ name = wb.createName();
+ name.setNameName("Interest_Rate");
+ name.setRefersToFormula("'Loan Calculator'!$E$5");
+
+ name = wb.createName();
+ name.setNameName("Loan_Amount");
+ name.setRefersToFormula("'Loan Calculator'!$E$4");
+
+ name = wb.createName();
+ name.setNameName("Loan_Start");
+ name.setRefersToFormula("'Loan Calculator'!$E$7");
+
+ name = wb.createName();
+ name.setNameName("Loan_Years");
+ name.setRefersToFormula("'Loan Calculator'!$E$6");
+
+ name = wb.createName();
+ name.setNameName("Number_of_Payments");
+ name.setRefersToFormula("'Loan Calculator'!$E$10");
+
+ name = wb.createName();
+ name.setNameName("Monthly_Payment");
+ name.setRefersToFormula("-PMT(Interest_Rate/12,Number_of_Payments,Loan_Amount)");
+
+ name = wb.createName();
+ name.setNameName("Total_Cost");
+ name.setRefersToFormula("'Loan Calculator'!$E$12");
+
+ name = wb.createName();
+ name.setNameName("Total_Interest");
+ name.setRefersToFormula("'Loan Calculator'!$E$11");
+
+ name = wb.createName();
+ name.setNameName("Values_Entered");
+ name.setRefersToFormula("IF(Loan_Amount*Interest_Rate*Loan_Years*Loan_Start>0,1,0)");
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/ss/SSPerformanceTest.java b/src/examples/src/org/apache/poi/examples/ss/SSPerformanceTest.java
new file mode 100644
index 0000000000..a5b7eb7170
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/ss/SSPerformanceTest.java
@@ -0,0 +1,254 @@
+/*
+ * ====================================================================
+ * 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.examples.ss;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ooxml.POIXMLTypeLoader;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellStyle;
+import org.apache.poi.ss.usermodel.FillPatternType;
+import org.apache.poi.ss.usermodel.Font;
+import org.apache.poi.ss.usermodel.HorizontalAlignment;
+import org.apache.poi.ss.usermodel.IndexedColors;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.VerticalAlignment;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.ss.util.CellReference;
+import org.apache.poi.xssf.streaming.SXSSFWorkbook;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+@SuppressWarnings({"java:S106","java:S4823","java:S1192"})
+public final class SSPerformanceTest {
+ private SSPerformanceTest() {}
+
+ public static void main(String[] args) throws IOException {
+ if (args.length < 4) {
+ usage("need at least four command arguments");
+ }
+
+ String type = args[0];
+ int rows = parseInt(args[1], "Failed to parse rows value as integer");
+ int cols = parseInt(args[2], "Failed to parse cols value as integer");
+ boolean saveFile = parseInt(args[3], "Failed to parse saveFile value as integer") != 0;
+
+ boolean warmup = false;
+ for(int arg = 4; arg < args.length;arg++) {
+ if(args[arg].equals("--unsynchronized-xmlbeans")) {
+ POIXMLTypeLoader.DEFAULT_XML_OPTIONS.setUnsynchronized();
+ }
+ if(args[arg].equals("--with-warmup-run")) {
+ warmup = true;
+ }
+ }
+
+ if(warmup) {
+ System.out.println("Performing a warmup run first");
+ runWithArgs(type, rows, cols, saveFile);
+ }
+
+ long timeStarted = System.currentTimeMillis();
+ runWithArgs(type, rows, cols, saveFile);
+ long timeFinished = System.currentTimeMillis();
+
+ System.out.printf("Elapsed %.2f seconds for arguments %s\n", ((double)timeFinished - timeStarted) / 1000, Arrays.toString(args));
+ }
+
+ private static void runWithArgs(String type, int rows, int cols, boolean saveFile) throws IOException {
+ try (Workbook workBook = createWorkbook(type)) {
+ boolean isHType = workBook instanceof HSSFWorkbook;
+ addContent(workBook, isHType, rows, cols);
+
+ if (saveFile) {
+ String fileName = type + "_" + rows + "_" + cols + "." + getFileSuffix(type);
+ saveFile(workBook, fileName);
+ }
+ }
+ }
+
+ private static void addContent(Workbook workBook, boolean isHType, int rows, int cols) {
+ Map styles = createStyles(workBook);
+
+ Sheet sheet = workBook.createSheet("Main Sheet");
+
+ Cell headerCell = sheet.createRow(0).createCell(0);
+ headerCell.setCellValue("Header text is spanned across multiple cells");
+ headerCell.setCellStyle(styles.get("header"));
+ sheet.addMergedRegion(CellRangeAddress.valueOf("$A$1:$F$1"));
+
+ int sheetNo = 0;
+ int rowIndexInSheet = 1;
+ double value = 0;
+ Calendar calendar = Calendar.getInstance();
+ for (int rowIndex = 0; rowIndex < rows; rowIndex++) {
+ if (isHType && sheetNo != rowIndex / 0x10000) {
+ sheet = workBook.createSheet("Spillover from sheet " + (++sheetNo));
+ headerCell.setCellValue("Header text is spanned across multiple cells");
+ headerCell.setCellStyle(styles.get("header"));
+ sheet.addMergedRegion(CellRangeAddress.valueOf("$A$1:$F$1"));
+ rowIndexInSheet = 1;
+ }
+
+ Row row = sheet.createRow(rowIndexInSheet);
+ for (int colIndex = 0; colIndex < cols; colIndex++) {
+ value = populateCell(styles, value, calendar, rowIndex, row, colIndex);
+ }
+ rowIndexInSheet++;
+ }
+ }
+
+ private static double populateCell(Map styles, double value, Calendar calendar, int rowIndex, Row row, int colIndex) {
+ Cell cell = row.createCell(colIndex);
+ String address = new CellReference(cell).formatAsString();
+ switch (colIndex){
+ case 0:
+ // column A: default number format
+ cell.setCellValue(value++);
+ break;
+ case 1:
+ // column B: #,##0
+ cell.setCellValue(value++);
+ cell.setCellStyle(styles.get("#,##0.00"));
+ break;
+ case 2:
+ // column C: $#,##0.00
+ cell.setCellValue(value++);
+ cell.setCellStyle(styles.get("$#,##0.00"));
+ break;
+ case 3:
+ // column D: red bold text on yellow background
+ cell.setCellValue(address);
+ cell.setCellStyle(styles.get("red-bold"));
+ break;
+ case 4:
+ // column E: boolean
+ // TODO booleans are shown as 1/0 instead of TRUE/FALSE
+ cell.setCellValue(rowIndex % 2 == 0);
+ break;
+ case 5:
+ // column F: date / time
+ cell.setCellValue(calendar);
+ cell.setCellStyle(styles.get("m/d/yyyy"));
+ calendar.roll(Calendar.DAY_OF_YEAR, -1);
+ break;
+ case 6:
+ // column F: formula
+ // TODO formulas are not yet supported in SXSSF
+ //cell.setCellFormula("SUM(A" + (rowIndex+1) + ":E" + (rowIndex+1)+ ")");
+ //break;
+ default:
+ cell.setCellValue(value++);
+ break;
+ }
+ return value;
+ }
+
+ private static void saveFile(Workbook workBook, String fileName) {
+ try {
+ FileOutputStream out = new FileOutputStream(fileName);
+ workBook.write(out);
+ out.close();
+ } catch (IOException ioe) {
+ System.err.println("Error: failed to write to file \"" + fileName + "\", reason=" + ioe.getMessage());
+ }
+ }
+
+ static Map createStyles(Workbook wb) {
+ Map styles = new HashMap<>();
+ CellStyle style;
+
+ Font headerFont = wb.createFont();
+ headerFont.setFontHeightInPoints((short) 14);
+ headerFont.setBold(true);
+ style = wb.createCellStyle();
+ style.setAlignment(HorizontalAlignment.CENTER);
+ style.setVerticalAlignment(VerticalAlignment.CENTER);
+ style.setFont(headerFont);
+ style.setFillForegroundColor(IndexedColors.LIGHT_CORNFLOWER_BLUE.getIndex());
+ style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+ styles.put("header", style);
+
+ Font monthFont = wb.createFont();
+ monthFont.setFontHeightInPoints((short)12);
+ monthFont.setColor(IndexedColors.RED.getIndex());
+ monthFont.setBold(true);
+ style = wb.createCellStyle();
+ style.setAlignment(HorizontalAlignment.CENTER);
+ style.setVerticalAlignment(VerticalAlignment.CENTER);
+ style.setFillForegroundColor(IndexedColors.YELLOW.getIndex());
+ style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+ style.setFont(monthFont);
+ styles.put("red-bold", style);
+
+ String[] nfmt = {"#,##0.00", "$#,##0.00", "m/d/yyyy"};
+ for(String fmt : nfmt){
+ style = wb.createCellStyle();
+ style.setDataFormat(wb.createDataFormat().getFormat(fmt));
+ styles.put(fmt, style);
+ }
+
+ return styles;
+ }
+
+
+ static void usage(String message) {
+ System.err.println(message);
+ System.err.println("usage: java SSPerformanceTest HSSF|XSSF|SXSSF rows cols saveFile (0|1)? [--unsynchronized-xmlbeans] [--with-warmup-run]");
+ System.exit(1);
+ }
+
+ static Workbook createWorkbook(String type) {
+ if ("HSSF".equals(type))
+ return new HSSFWorkbook();
+ else if ("XSSF".equals(type))
+ return new XSSFWorkbook();
+ else if ("SXSSF".equals(type))
+ return new SXSSFWorkbook();
+
+ usage("Unknown type \"" + type + "\"");
+ throw new IllegalArgumentException("Should not reach this point");
+ }
+
+ static String getFileSuffix(String type) {
+ if ("HSSF".equals(type))
+ return "xls";
+ else if ("XSSF".equals(type))
+ return "xlsx";
+ else if ("SXSSF".equals(type))
+ return "xlsx";
+ return null;
+ }
+
+ static int parseInt(String value, String msg) {
+ try {
+ return Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ usage(msg);
+ }
+ return 0;
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/ss/TimesheetDemo.java b/src/examples/src/org/apache/poi/examples/ss/TimesheetDemo.java
new file mode 100644
index 0000000000..77b55c826d
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/ss/TimesheetDemo.java
@@ -0,0 +1,234 @@
+/* ====================================================================
+ 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.examples.ss;
+
+import java.io.FileOutputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.BorderStyle;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellStyle;
+import org.apache.poi.ss.usermodel.FillPatternType;
+import org.apache.poi.ss.usermodel.Font;
+import org.apache.poi.ss.usermodel.HorizontalAlignment;
+import org.apache.poi.ss.usermodel.IndexedColors;
+import org.apache.poi.ss.usermodel.PrintSetup;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.VerticalAlignment;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+/**
+ * A weekly timesheet created using Apache POI.
+ * Usage:
+ * TimesheetDemo -xls|xlsx
+ *
+ * @author Yegor Kozlov
+ */
+@SuppressWarnings({"java:S106","java:S4823","java:S1192"})
+public final class TimesheetDemo {
+ private static final String[] titles = {
+ "Person", "ID", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun",
+ "Total\nHrs", "Overtime\nHrs", "Regular\nHrs"
+ };
+
+ private static final Object[][] sample_data = {
+ {"Yegor Kozlov", "YK", 5.0, 8.0, 10.0, 5.0, 5.0, 7.0, 6.0},
+ {"Gisella Bronzetti", "GB", 4.0, 3.0, 1.0, 3.5, null, null, 4.0},
+ };
+
+ private TimesheetDemo() {}
+
+ public static void main(String[] args) throws Exception {
+ Workbook wb;
+
+ if(args.length > 0 && args[0].equals("-xls")) wb = new HSSFWorkbook();
+ else wb = new XSSFWorkbook();
+
+ Map styles = createStyles(wb);
+
+ Sheet sheet = wb.createSheet("Timesheet");
+ PrintSetup printSetup = sheet.getPrintSetup();
+ printSetup.setLandscape(true);
+ sheet.setFitToPage(true);
+ sheet.setHorizontallyCenter(true);
+
+ //title row
+ Row titleRow = sheet.createRow(0);
+ titleRow.setHeightInPoints(45);
+ Cell titleCell = titleRow.createCell(0);
+ titleCell.setCellValue("Weekly Timesheet");
+ titleCell.setCellStyle(styles.get("title"));
+ sheet.addMergedRegion(CellRangeAddress.valueOf("$A$1:$L$1"));
+
+ //header row
+ Row headerRow = sheet.createRow(1);
+ headerRow.setHeightInPoints(40);
+ Cell headerCell;
+ for (int i = 0; i < titles.length; i++) {
+ headerCell = headerRow.createCell(i);
+ headerCell.setCellValue(titles[i]);
+ headerCell.setCellStyle(styles.get("header"));
+ }
+
+ int rownum = 2;
+ for (int i = 0; i < 10; i++) {
+ Row row = sheet.createRow(rownum++);
+ for (int j = 0; j < titles.length; j++) {
+ Cell cell = row.createCell(j);
+ if(j == 9){
+ //the 10th cell contains sum over week days, e.g. SUM(C3:I3)
+ String ref = "C" +rownum+ ":I" + rownum;
+ cell.setCellFormula("SUM("+ref+")");
+ cell.setCellStyle(styles.get("formula"));
+ } else if (j == 11){
+ cell.setCellFormula("J" +rownum+ "-K" + rownum);
+ cell.setCellStyle(styles.get("formula"));
+ } else {
+ cell.setCellStyle(styles.get("cell"));
+ }
+ }
+ }
+
+ //row with totals below
+ Row sumRow = sheet.createRow(rownum++);
+ sumRow.setHeightInPoints(35);
+ Cell cell;
+ cell = sumRow.createCell(0);
+ cell.setCellStyle(styles.get("formula"));
+ cell = sumRow.createCell(1);
+ cell.setCellValue("Total Hrs:");
+ cell.setCellStyle(styles.get("formula"));
+
+ for (int j = 2; j < 12; j++) {
+ cell = sumRow.createCell(j);
+ String ref = (char)('A' + j) + "3:" + (char)('A' + j) + "12";
+ cell.setCellFormula("SUM(" + ref + ")");
+ if(j >= 9) cell.setCellStyle(styles.get("formula_2"));
+ else cell.setCellStyle(styles.get("formula"));
+ }
+ rownum++;
+ sumRow = sheet.createRow(rownum++);
+ sumRow.setHeightInPoints(25);
+ cell = sumRow.createCell(0);
+ cell.setCellValue("Total Regular Hours");
+ cell.setCellStyle(styles.get("formula"));
+ cell = sumRow.createCell(1);
+ cell.setCellFormula("L13");
+ cell.setCellStyle(styles.get("formula_2"));
+ sumRow = sheet.createRow(rownum++);
+ sumRow.setHeightInPoints(25);
+ cell = sumRow.createCell(0);
+ cell.setCellValue("Total Overtime Hours");
+ cell.setCellStyle(styles.get("formula"));
+ cell = sumRow.createCell(1);
+ cell.setCellFormula("K13");
+ cell.setCellStyle(styles.get("formula_2"));
+
+ //set sample data
+ for (int i = 0; i < sample_data.length; i++) {
+ Row row = sheet.getRow(2 + i);
+ for (int j = 0; j < sample_data[i].length; j++) {
+ if(sample_data[i][j] == null) continue;
+
+ if(sample_data[i][j] instanceof String) {
+ row.getCell(j).setCellValue((String)sample_data[i][j]);
+ } else {
+ row.getCell(j).setCellValue((Double)sample_data[i][j]);
+ }
+ }
+ }
+
+ //finally set column widths, the width is measured in units of 1/256th of a character width
+ sheet.setColumnWidth(0, 30*256); //30 characters wide
+ for (int i = 2; i < 9; i++) {
+ sheet.setColumnWidth(i, 6*256); //6 characters wide
+ }
+ sheet.setColumnWidth(10, 10*256); //10 characters wide
+
+ // Write the output to a file
+ String file = "timesheet.xls";
+ if(wb instanceof XSSFWorkbook) file += "x";
+ FileOutputStream out = new FileOutputStream(file);
+ wb.write(out);
+ out.close();
+ }
+
+ /**
+ * Create a library of cell styles
+ */
+ private static Map createStyles(Workbook wb){
+ Map styles = new HashMap<>();
+ CellStyle style;
+ Font titleFont = wb.createFont();
+ titleFont.setFontHeightInPoints((short)18);
+ titleFont.setBold(true);
+ style = wb.createCellStyle();
+ style.setAlignment(HorizontalAlignment.CENTER);
+ style.setVerticalAlignment(VerticalAlignment.CENTER);
+ style.setFont(titleFont);
+ styles.put("title", style);
+
+ Font monthFont = wb.createFont();
+ monthFont.setFontHeightInPoints((short)11);
+ monthFont.setColor(IndexedColors.WHITE.getIndex());
+ style = wb.createCellStyle();
+ style.setAlignment(HorizontalAlignment.CENTER);
+ style.setVerticalAlignment(VerticalAlignment.CENTER);
+ style.setFillForegroundColor(IndexedColors.GREY_50_PERCENT.getIndex());
+ style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+ style.setFont(monthFont);
+ style.setWrapText(true);
+ styles.put("header", style);
+
+ style = wb.createCellStyle();
+ style.setAlignment(HorizontalAlignment.CENTER);
+ style.setWrapText(true);
+ style.setBorderRight(BorderStyle.THIN);
+ style.setRightBorderColor(IndexedColors.BLACK.getIndex());
+ style.setBorderLeft(BorderStyle.THIN);
+ style.setLeftBorderColor(IndexedColors.BLACK.getIndex());
+ style.setBorderTop(BorderStyle.THIN);
+ style.setTopBorderColor(IndexedColors.BLACK.getIndex());
+ style.setBorderBottom(BorderStyle.THIN);
+ style.setBottomBorderColor(IndexedColors.BLACK.getIndex());
+ styles.put("cell", style);
+
+ style = wb.createCellStyle();
+ style.setAlignment(HorizontalAlignment.CENTER);
+ style.setVerticalAlignment(VerticalAlignment.CENTER);
+ style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
+ style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+ style.setDataFormat(wb.createDataFormat().getFormat("0.00"));
+ styles.put("formula", style);
+
+ style = wb.createCellStyle();
+ style.setAlignment(HorizontalAlignment.CENTER);
+ style.setVerticalAlignment(VerticalAlignment.CENTER);
+ style.setFillForegroundColor(IndexedColors.GREY_40_PERCENT.getIndex());
+ style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+ style.setDataFormat(wb.createDataFormat().getFormat("0.00"));
+ styles.put("formula_2", style);
+
+ return styles;
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/ss/ToCSV.java b/src/examples/src/org/apache/poi/examples/ss/ToCSV.java
new file mode 100644
index 0000000000..f334667505
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/ss/ToCSV.java
@@ -0,0 +1,741 @@
+/* ====================================================================
+ 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.examples.ss;
+
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileWriter;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.util.ArrayList;
+
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellType;
+import org.apache.poi.ss.usermodel.DataFormatter;
+import org.apache.poi.ss.usermodel.FormulaEvaluator;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.usermodel.WorkbookFactory;
+
+/**
+ * Demonstrates one way to convert an Excel spreadsheet into a CSV
+ * file. This class makes the following assumptions;
+ *
+ *
1. Where the Excel workbook contains more that one worksheet, then a single
+ * CSV file will contain the data from all of the worksheets.
+ *
2. The data matrix contained in the CSV file will be square. This means that
+ * the number of fields in each record of the CSV file will match the number
+ * of cells in the longest row found in the Excel workbook. Any short records
+ * will be 'padded' with empty fields - an empty field is represented in the
+ * the CSV file in this way - ,,.
+ *
3. Empty fields will represent missing cells.
+ *
4. A record consisting of empty fields will be used to represent an empty row
+ * in the Excel workbook.
+ *
+ * Therefore, if the worksheet looked like this;
+ *
+ *
+ * Typically, the comma is used to separate each of the fields that, together,
+ * constitute a single record or line within the CSV file. This is not however
+ * a hard and fast rule and so this class allows the user to determine which
+ * character is used as the field separator and assumes the comma if none other
+ * is specified.
+ *
+ * If a field contains the separator then it will be escaped. If the file should
+ * obey Excel's CSV formatting rules, then the field will be surrounded with
+ * speech marks whilst if it should obey UNIX conventions, each occurrence of
+ * the separator will be preceded by the backslash character.
+ *
+ * If a field contains an end of line (EOL) character then it too will be
+ * escaped. If the file should obey Excel's CSV formatting rules then the field
+ * will again be surrounded by speech marks. On the other hand, if the file
+ * should follow UNIX conventions then a single backslash will precede the
+ * EOL character. There is no single applicable standard for UNIX and some
+ * appications replace the CR with \r and the LF with \n but this class will
+ * not do so.
+ *
+ * If the field contains double quotes then that character will be escaped. It
+ * seems as though UNIX does not define a standard for this whilst Excel does.
+ * Should the CSV file have to obey Excel's formmating rules then the speech
+ * mark character will be escaped with a second set of speech marks. Finally, an
+ * enclosing set of speah marks will also surround the entire field. Thus, if
+ * the following line of text appeared in a cell - "Hello" he said - it would
+ * look like this when converted into a field within a CSV file - """Hello"" he
+ * said".
+ *
+ * Finally, it is worth noting that talk of CSV 'standards' is really slightly
+ * missleading as there is no such thing. It may well be that the code in this
+ * class has to be modified to produce files to suit a specific application
+ * or requirement.
+ *
+ * @author Mark B
+ * @version 1.00 9th April 2010
+ * 1.10 13th April 2010 - Added support for processing all Excel
+ * workbooks in a folder along with the ability
+ * to specify a field separator character.
+ * 2.00 14th April 2010 - Added support for embedded characters; the
+ * field separator, EOL and double quotes or
+ * speech marks. In addition, gave the client
+ * the ability to select how these are handled,
+ * either obeying Excel's or UNIX formatting
+ * conventions.
+ */
+@SuppressWarnings({"java:S106","java:S4823","java:S1192"})
+public class ToCSV {
+
+ private Workbook workbook;
+ private ArrayList> csvData;
+ private int maxRowWidth;
+ private int formattingConvention;
+ private DataFormatter formatter;
+ private FormulaEvaluator evaluator;
+ private String separator;
+
+ private static final String CSV_FILE_EXTENSION = ".csv";
+ private static final String DEFAULT_SEPARATOR = ",";
+
+ /**
+ * Identifies that the CSV file should obey Excel's formatting conventions
+ * with regard to escaping certain embedded characters - the field separator,
+ * speech mark and end of line (EOL) character
+ */
+ public static final int EXCEL_STYLE_ESCAPING = 0;
+
+ /**
+ * Identifies that the CSV file should obey UNIX formatting conventions
+ * with regard to escaping certain embedded characters - the field separator
+ * and end of line (EOL) character
+ */
+ public static final int UNIX_STYLE_ESCAPING = 1;
+
+ /**
+ * Process the contents of a folder, convert the contents of each Excel
+ * workbook into CSV format and save the resulting file to the specified
+ * folder using the same name as the original workbook with the .xls or
+ * .xlsx extension replaced by .csv. This method will ensure that the
+ * CSV file created contains the comma field separator and that embedded
+ * characters such as the field separator, the EOL and double quotes are
+ * escaped in accordance with Excel's convention.
+ *
+ * @param strSource An instance of the String class that encapsulates the
+ * name of and path to either a folder containing those Excel
+ * workbook(s) or the name of and path to an individual Excel workbook
+ * that is/are to be converted.
+ * @param strDestination An instance of the String class encapsulating the
+ * name of and path to a folder that will contain the resulting CSV
+ * files.
+ * @throws java.io.FileNotFoundException Thrown if any file cannot be located
+ * on the filesystem during processing.
+ * @throws java.io.IOException Thrown if the filesystem encounters any
+ * problems during processing.
+ * @throws java.lang.IllegalArgumentException Thrown if the values passed
+ * to the strSource parameter refers to a file or folder that does not
+ * exist or if the value passed to the strDestination paramater refers
+ * to a folder that does not exist or simply does not refer to a
+ * folder.
+ */
+ public void convertExcelToCSV(String strSource, String strDestination)
+ throws FileNotFoundException, IOException,
+ IllegalArgumentException {
+
+ // Simply chain the call to the overloaded convertExcelToCSV(String,
+ // String, String, int) method, pass the default separator and ensure
+ // that certain embedded characters are escaped in accordance with
+ // Excel's formatting conventions
+ this.convertExcelToCSV(strSource, strDestination,
+ ToCSV.DEFAULT_SEPARATOR, ToCSV.EXCEL_STYLE_ESCAPING);
+ }
+
+ /**
+ * Process the contents of a folder, convert the contents of each Excel
+ * workbook into CSV format and save the resulting file to the specified
+ * folder using the same name as the original workbook with the .xls or
+ * .xlsx extension replaced by .csv. This method allows the client to
+ * define the field separator but will ensure that embedded characters such
+ * as the field separator, the EOL and double quotes are escaped in
+ * accordance with Excel's convention.
+ *
+ * @param strSource An instance of the String class that encapsulates the
+ * name of and path to either a folder containing those Excel
+ * workbook(s) or the name of and path to an individual Excel workbook
+ * that is/are to be converted.
+ * @param strDestination An instance of the String class encapsulating the
+ * name of and path to a folder that will contain the resulting CSV
+ * files.
+ * @param separator An instance of the String class that encapsulates the
+ * character or characters the client wishes to use as the field
+ * separator.
+ * @throws java.io.FileNotFoundException Thrown if any file cannot be located
+ * on the filesystem during processing.
+ * @throws java.io.IOException Thrown if the filesystem encounters any
+ * problems during processing.
+ * @throws java.lang.IllegalArgumentException Thrown if the values passed
+ * to the strSource parameter refers to a file or folder that does not
+ * exist or if the value passed to the strDestination paramater refers
+ * to a folder that does not exist or simply does not refer to a
+ * folder.
+ */
+ public void convertExcelToCSV(String strSource, String strDestination,
+ String separator)
+ throws FileNotFoundException, IOException,
+ IllegalArgumentException {
+
+ // Simply chain the call to the overloaded convertExcelToCSV(String,
+ // String, String, int) method and ensure that certain embedded
+ // characters are escaped in accordance with Excel's formatting
+ // conventions
+ this.convertExcelToCSV(strSource, strDestination,
+ separator, ToCSV.EXCEL_STYLE_ESCAPING);
+ }
+
+ /**
+ * Process the contents of a folder, convert the contents of each Excel
+ * workbook into CSV format and save the resulting file to the specified
+ * folder using the same name as the original workbook with the .xls or
+ * .xlsx extension replaced by .csv
+ *
+ * @param strSource An instance of the String class that encapsulates the
+ * name of and path to either a folder containing those Excel
+ * workbook(s) or the name of and path to an individual Excel workbook
+ * that is/are to be converted.
+ * @param strDestination An instance of the String class encapsulating the name
+ * of and path to a folder that will contain the resulting CSV files.
+ * @param formattingConvention A primitive int whose value will determine
+ * whether certain embedded characters should be escaped in accordance
+ * with Excel's or UNIX formatting conventions. Two constants are
+ * defined to support this option; ToCSV.EXCEL_STYLE_ESCAPING and
+ * ToCSV.UNIX_STYLE_ESCAPING
+ * @param separator An instance of the String class encapsulating the
+ * characters or characters that should be used to separate items
+ * on a line within the CSV file.
+ * @throws java.io.FileNotFoundException Thrown if any file cannot be located
+ * on the filesystem during processing.
+ * @throws java.io.IOException Thrown if the filesystem encounters any
+ * problems during processing.
+ * @throws java.lang.IllegalArgumentException Thrown if the values passed
+ * to the strSource parameter refers to a file or folder that does not
+ * exist, if the value passed to the strDestination paramater refers
+ * to a folder that does not exist, if the value passed to the
+ * strDestination parameter does not refer to a folder or if the
+ * value passed to the formattingConvention parameter is other than
+ * one of the values defined by the constants ToCSV.EXCEL_STYLE_ESCAPING
+ * and ToCSV.UNIX_STYLE_ESCAPING.
+ */
+ public void convertExcelToCSV(String strSource, String strDestination,
+ String separator, int formattingConvention)
+ throws FileNotFoundException, IOException,
+ IllegalArgumentException {
+ // Check that the source file/folder exists.
+ File source = new File(strSource);
+ if(!source.exists()) {
+ throw new IllegalArgumentException("The source for the Excel " +
+ "file(s) cannot be found at " + source);
+ }
+
+ // Ensure thaat the folder the user has chosen to save the CSV files
+ // away into firstly exists and secondly is a folder rather than, for
+ // instance, a data file.
+ File destination = new File(strDestination);
+ if(!destination.exists()) {
+ throw new IllegalArgumentException("The destination directory " + destination + " for the " +
+ "converted CSV file(s) does not exist.");
+ }
+ if(!destination.isDirectory()) {
+ throw new IllegalArgumentException("The destination " + destination + " for the CSV " +
+ "file(s) is not a directory/folder.");
+ }
+
+ // Ensure the value passed to the formattingConvention parameter is
+ // within range.
+ if(formattingConvention != ToCSV.EXCEL_STYLE_ESCAPING &&
+ formattingConvention != ToCSV.UNIX_STYLE_ESCAPING) {
+ throw new IllegalArgumentException("The value passed to the " +
+ "formattingConvention parameter is out of range: " + formattingConvention + ", expecting one of " +
+ ToCSV.EXCEL_STYLE_ESCAPING + " or " + ToCSV.UNIX_STYLE_ESCAPING);
+ }
+
+ // Copy the spearator character and formatting convention into local
+ // variables for use in other methods.
+ this.separator = separator;
+ this.formattingConvention = formattingConvention;
+
+ // Check to see if the sourceFolder variable holds a reference to
+ // a file or a folder full of files.
+ final File[] filesList;
+ if(source.isDirectory()) {
+ // Get a list of all of the Excel spreadsheet files (workbooks) in
+ // the source folder/directory
+ filesList = source.listFiles(new ExcelFilenameFilter());
+ }
+ else {
+ // Assume that it must be a file handle - although there are other
+ // options the code should perhaps check - and store the reference
+ // into the filesList variable.
+ filesList = new File[]{source};
+ }
+
+ // Step through each of the files in the source folder and for each
+ // open the workbook, convert it's contents to CSV format and then
+ // save the resulting file away into the folder specified by the
+ // contents of the destination variable. Note that the name of the
+ // csv file will be created by taking the name of the Excel file,
+ // removing the extension and replacing it with .csv. Note that there
+ // is one drawback with this approach; if the folder holding the files
+ // contains two workbooks whose names match but one is a binary file
+ // (.xls) and the other a SpreadsheetML file (.xlsx), then the names
+ // for both CSV files will be identical and one CSV file will,
+ // therefore, over-write the other.
+ if (filesList != null) {
+ for(File excelFile : filesList) {
+ // Open the workbook
+ this.openWorkbook(excelFile);
+
+ // Convert it's contents into a CSV file
+ this.convertToCSV();
+
+ // Build the name of the csv folder from that of the Excel workbook.
+ // Simply replace the .xls or .xlsx file extension with .csv
+ String destinationFilename = excelFile.getName();
+ destinationFilename = destinationFilename.substring(
+ 0, destinationFilename.lastIndexOf('.')) +
+ ToCSV.CSV_FILE_EXTENSION;
+
+ // Save the CSV file away using the newly constricted file name
+ // and to the specified directory.
+ this.saveCSVFile(new File(destination, destinationFilename));
+ }
+ }
+ }
+
+ /**
+ * Open an Excel workbook ready for conversion.
+ *
+ * @param file An instance of the File class that encapsulates a handle
+ * to a valid Excel workbook. Note that the workbook can be in
+ * either binary (.xls) or SpreadsheetML (.xlsx) format.
+ * @throws java.io.FileNotFoundException Thrown if the file cannot be located.
+ * @throws java.io.IOException Thrown if a problem occurs in the file system.
+ */
+ private void openWorkbook(File file) throws FileNotFoundException,
+ IOException {
+ System.out.println("Opening workbook [" + file.getName() + "]");
+ try (FileInputStream fis = new FileInputStream(file)) {
+
+ // Open the workbook and then create the FormulaEvaluator and
+ // DataFormatter instances that will be needed to, respectively,
+ // force evaluation of forumlae found in cells and create a
+ // formatted String encapsulating the cells contents.
+ this.workbook = WorkbookFactory.create(fis);
+ this.evaluator = this.workbook.getCreationHelper().createFormulaEvaluator();
+ this.formatter = new DataFormatter(true);
+ }
+ }
+
+ /**
+ * Called to convert the contents of the currently opened workbook into
+ * a CSV file.
+ */
+ private void convertToCSV() {
+ Sheet sheet;
+ Row row;
+ int lastRowNum;
+ this.csvData = new ArrayList<>();
+
+ System.out.println("Converting files contents to CSV format.");
+
+ // Discover how many sheets there are in the workbook....
+ int numSheets = this.workbook.getNumberOfSheets();
+
+ // and then iterate through them.
+ for(int i = 0; i < numSheets; i++) {
+
+ // Get a reference to a sheet and check to see if it contains
+ // any rows.
+ sheet = this.workbook.getSheetAt(i);
+ if(sheet.getPhysicalNumberOfRows() > 0) {
+ // Note down the index number of the bottom-most row and
+ // then iterate through all of the rows on the sheet starting
+ // from the very first row - number 1 - even if it is missing.
+ // Recover a reference to the row and then call another method
+ // which will strip the data from the cells and build lines
+ // for inclusion in the resylting CSV file.
+ lastRowNum = sheet.getLastRowNum();
+ for(int j = 0; j <= lastRowNum; j++) {
+ row = sheet.getRow(j);
+ this.rowToCSV(row);
+ }
+ }
+ }
+ }
+
+ /**
+ * Called to actually save the data recovered from the Excel workbook
+ * as a CSV file.
+ *
+ * @param file An instance of the File class that encapsulates a handle
+ * referring to the CSV file.
+ * @throws java.io.FileNotFoundException Thrown if the file cannot be found.
+ * @throws java.io.IOException Thrown to indicate and error occurred in the
+ * underylying file system.
+ */
+ private void saveCSVFile(File file) throws FileNotFoundException, IOException {
+ ArrayList line;
+ StringBuilder buffer;
+ String csvLineElement;
+
+ // Open a writer onto the CSV file.
+ try (BufferedWriter bw = new BufferedWriter(new FileWriter(file))) {
+
+ System.out.println("Saving the CSV file [" + file.getName() + "]");
+
+ // Step through the elements of the ArrayList that was used to hold
+ // all of the data recovered from the Excel workbooks' sheets, rows
+ // and cells.
+ for(int i = 0; i < this.csvData.size(); i++) {
+ buffer = new StringBuilder();
+
+ // Get an element from the ArrayList that contains the data for
+ // the workbook. This element will itself be an ArrayList
+ // containing Strings and each String will hold the data recovered
+ // from a single cell. The for() loop is used to recover elements
+ // from this 'row' ArrayList one at a time and to write the Strings
+ // away to a StringBuilder thus assembling a single line for inclusion
+ // in the CSV file. If a row was empty or if it was short, then
+ // the ArrayList that contains it's data will also be shorter than
+ // some of the others. Therefore, it is necessary to check within
+ // the for loop to ensure that the ArrayList contains data to be
+ // processed. If it does, then an element will be recovered and
+ // appended to the StringBuilder.
+ line = this.csvData.get(i);
+ for(int j = 0; j < this.maxRowWidth; j++) {
+ if(line.size() > j) {
+ csvLineElement = line.get(j);
+ if(csvLineElement != null) {
+ buffer.append(this.escapeEmbeddedCharacters(
+ csvLineElement));
+ }
+ }
+ if(j < (this.maxRowWidth - 1)) {
+ buffer.append(this.separator);
+ }
+ }
+
+ // Once the line is built, write it away to the CSV file.
+ bw.write(buffer.toString().trim());
+
+ // Condition the inclusion of new line characters so as to
+ // avoid an additional, superfluous, new line at the end of
+ // the file.
+ if(i < (this.csvData.size() - 1)) {
+ bw.newLine();
+ }
+ }
+ }
+ }
+
+ /**
+ * Called to convert a row of cells into a line of data that can later be
+ * output to the CSV file.
+ *
+ * @param row An instance of either the HSSFRow or XSSFRow classes that
+ * encapsulates information about a row of cells recovered from
+ * an Excel workbook.
+ */
+ private void rowToCSV(Row row) {
+ Cell cell;
+ int lastCellNum;
+ ArrayList csvLine = new ArrayList<>();
+
+ // Check to ensure that a row was recovered from the sheet as it is
+ // possible that one or more rows between other populated rows could be
+ // missing - blank. If the row does contain cells then...
+ if(row != null) {
+
+ // Get the index for the right most cell on the row and then
+ // step along the row from left to right recovering the contents
+ // of each cell, converting that into a formatted String and
+ // then storing the String into the csvLine ArrayList.
+ lastCellNum = row.getLastCellNum();
+ for(int i = 0; i <= lastCellNum; i++) {
+ cell = row.getCell(i);
+ if(cell == null) {
+ csvLine.add("");
+ }
+ else {
+ if(cell.getCellType() != CellType.FORMULA) {
+ csvLine.add(this.formatter.formatCellValue(cell));
+ }
+ else {
+ csvLine.add(this.formatter.formatCellValue(cell, this.evaluator));
+ }
+ }
+ }
+ // Make a note of the index number of the right most cell. This value
+ // will later be used to ensure that the matrix of data in the CSV file
+ // is square.
+ if(lastCellNum > this.maxRowWidth) {
+ this.maxRowWidth = lastCellNum;
+ }
+ }
+ this.csvData.add(csvLine);
+ }
+
+ /**
+ * Checks to see whether the field - which consists of the formatted
+ * contents of an Excel worksheet cell encapsulated within a String - contains
+ * any embedded characters that must be escaped. The method is able to
+ * comply with either Excel's or UNIX formatting conventions in the
+ * following manner;
+ *
+ * With regard to UNIX conventions, if the field contains any embedded
+ * field separator or EOL characters they will each be escaped by prefixing
+ * a leading backspace character. These are the only changes that have yet
+ * emerged following some research as being required.
+ *
+ * Excel has other embedded character escaping requirements, some that emerged
+ * from empirical testing, other through research. Firstly, with regards to
+ * any embedded speech marks ("), each occurrence should be escaped with
+ * another speech mark and the whole field then surrounded with speech marks.
+ * Thus if a field holds "Hello" he said then it should be modified
+ * to appear as """Hello"" he said". Furthermore, if the field
+ * contains either embedded separator or EOL characters, it should also
+ * be surrounded with speech marks. As a result 1,400 would become
+ * "1,400" assuming that the comma is the required field separator.
+ * This has one consequence in, if a field contains embedded speech marks
+ * and embedded separator characters, checks for both are not required as the
+ * additional set of speech marks that should be placed around ay field
+ * containing embedded speech marks will also account for the embedded
+ * separator.
+ *
+ * It is worth making one further note with regard to embedded EOL
+ * characters. If the data in a worksheet is exported as a CSV file using
+ * Excel itself, then the field will be surounded with speech marks. If the
+ * resulting CSV file is then re-imports into another worksheet, the EOL
+ * character will result in the original simgle field occupying more than
+ * one cell. This same 'feature' is replicated in this classes behaviour.
+ *
+ * @param field An instance of the String class encapsulating the formatted
+ * contents of a cell on an Excel worksheet.
+ * @return A String that encapsulates the formatted contents of that
+ * Excel worksheet cell but with any embedded separator, EOL or
+ * speech mark characters correctly escaped.
+ */
+ private String escapeEmbeddedCharacters(String field) {
+ StringBuilder buffer;
+
+ // If the fields contents should be formatted to confrom with Excel's
+ // convention....
+ if(this.formattingConvention == ToCSV.EXCEL_STYLE_ESCAPING) {
+
+ // Firstly, check if there are any speech marks (") in the field;
+ // each occurrence must be escaped with another set of spech marks
+ // and then the entire field should be enclosed within another
+ // set of speech marks. Thus, "Yes" he said would become
+ // """Yes"" he said"
+ if(field.contains("\"")) {
+ buffer = new StringBuilder(field.replaceAll("\"", "\\\"\\\""));
+ buffer.insert(0, "\"");
+ buffer.append("\"");
+ }
+ else {
+ // If the field contains either embedded separator or EOL
+ // characters, then escape the whole field by surrounding it
+ // with speech marks.
+ buffer = new StringBuilder(field);
+ if((buffer.indexOf(this.separator)) > -1 ||
+ (buffer.indexOf("\n")) > -1) {
+ buffer.insert(0, "\"");
+ buffer.append("\"");
+ }
+ }
+ return(buffer.toString().trim());
+ }
+ // The only other formatting convention this class obeys is the UNIX one
+ // where any occurrence of the field separator or EOL character will
+ // be escaped by preceding it with a backslash.
+ else {
+ if(field.contains(this.separator)) {
+ field = field.replaceAll(this.separator, ("\\\\" + this.separator));
+ }
+ if(field.contains("\n")) {
+ field = field.replaceAll("\n", "\\\\\n");
+ }
+ return(field);
+ }
+ }
+
+ /**
+ * The main() method contains code that demonstrates how to use the class.
+ *
+ * @param args An array containing zero, one or more elements all of type
+ * String. Each element will encapsulate an argument specified by the
+ * user when running the program from the command prompt.
+ */
+ public static void main(String[] args) {
+ // Check the number of arguments passed to the main method. There
+ // must be two, three or four; the name of and path to either the folder
+ // containing the Excel files or an individual Excel workbook that is/are
+ // to be converted, the name of and path to the folder to which the CSV
+ // files should be written, - optionally - the separator character
+ // that should be used to separate individual items (fields) on the
+ // lines (records) of the CSV file and - again optionally - an integer
+ // that idicates whether the CSV file ought to obey Excel's or UNIX
+ // convnetions with regard to formatting fields that contain embedded
+ // separator, Speech mark or EOL character(s).
+ //
+ // Note that the names of the CSV files will be derived from those
+ // of the Excel file(s). Put simply the .xls or .xlsx extension will be
+ // replaced with .csv. Therefore, if the source folder contains files
+ // with matching names but different extensions - Test.xls and Test.xlsx
+ // for example - then the CSV file generated from one will overwrite
+ // that generated from the other.
+ ToCSV converter;
+ boolean converted = true;
+ long startTime = System.currentTimeMillis();
+ try {
+ converter = new ToCSV();
+ if(args.length == 2) {
+ // Just the Source File/Folder and Destination Folder were
+ // passed to the main method.
+ converter.convertExcelToCSV(args[0], args[1]);
+ }
+ else if(args.length == 3) {
+ // The Source File/Folder, Destination Folder and Separator
+ // were passed to the main method.
+ converter.convertExcelToCSV(args[0], args[1], args[2]);
+ }
+ else if(args.length == 4) {
+ // The Source File/Folder, Destination Folder, Separator and
+ // Formatting Convnetion were passed to the main method.
+ converter.convertExcelToCSV(args[0], args[1],
+ args[2], Integer.parseInt(args[3]));
+ }
+ else {
+ // None or more than four parameters were passed so display
+ //a Usage message.
+ System.out.println("Usage: java ToCSV [Source File/Folder] " +
+ "[Destination Folder] [Separator] [Formatting Convention]\n" +
+ "\tSource File/Folder\tThis argument should contain the name of and\n" +
+ "\t\t\t\tpath to either a single Excel workbook or a\n" +
+ "\t\t\t\tfolder containing one or more Excel workbooks.\n" +
+ "\tDestination Folder\tThe name of and path to the folder that the\n" +
+ "\t\t\t\tCSV files should be written out into. The\n" +
+ "\t\t\t\tfolder must exist before running the ToCSV\n" +
+ "\t\t\t\tcode as it will not check for or create it.\n" +
+ "\tSeparator\t\tOptional. The character or characters that\n" +
+ "\t\t\t\tshould be used to separate fields in the CSV\n" +
+ "\t\t\t\trecord. If no value is passed then the comma\n" +
+ "\t\t\t\twill be assumed.\n" +
+ "\tFormatting Convention\tOptional. This argument can take one of two\n" +
+ "\t\t\t\tvalues. Passing 0 (zero) will result in a CSV\n" +
+ "\t\t\t\tfile that obeys Excel's formatting conventions\n" +
+ "\t\t\t\twhilst passing 1 (one) will result in a file\n" +
+ "\t\t\t\tthat obeys UNIX formatting conventions. If no\n" +
+ "\t\t\t\tvalue is passed, then the CSV file produced\n" +
+ "\t\t\t\twill obey Excel's formatting conventions.");
+ converted = false;
+ }
+ }
+ // It is not wise to have such a wide catch clause - Exception is very
+ // close to being at the top of the inheritance hierarchy - though it
+ // will suffice for this example as it is really not possible to recover
+ // easilly from an exceptional set of circumstances at this point in the
+ // program. It should however, ideally be replaced with one or more
+ // catch clauses optimised to handle more specific problems.
+ catch(Exception ex) {
+ System.out.println("Caught an: " + ex.getClass().getName());
+ System.out.println("Message: " + ex.getMessage());
+ System.out.println("Stacktrace follows:.....");
+ ex.printStackTrace(System.out);
+ converted = false;
+ }
+
+ if (converted) {
+ System.out.println("Conversion took " +
+ ((System.currentTimeMillis() - startTime)/1000) + " seconds");
+ }
+ }
+
+ /**
+ * An instance of this class can be used to control the files returned
+ * be a call to the listFiles() method when made on an instance of the
+ * File class and that object refers to a folder/directory
+ */
+ static class ExcelFilenameFilter implements FilenameFilter {
+
+ /**
+ * Determine those files that will be returned by a call to the
+ * listFiles() method. In this case, the name of the file must end with
+ * either of the following two extension; '.xls' or '.xlsx'. For the
+ * future, it is very possible to parameterise this and allow the
+ * containing class to pass, for example, an array of Strings to this
+ * class on instantiation. Each element in that array could encapsulate
+ * a valid file extension - '.xls', '.xlsx', '.xlt', '.xlst', etc. These
+ * could then be used to control which files were returned by the call
+ * to the listFiles() method.
+ *
+ * @param file An instance of the File class that encapsulates a handle
+ * referring to the folder/directory that contains the file.
+ * @param name An instance of the String class that encapsulates the
+ * name of the file.
+ * @return A boolean value that indicates whether the file should be
+ * included in the array retirned by the call to the listFiles()
+ * method. In this case true will be returned if the name of the
+ * file ends with either '.xls' or '.xlsx' and false will be
+ * returned in all other instances.
+ */
+ @Override
+ public boolean accept(File file, String name) {
+ return(name.endsWith(".xls") || name.endsWith(".xlsx"));
+ }
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/ss/formula/CalculateMortgage.java b/src/examples/src/org/apache/poi/examples/ss/formula/CalculateMortgage.java
new file mode 100644
index 0000000000..2675df181b
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/ss/formula/CalculateMortgage.java
@@ -0,0 +1,91 @@
+/* ====================================================================
+ 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.examples.ss.formula;
+
+import org.apache.poi.ss.formula.OperationEvaluationContext;
+import org.apache.poi.ss.formula.eval.ErrorEval;
+import org.apache.poi.ss.formula.eval.EvaluationException;
+import org.apache.poi.ss.formula.eval.NumberEval;
+import org.apache.poi.ss.formula.eval.OperandResolver;
+import org.apache.poi.ss.formula.eval.ValueEval;
+import org.apache.poi.ss.formula.functions.FreeRefFunction;
+
+/**
+ * A simple user-defined function to calculate principal and interest.
+ *
+ * @author Jon Svede ( jon [at] loquatic [dot] com )
+ * @author Brian Bush ( brian [dot] bush [at] nrel [dot] gov )
+ *
+ */
+public class CalculateMortgage implements FreeRefFunction {
+
+ @Override
+ public ValueEval evaluate( ValueEval[] args, OperationEvaluationContext ec ) {
+
+ // verify that we have enough data
+ if (args.length != 3) {
+ return ErrorEval.VALUE_INVALID;
+ }
+
+ // declare doubles for values
+ double principal, rate, years, result;
+ try {
+ // extract values as ValueEval
+ ValueEval v1 = OperandResolver.getSingleValue( args[0],
+ ec.getRowIndex(),
+ ec.getColumnIndex() ) ;
+ ValueEval v2 = OperandResolver.getSingleValue( args[1],
+ ec.getRowIndex(),
+ ec.getColumnIndex() ) ;
+ ValueEval v3 = OperandResolver.getSingleValue( args[2],
+ ec.getRowIndex(),
+ ec.getColumnIndex() ) ;
+
+ // get data as doubles
+ principal = OperandResolver.coerceValueToDouble( v1 ) ;
+ rate = OperandResolver.coerceValueToDouble( v2 ) ;
+ years = OperandResolver.coerceValueToDouble( v3 ) ;
+
+ result = calculateMortgagePayment( principal, rate, years ) ;
+ System.out.println( "Result = " + result ) ;
+
+ checkValue(result);
+
+ } catch (EvaluationException e) {
+ return e.getErrorEval();
+ }
+
+ return new NumberEval( result ) ;
+ }
+
+ public double calculateMortgagePayment( double p, double r, double y ) {
+ double i = r / 12 ;
+ double n = y * 12 ;
+
+ return p * (( i * Math.pow((1 + i),n ) ) / ( Math.pow((1 + i),n) - 1));
+ }
+ /**
+ * Excel does not support infinities and NaNs, rather, it gives a #NUM! error in these cases
+ *
+ * @throws EvaluationException (#NUM!) if result is NaN> or Infinity
+ */
+ private void checkValue(double result) throws EvaluationException {
+ if (Double.isNaN(result) || Double.isInfinite(result)) {
+ throw new EvaluationException(ErrorEval.NUM_ERROR);
+ }
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/ss/formula/CheckFunctionsSupported.java b/src/examples/src/org/apache/poi/examples/ss/formula/CheckFunctionsSupported.java
new file mode 100644
index 0000000000..4135c80904
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/ss/formula/CheckFunctionsSupported.java
@@ -0,0 +1,162 @@
+/* ====================================================================
+ 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.examples.ss.formula;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.apache.poi.ss.formula.eval.NotImplementedException;
+import org.apache.poi.ss.formula.eval.NotImplementedFunctionException;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.FormulaEvaluator;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.usermodel.WorkbookFactory;
+import org.apache.poi.ss.util.CellReference;
+
+/**
+ * Attempts to re-evaluate all the formulas in the workbook, and
+ * reports what (if any) formula functions used are not (currently)
+ * supported by Apache POI.
+ *
+ *
This provides examples of how to evaluate formulas in excel
+ * files using Apache POI, along with how to handle errors whilst
+ * doing so.
+ */
+@SuppressWarnings({"java:S106","java:S4823","java:S1192"})
+public class CheckFunctionsSupported {
+ public static void main(String[] args) throws Exception {
+ if (args.length < 1) {
+ System.err.println("Use:");
+ System.err.println(" CheckFunctionsSupported ");
+ return;
+ }
+
+ Workbook wb = WorkbookFactory.create(new File(args[0]));
+ CheckFunctionsSupported check = new CheckFunctionsSupported(wb);
+
+ // Fetch all the problems
+ List problems = new ArrayList<>();
+ for (int sn=0; sn unsupportedFunctions = new TreeSet<>();
+ for (FormulaEvaluationProblems p : problems) {
+ unsupportedFunctions.addAll(p.unsupportedFunctions);
+ }
+ if (unsupportedFunctions.isEmpty()) {
+ System.out.println("There are no unsupported formula functions used");
+ } else {
+ System.out.println("Unsupported formula functions:");
+ for (String function : unsupportedFunctions) {
+ System.out.println(" " + function);
+ }
+ System.out.println("Total unsupported functions = " + unsupportedFunctions.size());
+ }
+
+ // Report sheet by sheet
+ for (int sn=0; sn getUnsupportedFunctions(String sheetName) {
+ return getUnsupportedFunctions(workbook.getSheet(sheetName));
+ }
+ public Set getUnsupportedFunctions(int sheetIndex) {
+ return getUnsupportedFunctions(workbook.getSheetAt(sheetIndex));
+ }
+ public Set getUnsupportedFunctions(Sheet sheet) {
+ FormulaEvaluationProblems problems = getEvaluationProblems(sheet);
+ return problems.unsupportedFunctions;
+ }
+
+ public FormulaEvaluationProblems getEvaluationProblems(String sheetName) {
+ return getEvaluationProblems(workbook.getSheet(sheetName));
+ }
+ public FormulaEvaluationProblems getEvaluationProblems(int sheetIndex) {
+ return getEvaluationProblems(workbook.getSheetAt(sheetIndex));
+ }
+ public FormulaEvaluationProblems getEvaluationProblems(Sheet sheet) {
+ Set unsupportedFunctions = new HashSet<>();
+ Map unevaluatableCells = new HashMap<>();
+
+ for (Row r : sheet) {
+ for (Cell c : r) {
+ try {
+ evaluator.evaluate(c);
+ } catch (Exception e) {
+ if (e instanceof NotImplementedException && e.getCause() != null) {
+ // Has been wrapped with cell details, but we know those
+ e = (Exception)e.getCause();
+ }
+
+ if (e instanceof NotImplementedFunctionException) {
+ NotImplementedFunctionException nie = (NotImplementedFunctionException)e;
+ unsupportedFunctions.add(nie.getFunctionName());
+ }
+ unevaluatableCells.put(new CellReference(c), e);
+ }
+ }
+ }
+
+ return new FormulaEvaluationProblems(unsupportedFunctions, unevaluatableCells);
+ }
+
+ public static class FormulaEvaluationProblems {
+ /** Which used functions are unsupported by POI at this time */
+ private final Set unsupportedFunctions;
+ /** Which cells had unevaluatable formulas, and why? */
+ private final Map unevaluatableCells;
+
+ protected FormulaEvaluationProblems(Set unsupportedFunctions,
+ Map unevaluatableCells) {
+ this.unsupportedFunctions = Collections.unmodifiableSet(unsupportedFunctions);
+ this.unevaluatableCells = Collections.unmodifiableMap(unevaluatableCells);
+ }
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/ss/formula/SettingExternalFunction.java b/src/examples/src/org/apache/poi/examples/ss/formula/SettingExternalFunction.java
new file mode 100644
index 0000000000..49feb3f7aa
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/ss/formula/SettingExternalFunction.java
@@ -0,0 +1,93 @@
+/*
+ * ====================================================================
+ * 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.examples.ss.formula;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.poi.ss.formula.OperationEvaluationContext;
+import org.apache.poi.ss.formula.eval.ErrorEval;
+import org.apache.poi.ss.formula.eval.ValueEval;
+import org.apache.poi.ss.formula.functions.FreeRefFunction;
+import org.apache.poi.ss.formula.udf.UDFFinder;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+/**
+ * Demonstrates how to use functions provided by third-party add-ins, e.g. Bloomberg Excel Add-in.
+ *
+ * There can be situations when you are not interested in formula evaluation,
+ * you just need to set the formula and the workbook will be evaluation by the client.
+ */
+public class SettingExternalFunction {
+
+ /**
+ * wrap external functions in a plugin
+ */
+ public static class BloombergAddIn implements UDFFinder {
+ private final Map _functionsByName;
+
+ public BloombergAddIn() {
+ // dummy function that returns NA
+ // don't care about the implementation, we are not interested in evaluation
+ // and this method will never be called
+ FreeRefFunction NA = new FreeRefFunction() {
+ @Override
+ public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) {
+ return ErrorEval.NA;
+ }
+ };
+ _functionsByName = new HashMap<>();
+ _functionsByName.put("BDP", NA);
+ _functionsByName.put("BDH", NA);
+ _functionsByName.put("BDS", NA);
+ }
+
+ @Override
+ public FreeRefFunction findFunction(String name) {
+ return _functionsByName.get(name.toUpperCase(Locale.ROOT));
+ }
+
+ }
+
+ public static void main( String[] args ) throws IOException {
+
+ try (Workbook wb = new XSSFWorkbook()) { // or new HSSFWorkbook()
+
+ // register the add-in
+ wb.addToolPack(new BloombergAddIn());
+
+ Sheet sheet = wb.createSheet();
+ Row row = sheet.createRow(0);
+ row.createCell(0).setCellFormula("BDP(\"GOOG Equity\",\"CHG_PCT_YTD\")/100");
+ row.createCell(1).setCellFormula("BDH(\"goog us equity\",\"EBIT\",\"1/1/2005\",\"12/31/2009\",\"per=cy\",\"curr=USD\") ");
+ row.createCell(2).setCellFormula("BDS(\"goog us equity\",\"top_20_holders_public_filings\") ");
+
+ try (FileOutputStream out = new FileOutputStream("bloomberg-demo.xlsx")) {
+ wb.write(out);
+ }
+ }
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/ss/formula/UserDefinedFunctionExample.java b/src/examples/src/org/apache/poi/examples/ss/formula/UserDefinedFunctionExample.java
new file mode 100644
index 0000000000..939fd9ab9d
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/ss/formula/UserDefinedFunctionExample.java
@@ -0,0 +1,80 @@
+/* ====================================================================
+ 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.examples.ss.formula;
+
+import java.io.File;
+
+import org.apache.poi.ss.formula.functions.FreeRefFunction;
+import org.apache.poi.ss.formula.udf.DefaultUDFFinder;
+import org.apache.poi.ss.formula.udf.UDFFinder;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellValue;
+import org.apache.poi.ss.usermodel.FormulaEvaluator;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.usermodel.WorkbookFactory;
+import org.apache.poi.ss.util.CellReference;
+
+
+/**
+ * An example class of how to invoke a User Defined Function for a given
+ * XLS instance using POI's UDFFinder implementation.
+ */
+@SuppressWarnings({"java:S106","java:S4823","java:S1192"})
+public final class UserDefinedFunctionExample {
+
+ private UserDefinedFunctionExample() {}
+
+ public static void main(String[] args ) throws Exception {
+
+ if( args.length != 2 ) {
+ // e.g. src/examples/src/org/apache/poi/ss/examples/formula/mortgage-calculation.xls Sheet1!B4
+ System.out.println( "usage: UserDefinedFunctionExample fileName cellId" ) ;
+ return;
+ }
+
+ System.out.println( "fileName: " + args[0] ) ;
+ System.out.println( "cell: " + args[1] ) ;
+
+ File workbookFile = new File( args[0] ) ;
+
+ try (Workbook workbook = WorkbookFactory.create(workbookFile, null, true)) {
+ String[] functionNames = {"calculatePayment"};
+ FreeRefFunction[] functionImpls = {new CalculateMortgage()};
+
+ UDFFinder udfToolpack = new DefaultUDFFinder(functionNames, functionImpls);
+
+ // register the user-defined function in the workbook
+ workbook.addToolPack(udfToolpack);
+
+ FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator();
+
+ CellReference cr = new CellReference(args[1]);
+ String sheetName = cr.getSheetName();
+ Sheet sheet = workbook.getSheet(sheetName);
+ int rowIdx = cr.getRow();
+ int colIdx = cr.getCol();
+ Row row = sheet.getRow(rowIdx);
+ Cell cell = row.getCell(colIdx);
+
+ CellValue value = evaluator.evaluate(cell);
+
+ System.out.println("returns value: " + value);
+ }
+ }
+}
diff --git a/src/examples/src/org/apache/poi/examples/ss/formula/mortgage-calculation.xls b/src/examples/src/org/apache/poi/examples/ss/formula/mortgage-calculation.xls
new file mode 100644
index 0000000000..4e71ba8e65
Binary files /dev/null and b/src/examples/src/org/apache/poi/examples/ss/formula/mortgage-calculation.xls differ
diff --git a/src/examples/src/org/apache/poi/examples/ss/html/HSSFHtmlHelper.java b/src/examples/src/org/apache/poi/examples/ss/html/HSSFHtmlHelper.java
new file mode 100644
index 0000000000..4614a31031
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/ss/html/HSSFHtmlHelper.java
@@ -0,0 +1,66 @@
+/* ====================================================================
+ 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.examples.ss.html;
+
+import java.util.Formatter;
+
+import org.apache.poi.hssf.usermodel.HSSFCellStyle;
+import org.apache.poi.hssf.usermodel.HSSFPalette;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.hssf.util.HSSFColor;
+import org.apache.poi.hssf.util.HSSFColor.HSSFColorPredefined;
+import org.apache.poi.ss.usermodel.CellStyle;
+
+/**
+ * Implementation of {@link HtmlHelper} for HSSF files.
+ */
+public class HSSFHtmlHelper implements HtmlHelper {
+ private final HSSFWorkbook wb;
+ private final HSSFPalette colors;
+
+ private static final HSSFColor HSSF_AUTO = HSSFColorPredefined.AUTOMATIC.getColor();
+
+ public HSSFHtmlHelper(HSSFWorkbook wb) {
+ this.wb = wb;
+ // If there is no custom palette, then this creates a new one that is
+ // a copy of the default
+ colors = wb.getCustomPalette();
+ }
+
+ @Override
+ public void colorStyles(CellStyle style, Formatter out) {
+ HSSFCellStyle cs = (HSSFCellStyle) style;
+ out.format(" /* fill pattern = %d */%n", cs.getFillPattern().getCode());
+ styleColor(out, "background-color", cs.getFillForegroundColor());
+ styleColor(out, "color", cs.getFont(wb).getColor());
+ styleColor(out, "border-left-color", cs.getLeftBorderColor());
+ styleColor(out, "border-right-color", cs.getRightBorderColor());
+ styleColor(out, "border-top-color", cs.getTopBorderColor());
+ styleColor(out, "border-bottom-color", cs.getBottomBorderColor());
+ }
+
+ private void styleColor(Formatter out, String attr, short index) {
+ HSSFColor color = colors.getColor(index);
+ if (index == HSSF_AUTO.getIndex() || color == null) {
+ out.format(" /* %s: index = %d */%n", attr, index);
+ } else {
+ short[] rgb = color.getTriplet();
+ out.format(" %s: #%02x%02x%02x; /* index = %d */%n", attr, rgb[0],
+ rgb[1], rgb[2], index);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/examples/src/org/apache/poi/examples/ss/html/HtmlHelper.java b/src/examples/src/org/apache/poi/examples/ss/html/HtmlHelper.java
new file mode 100644
index 0000000000..8747ad4c9d
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/ss/html/HtmlHelper.java
@@ -0,0 +1,40 @@
+/* ====================================================================
+ 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.examples.ss.html;
+
+import java.util.Formatter;
+
+import org.apache.poi.ss.usermodel.CellStyle;
+
+/**
+ * This interface is used where code wants to be independent of the workbook
+ * formats. If you are writing such code, you can add a method to this
+ * interface, and then implement it for both HSSF and XSSF workbooks, letting
+ * the driving code stay independent of format.
+ *
+ * @author Ken Arnold, Industrious Media LLC
+ */
+public interface HtmlHelper {
+ /**
+ * Outputs the appropriate CSS style for the given cell style.
+ *
+ * @param style The cell style.
+ * @param out The place to write the output.
+ */
+ void colorStyles(CellStyle style, Formatter out);
+}
diff --git a/src/examples/src/org/apache/poi/examples/ss/html/ToHtml.java b/src/examples/src/org/apache/poi/examples/ss/html/ToHtml.java
new file mode 100644
index 0000000000..930de56a29
--- /dev/null
+++ b/src/examples/src/org/apache/poi/examples/ss/html/ToHtml.java
@@ -0,0 +1,508 @@
+/* ====================================================================
+ 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.examples.ss.html;
+
+import java.io.BufferedReader;
+import java.io.Closeable;
+import java.io.FileInputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.util.Formatter;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.format.CellFormat;
+import org.apache.poi.ss.format.CellFormatResult;
+import org.apache.poi.ss.usermodel.BorderStyle;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellStyle;
+import org.apache.poi.ss.usermodel.CellType;
+import org.apache.poi.ss.usermodel.Font;
+import org.apache.poi.ss.usermodel.HorizontalAlignment;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.VerticalAlignment;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.usermodel.WorkbookFactory;
+import org.apache.poi.util.IOUtils;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+/**
+ * This example shows how to display a spreadsheet in HTML using the classes for
+ * spreadsheet display.
+ */
+@SuppressWarnings({"java:S106","java:S4823","java:S1192"})
+public final class ToHtml {
+ private final Workbook wb;
+ private final Appendable output;
+ private boolean completeHTML;
+ private Formatter out;
+ private boolean gotBounds;
+ private int firstColumn;
+ private int endColumn;
+ private HtmlHelper helper;
+
+ private static final String DEFAULTS_CLASS = "excelDefaults";
+ private static final String COL_HEAD_CLASS = "colHeader";
+ private static final String ROW_HEAD_CLASS = "rowHeader";
+
+ private static final Map HALIGN = mapFor(
+ HorizontalAlignment.LEFT, "left",
+ HorizontalAlignment.CENTER, "center",
+ HorizontalAlignment.RIGHT, "right",
+ HorizontalAlignment.FILL, "left",
+ HorizontalAlignment.JUSTIFY, "left",
+ HorizontalAlignment.CENTER_SELECTION, "center");
+
+ private static final Map VALIGN = mapFor(
+ VerticalAlignment.BOTTOM, "bottom",
+ VerticalAlignment.CENTER, "middle",
+ VerticalAlignment.TOP, "top");
+
+ private static final Map BORDER = mapFor(
+ BorderStyle.DASH_DOT, "dashed 1pt",
+ BorderStyle.DASH_DOT_DOT, "dashed 1pt",
+ BorderStyle.DASHED, "dashed 1pt",
+ BorderStyle.DOTTED, "dotted 1pt",
+ BorderStyle.DOUBLE, "double 3pt",
+ BorderStyle.HAIR, "solid 1px",
+ BorderStyle.MEDIUM, "solid 2pt",
+ BorderStyle.MEDIUM_DASH_DOT, "dashed 2pt",
+ BorderStyle.MEDIUM_DASH_DOT_DOT, "dashed 2pt",
+ BorderStyle.MEDIUM_DASHED, "dashed 2pt",
+ BorderStyle.NONE, "none",
+ BorderStyle.SLANTED_DASH_DOT, "dashed 2pt",
+ BorderStyle.THICK, "solid 3pt",
+ BorderStyle.THIN, "dashed 1pt");
+
+ private static final int IDX_TABLE_WIDTH = -2;
+ private static final int IDX_HEADER_COL_WIDTH = -1;
+
+
+ @SuppressWarnings({"unchecked"})
+ private static Map mapFor(Object... mapping) {
+ Map map = new HashMap<>();
+ for (int i = 0; i < mapping.length; i += 2) {
+ map.put((K) mapping[i], (V) mapping[i + 1]);
+ }
+ return map;
+ }
+
+ /**
+ * Creates a new examples to HTML for the given workbook.
+ *
+ * @param wb The workbook.
+ * @param output Where the HTML output will be written.
+ *
+ * @return An object for converting the workbook to HTML.
+ */
+ public static ToHtml create(Workbook wb, Appendable output) {
+ return new ToHtml(wb, output);
+ }
+
+ /**
+ * Creates a new examples to HTML for the given workbook. If the path ends
+ * with ".xlsx" an {@link XSSFWorkbook} will be used; otherwise
+ * this will use an {@link HSSFWorkbook}.
+ *
+ * @param path The file that has the workbook.
+ * @param output Where the HTML output will be written.
+ *
+ * @return An object for converting the workbook to HTML.
+ */
+ public static ToHtml create(String path, Appendable output)
+ throws IOException {
+ return create(new FileInputStream(path), output);
+ }
+
+ /**
+ * Creates a new examples to HTML for the given workbook. This attempts to
+ * detect whether the input is XML (so it should create an {@link
+ * XSSFWorkbook} or not (so it should create an {@link HSSFWorkbook}).
+ *
+ * @param in The input stream that has the workbook.
+ * @param output Where the HTML output will be written.
+ *
+ * @return An object for converting the workbook to HTML.
+ */
+ public static ToHtml create(InputStream in, Appendable output)
+ throws IOException {
+ Workbook wb = WorkbookFactory.create(in);
+ return create(wb, output);
+ }
+
+ private ToHtml(Workbook wb, Appendable output) {
+ if (wb == null) {
+ throw new NullPointerException("wb");
+ }
+ if (output == null) {
+ throw new NullPointerException("output");
+ }
+ this.wb = wb;
+ this.output = output;
+ setupColorMap();
+ }
+
+ private void setupColorMap() {
+ if (wb instanceof HSSFWorkbook) {
+ helper = new HSSFHtmlHelper((HSSFWorkbook) wb);
+ } else if (wb instanceof XSSFWorkbook) {
+ helper = new XSSFHtmlHelper();
+ } else {
+ throw new IllegalArgumentException(
+ "unknown workbook type: " + wb.getClass().getSimpleName());
+ }
+ }
+
+ /**
+ * Run this class as a program
+ *
+ * @param args The command line arguments.
+ *
+ * @throws Exception Exception we don't recover from.
+ */
+ public static void main(String[] args) throws Exception {
+ if(args.length < 2){
+ System.err.println("usage: ToHtml inputWorkbook outputHtmlFile");
+ return;
+ }
+
+ try (
+ FileWriter fw = new FileWriter(args[1]);
+ PrintWriter pw = new PrintWriter(fw)
+ ) {
+ ToHtml toHtml = create(args[0], pw);
+ toHtml.setCompleteHTML(true);
+ toHtml.printPage();
+ }
+ }
+
+ public void setCompleteHTML(boolean completeHTML) {
+ this.completeHTML = completeHTML;
+ }
+
+ public void printPage() throws IOException {
+ try {
+ ensureOut();
+ if (completeHTML) {
+ out.format(
+ "%n");
+ out.format("%n");
+ out.format("%n");
+ out.format("%n");
+ out.format("%n");
+ }
+
+ print();
+
+ if (completeHTML) {
+ out.format("%n");
+ out.format("%n");
+ }
+ } finally {
+ IOUtils.closeQuietly(out);
+ if (output instanceof Closeable) {
+ IOUtils.closeQuietly((Closeable) output);
+ }
+ }
+ }
+
+ public void print() {
+ printInlineStyle();
+ printSheets();
+ }
+
+ private void printInlineStyle() {
+ //out.format("%n");
+ out.format("%n");
+ }
+
+ private void ensureOut() {
+ if (out == null) {
+ out = new Formatter(output);
+ }
+ }
+
+ public void printStyles() {
+ ensureOut();
+
+ // First, copy the base css
+ try (BufferedReader in = new BufferedReader(new InputStreamReader(
+ getClass().getResourceAsStream("excelStyle.css")))){
+ String line;
+ while ((line = in.readLine()) != null) {
+ out.format("%s%n", line);
+ }
+ } catch (IOException e) {
+ throw new IllegalStateException("Reading standard css", e);
+ }
+
+ // now add css for each used style
+ Set seen = new HashSet<>();
+ for (int i = 0; i < wb.getNumberOfSheets(); i++) {
+ Sheet sheet = wb.getSheetAt(i);
+ Iterator rows = sheet.rowIterator();
+ while (rows.hasNext()) {
+ Row row = rows.next();
+ for (Cell cell : row) {
+ CellStyle style = cell.getCellStyle();
+ if (!seen.contains(style)) {
+ printStyle(style);
+ seen.add(style);
+ }
+ }
+ }
+ }
+ }
+
+ private void printStyle(CellStyle style) {
+ out.format(".%s .%s {%n", DEFAULTS_CLASS, styleName(style));
+ styleContents(style);
+ out.format("}%n");
+ }
+
+ private void styleContents(CellStyle style) {
+ styleOut("text-align", style.getAlignment(), HALIGN);
+ styleOut("vertical-align", style.getVerticalAlignment(), VALIGN);
+ fontStyle(style);
+ borderStyles(style);
+ helper.colorStyles(style, out);
+ }
+
+ private void borderStyles(CellStyle style) {
+ styleOut("border-left", style.getBorderLeft(), BORDER);
+ styleOut("border-right", style.getBorderRight(), BORDER);
+ styleOut("border-top", style.getBorderTop(), BORDER);
+ styleOut("border-bottom", style.getBorderBottom(), BORDER);
+ }
+
+ private void fontStyle(CellStyle style) {
+ Font font = wb.getFontAt(style.getFontIndexAsInt());
+
+ if (font.getBold()) {
+ out.format(" font-weight: bold;%n");
+ }
+ if (font.getItalic()) {
+ out.format(" font-style: italic;%n");
+ }
+
+ int fontheight = font.getFontHeightInPoints();
+ if (fontheight == 9) {
+ //fix for stupid ol Windows
+ fontheight = 10;
+ }
+ out.format(" font-size: %dpt;%n", fontheight);
+
+ // Font color is handled with the other colors
+ }
+
+ private String styleName(CellStyle style) {
+ if (style == null) {
+ style = wb.getCellStyleAt((short) 0);
+ }
+ StringBuilder sb = new StringBuilder();
+ try (Formatter fmt = new Formatter(sb)) {
+ fmt.format("style_%02x", style.getIndex());
+ return fmt.toString();
+ }
+ }
+
+ private void styleOut(String attr, K key, Map mapping) {
+ String value = mapping.get(key);
+ if (value != null) {
+ out.format(" %s: %s;%n", attr, value);
+ }
+ }
+
+ private static CellType ultimateCellType(Cell c) {
+ CellType type = c.getCellType();
+ if (type == CellType.FORMULA) {
+ type = c.getCachedFormulaResultType();
+ }
+ return type;
+ }
+
+ private void printSheets() {
+ ensureOut();
+ Sheet sheet = wb.getSheetAt(0);
+ printSheet(sheet);
+ }
+
+ public void printSheet(Sheet sheet) {
+ ensureOut();
+ Map widths = computeWidths(sheet);
+ int tableWidth = widths.get(IDX_TABLE_WIDTH);
+ out.format("