tag r1847494 as 4.0.1

git-svn-id: https://svn.apache.org/repos/asf/poi/tags/REL_4_0_1@1847495 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/build.xml b/build.xml
index d7162c2..7cf2c3a 100644
--- a/build.xml
+++ b/build.xml
@@ -1950,6 +1950,7 @@
                 <mapper>
                     <chainedmapper>
                         <filtermapper>
+                            <replaceregex pattern=".jar" replace="" flags="g"/>
                             <replaceregex pattern="ooxml-lite" replace="ooxml-schemas" flags="g"/>
                             <replaceregex pattern="(.java|.src)$$" replace="-sources"/>
                             <replaceregex pattern=".*(?:build|src).?(.*)?" replace="\1"/>
@@ -2112,6 +2113,7 @@
                       sonar/*/src/**,
                       compile-lib/**,
                       ooxml-lib/**,
+                      ooxml-testlib/**,
                       scripts/**,
                       TEST*,
                       *.ipr,
@@ -2452,7 +2454,7 @@
             <copy todir="${repo}">
                 <mappedresources>
                     <!-- add sha-512 when nexus rules are updated (1/2) -->
-                    <fileset dir="build/dist/maven" includes="@{artifactId}/**" excludes="**/*.sha512"/>
+                    <fileset dir="build/dist/maven" includes="@{artifactId}/**" excludes="**/*.sha512,**/*.sha256"/>
                     <regexpmapper from="^([^/]+)/(.*)$$" to="org/apache/poi/\1/${version.id}/\2" handledirsep="true"/>
                 </mappedresources>
             </copy>
diff --git a/sonar/examples/pom.xml b/sonar/examples/pom.xml
index 441dfad..22c576a 100644
--- a/sonar/examples/pom.xml
+++ b/sonar/examples/pom.xml
@@ -6,7 +6,7 @@
     <parent>
         <groupId>org.apache.poi</groupId>
         <artifactId>poi-parent</artifactId>
-        <version>4.0.1-SNAPSHOT</version>
+        <version>4.0.2-SNAPSHOT</version>
     </parent>
     <artifactId>poi-examples</artifactId>
     <packaging>jar</packaging>
diff --git a/sonar/excelant/pom.xml b/sonar/excelant/pom.xml
index b981403..220d4eb 100644
--- a/sonar/excelant/pom.xml
+++ b/sonar/excelant/pom.xml
@@ -6,7 +6,7 @@
     <parent>
         <groupId>org.apache.poi</groupId>
         <artifactId>poi-parent</artifactId>
-        <version>4.0.1-SNAPSHOT</version>
+        <version>4.0.2-SNAPSHOT</version>
     </parent>
     <artifactId>poi-excelant</artifactId>
     <packaging>jar</packaging>
diff --git a/sonar/main/pom.xml b/sonar/main/pom.xml
index b4ed72f..9bd0af9 100644
--- a/sonar/main/pom.xml
+++ b/sonar/main/pom.xml
@@ -6,7 +6,7 @@
     <parent>
         <groupId>org.apache.poi</groupId>
         <artifactId>poi-parent</artifactId>
-        <version>4.0.1-SNAPSHOT</version>
+        <version>4.0.2-SNAPSHOT</version>
     </parent>
     <artifactId>poi-main</artifactId>
     <packaging>jar</packaging>
diff --git a/sonar/ooxml-schema-encryption/pom.xml b/sonar/ooxml-schema-encryption/pom.xml
index 5b2489a..d768a5f 100644
--- a/sonar/ooxml-schema-encryption/pom.xml
+++ b/sonar/ooxml-schema-encryption/pom.xml
@@ -6,7 +6,7 @@
     <parent>
         <groupId>org.apache.poi</groupId>
         <artifactId>poi-parent</artifactId>
-        <version>4.0.1-SNAPSHOT</version>
+        <version>4.0.2-SNAPSHOT</version>
         <relativePath>..</relativePath>
     </parent>
     <artifactId>poi-ooxml-schema-encryption</artifactId>
diff --git a/sonar/ooxml-schema-security/pom.xml b/sonar/ooxml-schema-security/pom.xml
index d872d9b..06318b2 100644
--- a/sonar/ooxml-schema-security/pom.xml
+++ b/sonar/ooxml-schema-security/pom.xml
@@ -6,7 +6,7 @@
     <parent>
         <groupId>org.apache.poi</groupId>
         <artifactId>poi-parent</artifactId>
-        <version>4.0.1-SNAPSHOT</version>
+        <version>4.0.2-SNAPSHOT</version>
         <relativePath>..</relativePath>
     </parent>
     <artifactId>poi-ooxml-schema-security</artifactId>
diff --git a/sonar/ooxml-schema/pom.xml b/sonar/ooxml-schema/pom.xml
index 92c2e26..7bb6943 100644
--- a/sonar/ooxml-schema/pom.xml
+++ b/sonar/ooxml-schema/pom.xml
@@ -6,7 +6,7 @@
     <parent>
         <groupId>org.apache.poi</groupId>
         <artifactId>poi-parent</artifactId>
-        <version>4.0.1-SNAPSHOT</version>
+        <version>4.0.2-SNAPSHOT</version>
         <relativePath>..</relativePath>
     </parent>
     <artifactId>poi-ooxml-schema</artifactId>
diff --git a/sonar/ooxml/pom.xml b/sonar/ooxml/pom.xml
index fad8ac5..dc4ffc8 100644
--- a/sonar/ooxml/pom.xml
+++ b/sonar/ooxml/pom.xml
@@ -6,7 +6,7 @@
     <parent>
         <groupId>org.apache.poi</groupId>
         <artifactId>poi-parent</artifactId>
-        <version>4.0.1-SNAPSHOT</version>
+        <version>4.0.2-SNAPSHOT</version>
     </parent>
     <artifactId>poi-ooxml</artifactId>
     <packaging>jar</packaging>
diff --git a/sonar/pom.xml b/sonar/pom.xml
index 87c2daf..3fcaf93 100644
--- a/sonar/pom.xml
+++ b/sonar/pom.xml
@@ -4,7 +4,7 @@
     <groupId>org.apache.poi</groupId>
     <artifactId>poi-parent</artifactId>
     <packaging>pom</packaging>
-    <version>4.0.1-SNAPSHOT</version>
+    <version>4.0.2-SNAPSHOT</version>
     <name>Apache POI - the Java API for Microsoft Documents</name>
     <description>Maven build of Apache POI for Sonar checks</description>
     <url>http://poi.apache.org/</url>
diff --git a/sonar/scratchpad/pom.xml b/sonar/scratchpad/pom.xml
index e7ef730..a980e15 100644
--- a/sonar/scratchpad/pom.xml
+++ b/sonar/scratchpad/pom.xml
@@ -6,7 +6,7 @@
     <parent>
         <groupId>org.apache.poi</groupId>
         <artifactId>poi-parent</artifactId>
-        <version>4.0.1-SNAPSHOT</version>
+        <version>4.0.2-SNAPSHOT</version>
     </parent>
     <artifactId>poi-scratchpad</artifactId>
     <packaging>jar</packaging>
diff --git a/src/integrationtest/org/apache/poi/TestAllFiles.java b/src/integrationtest/org/apache/poi/TestAllFiles.java
index 61c47b9..40f19f9 100644
--- a/src/integrationtest/org/apache/poi/TestAllFiles.java
+++ b/src/integrationtest/org/apache/poi/TestAllFiles.java
@@ -289,6 +289,7 @@
         "document/Bug50955.doc",
         "document/57843.doc",
         "slideshow/PPT95.ppt",
+        "slideshow/pp40only.ppt",
         "slideshow/Divino_Revelado.pptx",
         "openxml4j/OPCCompliance_CoreProperties_DCTermsNamespaceLimitedUseFAIL.docx",
         "openxml4j/OPCCompliance_CoreProperties_DoNotUseCompatibilityMarkupFAIL.docx",
diff --git a/src/integrationtest/org/apache/poi/stress/XSSFFileHandler.java b/src/integrationtest/org/apache/poi/stress/XSSFFileHandler.java
index 5c47d9a..48a30a2 100644
--- a/src/integrationtest/org/apache/poi/stress/XSSFFileHandler.java
+++ b/src/integrationtest/org/apache/poi/stress/XSSFFileHandler.java
@@ -35,7 +35,6 @@
 import java.util.Locale;
 import java.util.Set;
 
-import javax.xml.parsers.ParserConfigurationException;
 import javax.xml.transform.TransformerException;
 
 import org.apache.poi.EncryptedDocumentException;
@@ -148,7 +147,7 @@
     }
     
     private void exportToXML(XSSFWorkbook wb) throws SAXException,
-            ParserConfigurationException, TransformerException {
+            TransformerException {
         for (XSSFMap map : wb.getCustomXMLMappings()) {
             XSSFExportToXml exporter = new XSSFExportToXml(map);
 
@@ -165,7 +164,6 @@
         // zip-bomb
         EXPECTED_ADDITIONAL_FAILURES.add("spreadsheet/54764.xlsx");
         EXPECTED_ADDITIONAL_FAILURES.add("spreadsheet/54764-2.xlsx");
-        EXPECTED_ADDITIONAL_FAILURES.add("spreadsheet/54764.xlsx");
         EXPECTED_ADDITIONAL_FAILURES.add("spreadsheet/poc-xmlbomb.xlsx");
         EXPECTED_ADDITIONAL_FAILURES.add("spreadsheet/poc-xmlbomb-empty.xlsx");
         // strict OOXML
@@ -185,18 +183,19 @@
     public void handleAdditional(File file) throws Exception {
         // redirect stdout as the examples often write lots of text
         PrintStream oldOut = System.out;
+        String testFile = file.getParentFile().getName() + "/" + file.getName();
         try {
             System.setOut(new NullPrintStream());
             FromHowTo.main(new String[]{file.getAbsolutePath()});
             XLSX2CSV.main(new String[]{file.getAbsolutePath()});
 
             assertFalse("Expected Extraction to fail for file " + file + " and handler " + this + ", but did not fail!",
-                    EXPECTED_ADDITIONAL_FAILURES.contains(file.getParentFile().getName() + "/" + file.getName()));
+                    EXPECTED_ADDITIONAL_FAILURES.contains(testFile));
 
         } catch (OLE2NotOfficeXmlFileException e) {
             // we have some files that are not actually OOXML and thus cannot be tested here
         } catch (IllegalArgumentException | InvalidFormatException | POIXMLException | IOException e) {
-            if(!EXPECTED_ADDITIONAL_FAILURES.contains(file.getParentFile().getName() + "/" + file.getName())) {
+            if(!EXPECTED_ADDITIONAL_FAILURES.contains(testFile)) {
                 throw e;
             }
         } finally {
diff --git a/src/java/org/apache/poi/hpsf/VariantSupport.java b/src/java/org/apache/poi/hpsf/VariantSupport.java
index e606790..0e8dc08 100644
--- a/src/java/org/apache/poi/hpsf/VariantSupport.java
+++ b/src/java/org/apache/poi/hpsf/VariantSupport.java
@@ -36,7 +36,7 @@
  * Supports reading and writing of variant data.<p>
  *
  * <strong>FIXME (3):</strong> Reading and writing should be made more
- * uniform than it is now. The following items should be resolved:<p>
+ * uniform than it is now. The following items should be resolved:
  *
  * <ul>
  *
diff --git a/src/java/org/apache/poi/hpsf/wellknown/PropertyIDMap.java b/src/java/org/apache/poi/hpsf/wellknown/PropertyIDMap.java
index 130408e..c01ded7 100644
--- a/src/java/org/apache/poi/hpsf/wellknown/PropertyIDMap.java
+++ b/src/java/org/apache/poi/hpsf/wellknown/PropertyIDMap.java
@@ -33,8 +33,11 @@
  * The methods {@link #getSummaryInformationProperties} and {@link
  * #getDocumentSummaryInformationProperties} return singleton {@link
  * PropertyIDMap}s. An application that wants to extend these maps
- * should treat them as unmodifiable, copy them and modifiy the
+ * should treat them as unmodifiable, copy them and modify the
  * copies.
+ *
+ * Trying to modify the map directly will cause exceptions
+ * {@link UnsupportedOperationException} to be thrown.
  */
 public class PropertyIDMap implements Map<Long,String> {
 
@@ -490,11 +493,13 @@
 
     @Override
     public String put(Long key, String value) {
+        //noinspection ConstantConditions
         return idMap.put(key, value);
     }
 
     @Override
     public String remove(Object key) {
+        //noinspection ConstantConditions
         return idMap.remove(key);
     }
 
diff --git a/src/java/org/apache/poi/hssf/model/InternalWorkbook.java b/src/java/org/apache/poi/hssf/model/InternalWorkbook.java
index 46d8678..4ff3701 100644
--- a/src/java/org/apache/poi/hssf/model/InternalWorkbook.java
+++ b/src/java/org/apache/poi/hssf/model/InternalWorkbook.java
@@ -2276,6 +2276,8 @@
 
     /**
      * Only for internal calls - code based on this is not supported ...
+     *
+     * @return The list of records.
      */
     @Internal
     public WorkbookRecordList getWorkbookRecordList() {
diff --git a/src/java/org/apache/poi/hssf/record/RecordInputStream.java b/src/java/org/apache/poi/hssf/record/RecordInputStream.java
index 4f5f589..8347655 100644
--- a/src/java/org/apache/poi/hssf/record/RecordInputStream.java
+++ b/src/java/org/apache/poi/hssf/record/RecordInputStream.java
@@ -33,8 +33,9 @@
 import org.apache.poi.util.RecordFormatException;
 
 /**
- * Title:  Record Input Stream<P>
- * Description:  Wraps a stream and provides helper methods for the construction of records.<P>
+ * Title:  Record Input Stream
+ *
+ * Description:  Wraps a stream and provides helper methods for the construction of records.
  */
 public final class RecordInputStream implements LittleEndianInput {
 
@@ -194,11 +195,11 @@
 	private int readNextSid() {
 		int nAvailable  = _bhi.available();
 		if (nAvailable < EOFRecord.ENCODED_SIZE) {
-			if (nAvailable > 0) {
+			/*if (nAvailable > 0) {
 				// some scrap left over?
 				// ex45582-22397.xls has one extra byte after the last record
 				// Excel reads that file OK
-			}
+			}*/
 			return INVALID_SID_VALUE;
 		}
 		int result = _bhi.readRecordSID();
@@ -305,14 +306,13 @@
 	@Override
     public double readDouble() {
 		long valueLongBits = readLong();
-		double result = Double.longBitsToDouble(valueLongBits);
-		if (Double.isNaN(result)) {
+		/*if (Double.isNaN(result)) {
             // YK: Excel doesn't write NaN but instead converts the cell type into {@link CellType#ERROR}.
             // HSSF prior to version 3.7 had a bug: it could write Double.NaN but could not read such a file back.
             // This behavior was fixed in POI-3.7.
             //throw new RuntimeException("Did not expect to read NaN"); // (Because Excel typically doesn't write NaN)
-		}
-		return result;
+		}*/
+		return Double.longBitsToDouble(valueLongBits);
 	}
 	
 	public void readPlain(byte[] buf, int off, int len) {
diff --git a/src/java/org/apache/poi/hssf/record/SSTRecord.java b/src/java/org/apache/poi/hssf/record/SSTRecord.java
index 20d9931..1de2fe4 100644
--- a/src/java/org/apache/poi/hssf/record/SSTRecord.java
+++ b/src/java/org/apache/poi/hssf/record/SSTRecord.java
@@ -161,7 +161,7 @@
      * <P>
      * The data consists of sets of string data. This string data is
      * arranged as follows:
-     * <P>
+     * </P><P>
      * <pre>
      * short  string_length;   // length of string data
      * byte   string_flag;     // flag specifying special string
@@ -176,9 +176,9 @@
      * byte[] extension;       // optional extension (length of array
      *                         // is extend_length)
      * </pre>
-     * <P>
+     * </P><P>
      * The string_flag is bit mapped as follows:
-     * <P>
+     * </P><P>
      * <TABLE summary="string_flag mapping">
      *   <TR>
      *      <TH>Bit number</TH>
@@ -232,7 +232,7 @@
      * associated data. The UnicodeString class can handle the byte[]
      * vs short[] nature of the actual string data
      *
-     * @param in the RecordInputstream to read the record from
+     * @param in the RecordInputStream to read the record from
      */
     public SSTRecord(RecordInputStream in) {
         // this method is ALWAYS called after construction -- using
diff --git a/src/java/org/apache/poi/hssf/record/SharedFormulaRecord.java b/src/java/org/apache/poi/hssf/record/SharedFormulaRecord.java
index 770f740..328402d 100644
--- a/src/java/org/apache/poi/hssf/record/SharedFormulaRecord.java
+++ b/src/java/org/apache/poi/hssf/record/SharedFormulaRecord.java
@@ -77,7 +77,7 @@
 
     public String toString()
     {
-        StringBuffer buffer = new StringBuffer();
+        StringBuilder buffer = new StringBuilder();
 
         buffer.append("[SHARED FORMULA (").append(HexDump.intToHex(sid)).append("]\n");
         buffer.append("    .range      = ").append(getRange()).append("\n");
@@ -99,6 +99,10 @@
     }
 
     /**
+     * Convert formula into an array of {@link Ptg} tokens.
+     *
+     * @param formula The record to break into tokens, cannot be null
+     *
      * @return the equivalent {@link Ptg} array that the formula would have, were it not shared.
      */
     public Ptg[] getFormulaTokens(FormulaRecord formula) {
diff --git a/src/java/org/apache/poi/hssf/record/SharedValueRecordBase.java b/src/java/org/apache/poi/hssf/record/SharedValueRecordBase.java
index a503530..51fe091 100644
--- a/src/java/org/apache/poi/hssf/record/SharedValueRecordBase.java
+++ b/src/java/org/apache/poi/hssf/record/SharedValueRecordBase.java
@@ -42,6 +42,8 @@
 
 	/**
 	 * reads only the range (1 {@link CellRangeAddress8Bit}) from the stream
+	 *
+	 * @param in The interface for reading the record data.
 	 */
 	public SharedValueRecordBase(LittleEndianInput in) {
 		_range = new CellRangeAddress8Bit(in);
@@ -99,14 +101,12 @@
 			&& r.getLastColumn() >= colIx;
 	}
 	/**
-	 * @return {@code true} if (rowIx, colIx) describes the first cell in this shared value
-	 * object's range
-	 * 
 	 * @param rowIx the row index
 	 * @param colIx the column index
-	 * 
-	 * @return {@code true} if its the first cell in this shared value object range
-	 * 
+	 *
+	 * @return {@code true} if (rowIx, colIx) describes the first cell in this shared value
+	 * object's range
+	 *
 	 * @see #getRange()
 	 */
 	public final boolean isFirstCell(int rowIx, int colIx) {
diff --git a/src/java/org/apache/poi/poifs/filesystem/FileMagic.java b/src/java/org/apache/poi/poifs/filesystem/FileMagic.java
index 4ac6160..bab62c6 100644
--- a/src/java/org/apache/poi/poifs/filesystem/FileMagic.java
+++ b/src/java/org/apache/poi/poifs/filesystem/FileMagic.java
@@ -78,7 +78,7 @@
     /** PDF document */
     PDF("%PDF"),
     /** Some different HTML documents */
-    HTML("<!DOCTYP".getBytes(UTF_8), "<html".getBytes(UTF_8)),
+    HTML("<!DOCTYP".getBytes(UTF_8), "<html".getBytes(UTF_8), "<HTML".getBytes(UTF_8)),
     WORD2(new byte[]{ (byte)0xdb, (byte)0xa5, 0x2d, 0x00}),
     // keep UNKNOWN always as last enum!
     /** UNKNOWN magic */
@@ -101,17 +101,8 @@
 
     public static FileMagic valueOf(byte[] magic) {
         for (FileMagic fm : values()) {
-            int i=0;
-            boolean found = true;
             for (byte[] ma : fm.magic) {
-                for (byte m : ma) {
-                    byte d = magic[i++];
-                    if (!(d == m || (m == 0x70 && (d == 0x10 || d == 0x20 || d == 0x40)))) {
-                        found = false;
-                        break;
-                    }
-                }
-                if (found) {
+                if (findMagic(ma, magic)) {
                     return fm;
                 }
             }
@@ -119,6 +110,17 @@
         return UNKNOWN;
     }
 
+    private static boolean findMagic(byte[] cmp, byte[] actual) {
+        int i=0;
+        for (byte m : cmp) {
+            byte d = actual[i++];
+            if (!(d == m || (m == 0x70 && (d == 0x10 || d == 0x20 || d == 0x40)))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
 
     /**
      * Get the file magic of the supplied {@link File}<p>
diff --git a/src/java/org/apache/poi/sl/draw/DrawPaint.java b/src/java/org/apache/poi/sl/draw/DrawPaint.java
index d6986f3..a830605 100644
--- a/src/java/org/apache/poi/sl/draw/DrawPaint.java
+++ b/src/java/org/apache/poi/sl/draw/DrawPaint.java
@@ -29,7 +29,10 @@
 import java.awt.image.BufferedImage;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.Map;
 import java.util.Objects;
+import java.util.TreeMap;
+import java.util.function.BiFunction;
 
 import org.apache.poi.sl.usermodel.AbstractColorStyle;
 import org.apache.poi.sl.usermodel.ColorStyle;
@@ -197,28 +200,17 @@
 
             @Override
             public int getShade() {
-                int shade = orig.getShade();
-                switch (modifier) {
-                    case DARKEN:
-                        return Math.min(100000, Math.max(0,shade)+40000);
-                    case DARKEN_LESS:
-                        return Math.min(100000, Math.max(0,shade)+20000);
-                    default:
-                        return shade;
-                }
+                return scale(orig.getShade(), PaintModifier.DARKEN_LESS, PaintModifier.DARKEN);
             }
 
             @Override
             public int getTint() {
-                int tint = orig.getTint();
-                switch (modifier) {
-                    case LIGHTEN:
-                        return Math.min(100000, Math.max(0,tint)+40000);
-                    case LIGHTEN_LESS:
-                        return Math.min(100000, Math.max(0,tint)+20000);
-                    default:
-                        return tint;
-                }
+                return scale(orig.getTint(), PaintModifier.LIGHTEN_LESS, PaintModifier.LIGHTEN);
+            }
+
+            private int scale(int value, PaintModifier lessModifier, PaintModifier moreModifier) {
+                int delta = (modifier == lessModifier ? 20000 : (modifier == moreModifier ? 40000 : 0));
+                return Math.min(100000, Math.max(0,value)+delta);
             }
         };
 
@@ -300,7 +292,7 @@
         Color result = color.getColor();
 
         double alpha = getAlpha(result, color);
-        double hsl[] = RGB2HSL(result); // values are in the range [0..100] (usually ...)
+        double[] hsl = RGB2HSL(result); // values are in the range [0..100] (usually ...)
         applyHslModOff(hsl, 0, color.getHueMod(), color.getHueOff());
         applyHslModOff(hsl, 1, color.getSatMod(), color.getSatOff());
         applyHslModOff(hsl, 2, color.getLumMod(), color.getLumOff());
@@ -344,7 +336,7 @@
      * @param mod the modulation adjustment
      * @param off the offset adjustment
      */
-    private static void applyHslModOff(double hsl[], int hslPart, int mod, int off) {
+    private static void applyHslModOff(double[] hsl, int hslPart, int mod, int off) {
         if (mod == -1) {
             mod = 100000;
         }
@@ -363,7 +355,7 @@
      *
      * For a shade, the equation is luminance * %tint.
      */
-    private static void applyShade(double hsl[], ColorStyle fc) {
+    private static void applyShade(double[] hsl, ColorStyle fc) {
         int shade = fc.getShade();
         if (shade == -1) {
             return;
@@ -380,7 +372,7 @@
      * For a tint, the equation is luminance * %tint + (1-%tint).
      * (Note that 1-%tint is equal to the lumOff value in DrawingML.)
      */
-    private static void applyTint(double hsl[], ColorStyle fc) {
+    private static void applyTint(double[] hsl, ColorStyle fc) {
         int tint = fc.getTint();
         if (tint == -1) {
             return;
@@ -403,70 +395,63 @@
         }
 
         Rectangle2D anchor = DrawShape.getAnchor(graphics, shape);
-        final double h = anchor.getHeight(), w = anchor.getWidth(), x = anchor.getX(), y = anchor.getY();
 
         AffineTransform at = AffineTransform.getRotateInstance(Math.toRadians(angle), anchor.getCenterX(), anchor.getCenterY());
 
-        double diagonal = Math.sqrt(h * h + w * w);
-        Point2D p1 = new Point2D.Double(x + w / 2 - diagonal / 2, y + h / 2);
-        p1 = at.transform(p1, null);
-
-        Point2D p2 = new Point2D.Double(x + w, y + h / 2);
-        p2 = at.transform(p2, null);
+        double diagonal = Math.sqrt(Math.pow(anchor.getWidth(),2) + Math.pow(anchor.getHeight(),2));
+        final Point2D p1 = at.transform(new Point2D.Double(anchor.getCenterX() - diagonal / 2, anchor.getCenterY()), null);
+        final Point2D p2 = at.transform(new Point2D.Double(anchor.getMaxX(), anchor.getCenterY()), null);
 
 //        snapToAnchor(p1, anchor);
 //        snapToAnchor(p2, anchor);
 
-        if (p1.equals(p2)) {
-            // gradient paint on the same point throws an exception ... and doesn't make sense
-            return null;
-        }
-
-        float[] fractions = fill.getGradientFractions();
-        Color[] colors = new Color[fractions.length];
-
-        int i = 0;
-        for (ColorStyle fc : fill.getGradientColors()) {
-            // if fc is null, use transparent color to get color of background
-            colors[i++] = (fc == null) ? TRANSPARENT : applyColorTransform(fc);
-        }
-
-        return new LinearGradientPaint(p1, p2, fractions, colors);
+        // gradient paint on the same point throws an exception ... and doesn't make sense
+        return (p1.equals(p2)) ? null : safeFractions((f,c)->new LinearGradientPaint(p1,p2,f,c), fill);
     }
 
+
     @SuppressWarnings("WeakerAccess")
     protected Paint createRadialGradientPaint(GradientPaint fill, Graphics2D graphics) {
         Rectangle2D anchor = DrawShape.getAnchor(graphics, shape);
 
-        Point2D pCenter = new Point2D.Double(anchor.getX() + anchor.getWidth()/2,
-                anchor.getY() + anchor.getHeight()/2);
+        final Point2D pCenter = new Point2D.Double(anchor.getCenterX(), anchor.getCenterY());
 
-        float radius = (float)Math.max(anchor.getWidth(), anchor.getHeight());
+        final float radius = (float)Math.max(anchor.getWidth(), anchor.getHeight());
 
-        float[] fractions = fill.getGradientFractions();
-        Color[] colors = new Color[fractions.length];
-
-        int i=0;
-        for (ColorStyle fc : fill.getGradientColors()) {
-            colors[i++] = applyColorTransform(fc);
-        }
-
-        return new RadialGradientPaint(pCenter, radius, fractions, colors);
+        return safeFractions((f,c)->new RadialGradientPaint(pCenter,radius,f,c), fill);
     }
 
     @SuppressWarnings({"WeakerAccess", "unused"})
     protected Paint createPathGradientPaint(GradientPaint fill, Graphics2D graphics) {
         // currently we ignore an eventually center setting
 
-        float[] fractions = fill.getGradientFractions();
-        Color[] colors = new Color[fractions.length];
+        return safeFractions(PathGradientPaint::new, fill);
+    }
 
-        int i=0;
-        for (ColorStyle fc : fill.getGradientColors()) {
-            colors[i++] = applyColorTransform(fc);
+    private Paint safeFractions(BiFunction<float[],Color[],Paint> init, GradientPaint fill) {
+        float[] fractions = fill.getGradientFractions();
+        final ColorStyle[] styles = fill.getGradientColors();
+
+        // need to remap the fractions, because Java doesn't like repeating fraction values
+        Map<Float,Color> m = new TreeMap<>();
+        for (int i = 0; i<fractions.length; i++) {
+            // if fc is null, use transparent color to get color of background
+            m.put(fractions[i], (styles[i] == null ? TRANSPARENT : applyColorTransform(styles[i])));
         }
 
-        return new PathGradientPaint(colors, fractions);
+        final Color[] colors = new Color[m.size()];
+        if (fractions.length != m.size()) {
+            fractions = new float[m.size()];
+        }
+
+        int i=0;
+        for (Map.Entry<Float,Color> me : m.entrySet()) {
+            fractions[i] = me.getKey();
+            colors[i] = me.getValue();
+            i++;
+        }
+
+        return init.apply(fractions, colors);
     }
 
     /**
diff --git a/src/java/org/apache/poi/sl/draw/PathGradientPaint.java b/src/java/org/apache/poi/sl/draw/PathGradientPaint.java
index d4a2a5f..2281bbf 100644
--- a/src/java/org/apache/poi/sl/draw/PathGradientPaint.java
+++ b/src/java/org/apache/poi/sl/draw/PathGradientPaint.java
@@ -23,21 +23,24 @@
 import java.awt.geom.*;
 import java.awt.image.*;
 
+import org.apache.poi.util.Internal;
+
+@Internal
 class PathGradientPaint implements Paint {
 
     // http://asserttrue.blogspot.de/2010/01/how-to-iimplement-custom-paint-in-50.html
-    protected final Color colors[];
-    protected final float fractions[];
-    protected final int capStyle;
-    protected final int joinStyle;
-    protected final int transparency;
+    private final Color[] colors;
+    private final float[] fractions;
+    private final int capStyle;
+    private final int joinStyle;
+    private final int transparency;
 
     
-    public PathGradientPaint(Color colors[], float fractions[]) {
-        this(colors,fractions,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND);
+    PathGradientPaint(float[] fractions, Color[] colors) {
+        this(fractions,colors,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND);
     }
     
-    public PathGradientPaint(Color colors[], float fractions[], int capStyle, int joinStyle) {
+    private PathGradientPaint(float[] fractions, Color[] colors, int capStyle, int joinStyle) {
         this.colors = colors.clone();
         this.fractions = fractions.clone();
         this.capStyle = capStyle;
@@ -66,26 +69,26 @@
     }
 
     class PathGradientContext implements PaintContext {
-        protected final Rectangle deviceBounds;
-        protected final Rectangle2D userBounds;
+        final Rectangle deviceBounds;
+        final Rectangle2D userBounds;
         protected final AffineTransform xform;
-        protected final RenderingHints hints;
+        final RenderingHints hints;
 
         /**
          * for POI: the shape will be only known when the subclasses determines the concrete implementation 
          * in the draw/-content method, so we need to postpone the setting/creation as long as possible
          **/
         protected final Shape shape;
-        protected final PaintContext pCtx;
-        protected final int gradientSteps;
+        final PaintContext pCtx;
+        final int gradientSteps;
         WritableRaster raster;
 
-        public PathGradientContext(
-              ColorModel cm
-            , Rectangle deviceBounds
-            , Rectangle2D userBounds
-            , AffineTransform xform
-            , RenderingHints hints
+        PathGradientContext(
+                ColorModel cm
+                , Rectangle deviceBounds
+                , Rectangle2D userBounds
+                , AffineTransform xform
+                , RenderingHints hints
         ) {
             shape = (Shape)hints.get(Drawable.GRADIENT_SHAPE);
             if (shape == null) {
@@ -139,7 +142,7 @@
             return childRaster;
         }
 
-        protected int getGradientSteps(Shape gradientShape) {
+        int getGradientSteps(Shape gradientShape) {
             Rectangle rect = gradientShape.getBounds();
             int lower = 1;
             int upper = (int)(Math.max(rect.getWidth(),rect.getHeight())/2.0);
@@ -158,7 +161,7 @@
         
         
         
-        protected void createRaster() {
+        void createRaster() {
             ColorModel cm = getColorModel();
             raster = cm.createCompatibleWritableRaster((int)deviceBounds.getWidth(), (int)deviceBounds.getHeight());
             BufferedImage img = new BufferedImage(cm, raster, false, null);
@@ -168,7 +171,7 @@
             graphics.transform(xform);
 
             Raster img2 = pCtx.getRaster(0, 0, gradientSteps, 1);
-            int rgb[] = new int[cm.getNumComponents()];
+            int[] rgb = new int[cm.getNumComponents()];
 
             for (int i = gradientSteps-1; i>=0; i--) {
                 img2.getPixel(i, 0, rgb);
diff --git a/src/java/org/apache/poi/ss/formula/SheetNameFormatter.java b/src/java/org/apache/poi/ss/formula/SheetNameFormatter.java
index c6e13c3..6e10e67 100644
--- a/src/java/org/apache/poi/ss/formula/SheetNameFormatter.java
+++ b/src/java/org/apache/poi/ss/formula/SheetNameFormatter.java
@@ -59,6 +59,7 @@
      * @param rawSheetName - sheet name
      * @deprecated use <code>appendFormat(StringBuilder out, String rawSheetName)</code> instead
 	 */
+	@Deprecated
 	public static void appendFormat(StringBuffer out, String rawSheetName) {
 		boolean needsQuotes = needsDelimiting(rawSheetName);
 		if(needsQuotes) {
@@ -73,6 +74,7 @@
     /**
      * @deprecated use <code>appendFormat(StringBuilder out, String workbookName, String rawSheetName)</code> instead
      */
+    @Deprecated
 	public static void appendFormat(StringBuffer out, String workbookName, String rawSheetName) {
 		boolean needsQuotes = needsDelimiting(workbookName) || needsDelimiting(rawSheetName);
 		if(needsQuotes) {
@@ -123,7 +125,7 @@
 		}
 	}
 
-    private static void appendAndEscape(Appendable sb, String rawSheetName) {
+    static void appendAndEscape(Appendable sb, String rawSheetName) {
         int len = rawSheetName.length();
         for(int i=0; i<len; i++) {
             char ch = rawSheetName.charAt(i);
@@ -139,7 +141,12 @@
         }
     }
 
-	private static boolean needsDelimiting(String rawSheetName) {
+	/**
+	 * Tell if the given raw sheet name needs screening/delimiting.
+	 * @param rawSheetName the sheet name.
+	 * @return true if the given raw sheet name needs screening/delimiting, false otherwise.
+	 */
+	static boolean needsDelimiting(String rawSheetName) {
 		int len = rawSheetName.length();
 		if(len < 1) {
 			throw new RuntimeException("Zero length string is an invalid sheet name");
diff --git a/src/java/org/apache/poi/ss/formula/SheetRangeAndWorkbookIndexFormatter.java b/src/java/org/apache/poi/ss/formula/SheetRangeAndWorkbookIndexFormatter.java
new file mode 100644
index 0000000..24d39cf
--- /dev/null
+++ b/src/java/org/apache/poi/ss/formula/SheetRangeAndWorkbookIndexFormatter.java
@@ -0,0 +1,73 @@
+/* ====================================================================
+   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.ss.formula;
+
+public class SheetRangeAndWorkbookIndexFormatter {
+    private SheetRangeAndWorkbookIndexFormatter() {
+    }
+
+    public static String format(StringBuilder sb, int workbookIndex, String firstSheetName, String lastSheetName) {
+        if (anySheetNameNeedsEscaping(firstSheetName, lastSheetName)) {
+            return formatWithDelimiting(sb, workbookIndex, firstSheetName, lastSheetName);
+        } else {
+            return formatWithoutDelimiting(sb, workbookIndex, firstSheetName, lastSheetName);
+        }
+    }
+
+    private static String formatWithDelimiting(StringBuilder sb, int workbookIndex, String firstSheetName, String lastSheetName) {
+        sb.append('\'');
+        if (workbookIndex >= 0) {
+            sb.append('[');
+            sb.append(workbookIndex);
+            sb.append(']');
+        }
+
+        SheetNameFormatter.appendAndEscape(sb, firstSheetName);
+
+        if (lastSheetName != null) {
+            sb.append(':');
+            SheetNameFormatter.appendAndEscape(sb, lastSheetName);
+        }
+
+        sb.append('\'');
+        return sb.toString();
+    }
+
+    private static String formatWithoutDelimiting(StringBuilder sb, int workbookIndex, String firstSheetName, String lastSheetName) {
+        if (workbookIndex >= 0) {
+            sb.append('[');
+            sb.append(workbookIndex);
+            sb.append(']');
+        }
+
+        sb.append(firstSheetName);
+
+        if (lastSheetName != null) {
+            sb.append(':');
+            sb.append(lastSheetName);
+        }
+
+        return sb.toString();
+    }
+
+    private static boolean anySheetNameNeedsEscaping(String firstSheetName, String lastSheetName) {
+        boolean anySheetNameNeedsDelimiting = firstSheetName != null && SheetNameFormatter.needsDelimiting(firstSheetName);
+        anySheetNameNeedsDelimiting |= lastSheetName != null && SheetNameFormatter.needsDelimiting(lastSheetName);
+        return anySheetNameNeedsDelimiting;
+    }
+}
diff --git a/src/java/org/apache/poi/ss/formula/ptg/Area3DPxg.java b/src/java/org/apache/poi/ss/formula/ptg/Area3DPxg.java
index 65f59e8..41bea0c 100644
--- a/src/java/org/apache/poi/ss/formula/ptg/Area3DPxg.java
+++ b/src/java/org/apache/poi/ss/formula/ptg/Area3DPxg.java
@@ -20,6 +20,7 @@
 import org.apache.poi.ss.SpreadsheetVersion;
 import org.apache.poi.ss.formula.SheetIdentifier;
 import org.apache.poi.ss.formula.SheetNameFormatter;
+import org.apache.poi.ss.formula.SheetRangeAndWorkbookIndexFormatter;
 import org.apache.poi.ss.formula.SheetRangeIdentifier;
 import org.apache.poi.ss.util.AreaReference;
 import org.apache.poi.util.LittleEndianOutput;
@@ -102,16 +103,8 @@
     
     public String toFormulaString() {
         StringBuilder sb = new StringBuilder(64);
-        if (externalWorkbookNumber >= 0) {
-            sb.append('[');
-            sb.append(externalWorkbookNumber);
-            sb.append(']');
-        }
-        SheetNameFormatter.appendFormat(sb, firstSheetName);
-        if (lastSheetName != null) {
-            sb.append(':');
-            SheetNameFormatter.appendFormat(sb, lastSheetName);
-        }
+
+        SheetRangeAndWorkbookIndexFormatter.format(sb, externalWorkbookNumber, firstSheetName, lastSheetName);
         sb.append('!');
         sb.append(formatReferenceAsString());
         return sb.toString();
diff --git a/src/java/org/apache/poi/ss/formula/ptg/Ref3DPxg.java b/src/java/org/apache/poi/ss/formula/ptg/Ref3DPxg.java
index 67f73b3..12e7e54 100644
--- a/src/java/org/apache/poi/ss/formula/ptg/Ref3DPxg.java
+++ b/src/java/org/apache/poi/ss/formula/ptg/Ref3DPxg.java
@@ -18,7 +18,7 @@
 package org.apache.poi.ss.formula.ptg;
 
 import org.apache.poi.ss.formula.SheetIdentifier;
-import org.apache.poi.ss.formula.SheetNameFormatter;
+import org.apache.poi.ss.formula.SheetRangeAndWorkbookIndexFormatter;
 import org.apache.poi.ss.formula.SheetRangeIdentifier;
 import org.apache.poi.ss.util.CellReference;
 import org.apache.poi.util.LittleEndianOutput;
@@ -101,18 +101,8 @@
 
     public String toFormulaString() {
         StringBuilder sb = new StringBuilder(64);
-        if (externalWorkbookNumber >= 0) {
-            sb.append('[');
-            sb.append(externalWorkbookNumber);
-            sb.append(']');
-        }
-        if (firstSheetName != null) {
-            SheetNameFormatter.appendFormat(sb, firstSheetName);
-        }
-        if (lastSheetName != null) {
-            sb.append(':');
-            SheetNameFormatter.appendFormat(sb, lastSheetName);
-        }
+
+        SheetRangeAndWorkbookIndexFormatter.format(sb, externalWorkbookNumber, firstSheetName, lastSheetName);
         sb.append('!');
         sb.append(formatReferenceAsString());
         return sb.toString();
diff --git a/src/java/org/apache/poi/util/RecordFormatException.java b/src/java/org/apache/poi/util/RecordFormatException.java
index 2bc4ba3..65bea58 100644
--- a/src/java/org/apache/poi/util/RecordFormatException.java
+++ b/src/java/org/apache/poi/util/RecordFormatException.java
@@ -45,8 +45,8 @@
      * be thrown.  If assertTrue is <code>false</code>, this will throw this
      * exception with the message.
      *
-     * @param assertTrue
-     * @param message
+     * @param assertTrue If false, the exception is thrown, if true, no action is performed
+     * @param message The message to include in the thrown exception
      */
     public static void check(boolean assertTrue, String message) {
         if (! assertTrue) {
diff --git a/src/ooxml/java/org/apache/poi/ooxml/POIXMLDocumentPart.java b/src/ooxml/java/org/apache/poi/ooxml/POIXMLDocumentPart.java
index 54fa790..c8e1a27 100644
--- a/src/ooxml/java/org/apache/poi/ooxml/POIXMLDocumentPart.java
+++ b/src/ooxml/java/org/apache/poi/ooxml/POIXMLDocumentPart.java
@@ -615,7 +615,7 @@
     protected void read(POIXMLFactory factory, Map<PackagePart, POIXMLDocumentPart> context) throws OpenXML4JException {
         PackagePart pp = getPackagePart();
 
-        if (pp.getContentType().equals(XWPFRelation.TEMPLATE.getContentType())) {
+        if (pp.getContentType().equals(XWPFRelation.GLOSSARY_DOCUMENT.getContentType())) {
             logger.log(POILogger.WARN,
                     "POI does not currently support template.main+xml (glossary) parts.  " +
                     "Skipping this part for now.");
diff --git a/src/ooxml/java/org/apache/poi/ooxml/extractor/CommandLineTextExtractor.java b/src/ooxml/java/org/apache/poi/ooxml/extractor/CommandLineTextExtractor.java
index 999abd4..c3d429b 100644
--- a/src/ooxml/java/org/apache/poi/ooxml/extractor/CommandLineTextExtractor.java
+++ b/src/ooxml/java/org/apache/poi/ooxml/extractor/CommandLineTextExtractor.java
@@ -22,41 +22,37 @@
 
 /**
  * A command line wrapper around {@link ExtractorFactory}, useful
- *  for when debugging.
+ * for when debugging.
  */
 public class CommandLineTextExtractor {
-   public static final String DIVIDER = "=======================";
-   
-   public static void main(String[] args) throws Exception {
-      if(args.length < 1) {
-         System.err.println("Use:");
-         System.err.println("   CommandLineTextExtractor <filename> [filename] [filename]");
-         System.exit(1);
-      }
+    public static final String DIVIDER = "=======================";
 
-       for (String arg : args) {
-           System.out.println(DIVIDER);
+    public static void main(String[] args) throws Exception {
+        if (args.length < 1) {
+            System.err.println("Use:");
+            System.err.println("   CommandLineTextExtractor <filename> [filename] [filename]");
+            System.exit(1);
+        }
 
-           File f = new File(arg);
-           System.out.println(f);
+        for (String arg : args) {
+            System.out.println(DIVIDER);
 
-           POITextExtractor extractor =
-                   ExtractorFactory.createExtractor(f);
-           try {
-               POITextExtractor metadataExtractor =
-                       extractor.getMetadataTextExtractor();
+            File f = new File(arg);
+            System.out.println(f);
 
-               System.out.println("   " + DIVIDER);
-               String metaData = metadataExtractor.getText();
-               System.out.println(metaData);
-               System.out.println("   " + DIVIDER);
-               String text = extractor.getText();
-               System.out.println(text);
-               System.out.println(DIVIDER);
-               System.out.println("Had " + metaData.length() + " characters of metadata and " + text.length() + " characters of text");
-           } finally {
-               extractor.close();
-           }
-       }
-   }
+            try (POITextExtractor extractor = ExtractorFactory.createExtractor(f)) {
+                POITextExtractor metadataExtractor =
+                        extractor.getMetadataTextExtractor();
+
+                System.out.println("   " + DIVIDER);
+                String metaData = metadataExtractor.getText();
+                System.out.println(metaData);
+                System.out.println("   " + DIVIDER);
+                String text = extractor.getText();
+                System.out.println(text);
+                System.out.println(DIVIDER);
+                System.out.println("Had " + metaData.length() + " characters of metadata and " + text.length() + " characters of text");
+            }
+        }
+    }
 }
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/ZipHelper.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/ZipHelper.java
index 094e89c..0169909 100644
--- a/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/ZipHelper.java
+++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/ZipHelper.java
@@ -154,8 +154,6 @@
                 "The supplied data appears to be a raw XML file. " +
                 "Formats such as Office 2003 XML are not supported");
         default:
-        case OOXML:
-        case UNKNOWN:
             // Don't check for a Zip header, as to maintain backwards
             //  compatibility we need to let them seek over junk at the
             //  start before beginning processing.
diff --git a/src/ooxml/java/org/apache/poi/openxml4j/util/ZipInputStreamZipEntrySource.java b/src/ooxml/java/org/apache/poi/openxml4j/util/ZipInputStreamZipEntrySource.java
index e185239..0a56966 100644
--- a/src/ooxml/java/org/apache/poi/openxml4j/util/ZipInputStreamZipEntrySource.java
+++ b/src/ooxml/java/org/apache/poi/openxml4j/util/ZipInputStreamZipEntrySource.java
@@ -38,7 +38,7 @@
 	
 	/**
 	 * Reads all the entries from the ZipInputStream 
-	 *  into memory, and closes the source stream.
+	 *  into memory, and don't close (since POI 4.0.1) the source stream.
 	 * We'll then eat lots of memory, but be able to
 	 *  work with the entries at-will.
 	 */
@@ -50,7 +50,6 @@
 			}
 			zipEntries.put(zipEntry.getName(), new ZipArchiveFakeEntry(zipEntry, inp));
 		}
-		inp.close();
 	}
 
 	@Override
diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureShape.java
index 97b4ab8..9f07b18 100644
--- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureShape.java
+++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureShape.java
@@ -170,16 +170,24 @@
     
     @SuppressWarnings("WeakerAccess")
     protected String getBlipLink(){
-        String link = getBlip().getLink();
-        if (link.isEmpty()) return null;
-        return link;
+        CTBlip blip = getBlip();
+        if (blip != null) {
+            String link = blip.getLink();
+            return (link.isEmpty()) ? null : link;
+        } else {
+            return null;
+        }
     }
 
     @SuppressWarnings("WeakerAccess")
     protected String getBlipId(){
-        String id = getBlip().getEmbed();
-        if (id.isEmpty()) return null;
-        return id;
+        CTBlip blip = getBlip();
+        if (blip != null) {
+            String id = blip.getEmbed();
+            return (id.isEmpty()) ? null : id;
+        } else {
+            return null;
+        }
     }
 
     @Override
diff --git a/src/ooxml/testcases/org/apache/poi/ooxml/TestPOIXMLProperties.java b/src/ooxml/testcases/org/apache/poi/ooxml/TestPOIXMLProperties.java
index d91b819..1ad2a63 100644
--- a/src/ooxml/testcases/org/apache/poi/ooxml/TestPOIXMLProperties.java
+++ b/src/ooxml/testcases/org/apache/poi/ooxml/TestPOIXMLProperties.java
@@ -19,6 +19,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -89,7 +90,7 @@
         XSSFWorkbook newWorkbook =
                 XSSFTestDataSamples.writeOutAndReadBack(workbook);
         workbook.close();
-        assertTrue(workbook != newWorkbook);
+        assertNotSame(workbook, newWorkbook);
 
 
         POIXMLProperties newProps = newWorkbook.getProperties();
@@ -158,7 +159,7 @@
         p = ctProps.getPropertyArray(3);
         assertEquals("{D5CDD505-2E9C-101B-9397-08002B2CF9AE}", p.getFmtid());
         assertEquals("test-4", p.getName());
-        assertEquals(true, p.getBool());
+        assertTrue(p.getBool());
         assertEquals(5, p.getPid());
         
         wb2.close();
diff --git a/src/ooxml/testcases/org/apache/poi/xddf/usermodel/TestNecessaryOOXMLClasses.java b/src/ooxml/testcases/org/apache/poi/xddf/usermodel/TestNecessaryOOXMLClasses.java
index 74ec1bb..6cbf24a 100644
--- a/src/ooxml/testcases/org/apache/poi/xddf/usermodel/TestNecessaryOOXMLClasses.java
+++ b/src/ooxml/testcases/org/apache/poi/xddf/usermodel/TestNecessaryOOXMLClasses.java
@@ -87,6 +87,10 @@
         Assert.assertNotNull(ctLblAlgn);
         CTDashStopList ctDashStopList = CTDashStopList.Factory.newInstance();
         Assert.assertNotNull(ctDashStopList);
+        STDispBlanksAs stDashBlanksAs = STDispBlanksAs.Factory.newInstance();
+        Assert.assertNotNull(stDashBlanksAs);
+        CTDispBlanksAs ctDashBlanksAs = CTDispBlanksAs.Factory.newInstance();
+        Assert.assertNotNull(ctDashBlanksAs);
 
         STLblAlgn.Enum e1 = STLblAlgn.Enum.forString("ctr");
         Assert.assertNotNull(e1);
@@ -100,6 +104,8 @@
         Assert.assertNotNull(e5);
         STMarkerStyle.Enum e6 = STMarkerStyle.Enum.forString("circle");
         Assert.assertNotNull(e6);
+        STDispBlanksAs.Enum e7 = STDispBlanksAs.Enum.forString("span");
+        Assert.assertNotNull(e7);
 
         CTTextBulletTypefaceFollowText ctTextBulletTypefaceFollowText = CTTextBulletTypefaceFollowText.Factory.newInstance();
         Assert.assertNotNull(ctTextBulletTypefaceFollowText);
diff --git a/src/ooxml/testcases/org/apache/poi/xdgf/extractor/TestXDGFVisioExtractor.java b/src/ooxml/testcases/org/apache/poi/xdgf/extractor/TestXDGFVisioExtractor.java
index 6a3369e..c261e2c 100644
--- a/src/ooxml/testcases/org/apache/poi/xdgf/extractor/TestXDGFVisioExtractor.java
+++ b/src/ooxml/testcases/org/apache/poi/xdgf/extractor/TestXDGFVisioExtractor.java
@@ -42,7 +42,7 @@
     }
     
     @After
-    public void closeResoures() throws IOException {
+    public void closeResources() throws IOException {
         if(xml != null) {
             xml.close();
         }
diff --git a/src/ooxml/testcases/org/apache/poi/xslf/TestXSLFBugs.java b/src/ooxml/testcases/org/apache/poi/xslf/TestXSLFBugs.java
index 48b0816..8dbc8ca 100644
--- a/src/ooxml/testcases/org/apache/poi/xslf/TestXSLFBugs.java
+++ b/src/ooxml/testcases/org/apache/poi/xslf/TestXSLFBugs.java
@@ -17,12 +17,7 @@
 package org.apache.poi.xslf;
 
 import static org.apache.poi.POITestCase.assertContains;
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
+import static org.junit.Assert.*;
 
 import java.awt.Color;
 import java.awt.Dimension;
@@ -94,6 +89,25 @@
     private static final POIDataSamples slTests = POIDataSamples.getSlideShowInstance();
 
     @Test
+    public void bug62929() throws Exception {
+        try(XMLSlideShow ss1 = XSLFTestDataSamples.openSampleDocument("missing-blip-fill.pptx")) {
+            assertEquals(1, ss1.getSlides().size());
+
+            XSLFSlide slide = ss1.getSlides().get(0);
+
+            assertEquals(slide.getShapes().size(), 1);
+
+            XSLFPictureShape picture = (XSLFPictureShape)slide.getShapes().get(0);
+
+            assertEquals(picture.getShapeId(), 662);
+            assertFalse(picture.isExternalLinkedPicture());
+            assertNull(picture.getPictureData());
+            assertNull(picture.getPictureLink());
+            assertNull(picture.getClipping());
+        }
+    }
+
+    @Test
     public void bug62736() throws Exception {
         XMLSlideShow ss1 = XSLFTestDataSamples.openSampleDocument("bug62736.pptx");
 
diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java
index 94205da..199a9f6 100644
--- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java
+++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java
@@ -47,7 +47,7 @@
     private static final POIDataSamples samples = POIDataSamples.getSlideShowInstance();
     private static final File basedir = null;
     private static final String files =
-        "53446.ppt, alterman_security.ppt, alterman_security.pptx, KEY02.pptx, themes.pptx, backgrounds.pptx, layouts.pptx, sample.pptx, shapes.pptx, 54880_chinese.ppt";
+        "53446.ppt, alterman_security.ppt, alterman_security.pptx, KEY02.pptx, themes.pptx, backgrounds.pptx, layouts.pptx, sample.pptx, shapes.pptx, 54880_chinese.ppt, keyframes.pptx";
 
         
     
diff --git a/src/ooxml/testcases/org/apache/poi/xssf/XSSFTestDataSamples.java b/src/ooxml/testcases/org/apache/poi/xssf/XSSFTestDataSamples.java
index 0f03af7..b981b99 100644
--- a/src/ooxml/testcases/org/apache/poi/xssf/XSSFTestDataSamples.java
+++ b/src/ooxml/testcases/org/apache/poi/xssf/XSSFTestDataSamples.java
@@ -70,7 +70,7 @@
      * @param wb the workbook to write
      * @param testName a fragment of the filename
      * @return the location where the workbook was saved
-     * @throws IOException
+     * @throws IOException If writing the file fails
      */
     public static <R extends Workbook> File writeOut(R wb, String testName) throws IOException {
         final File file = getOutputFile(testName);
@@ -104,7 +104,9 @@
             file = TempFile.createTempFile(testName, ".xlsx");
         }
         if (file.exists()) {
-            file.delete();
+            if(!file.delete()) {
+                throw new IOException("Could not delete file " + file);
+            }
         }
         return file;
     }
@@ -114,7 +116,7 @@
      *
      * @param wb the workbook to write
      * @return the memory buffer
-     * @throws IOException
+     * @throws IOException If writing the file fails
      */
     public static <R extends Workbook> ByteArrayOutputStream writeOut(R wb) throws IOException {
         ByteArrayOutputStream out = new ByteArrayOutputStream(8192);
@@ -137,7 +139,7 @@
      * to avoid creating a temporary file. However, this may complicate the calling
      * code to avoid having the workbook, BAOS, and BAIS open at the same time.
      *
-     * @param wb
+     * @param wb The workbook to write out, it is closed after the call.
      * @param testName file name to be used to write to a file. This file will be cleaned up by a call to readBack(String)
      * @return workbook location
      * @throws RuntimeException if {@link #TEST_OUTPUT_DIR} System property is not set
@@ -161,18 +163,13 @@
      *
      * @param wb the workbook to write
      * @return the memory buffer
-     * @throws IOException
+     * @throws RuntimeException If writing the file fails
      */
-    public static <R extends Workbook> ByteArrayOutputStream writeOutAndClose(R wb) {
-        try {
-            ByteArrayOutputStream out = writeOut(wb);
-            // Do not close the workbook if there was a problem writing the workbook
-            wb.close();
-            return out;
-        }
-        catch (final IOException e) {
-            throw new RuntimeException(e);
-        }
+    public static <R extends Workbook> ByteArrayOutputStream writeOutAndClose(R wb) throws IOException {
+        ByteArrayOutputStream out = writeOut(wb);
+        // Do not close the workbook if there was a problem writing the workbook
+        wb.close();
+        return out;
     }
 
     /**
@@ -183,12 +180,14 @@
      *
      * @param file the workbook file to read and delete
      * @return the read back workbook
-     * @throws IOException
+     * @throws IOException If reading or deleting the file fails
      */
     public static XSSFWorkbook readBackAndDelete(File file) throws IOException {
         XSSFWorkbook wb = readBack(file);
         // do not delete the file if there's an error--might be helpful for debugging
-        file.delete();
+        if(!file.delete()) {
+            throw new IOException("Could not delete file " + file + " after reading");
+        }
         return wb;
     }
 
@@ -198,16 +197,12 @@
      *
      * @param file the workbook file to read
      * @return the read back workbook
-     * @throws IOException
+     * @throws IOException If reading the file fails
      */
     public static XSSFWorkbook readBack(File file) throws IOException {
-        InputStream in = new FileInputStream(file);
-        try {
+        try (InputStream in = new FileInputStream(file)) {
             return new XSSFWorkbook(in);
         }
-        finally {
-            in.close();
-        }
     }
 
     /**
@@ -216,17 +211,13 @@
      *
      * @param out the output stream to read back from
      * @return the read back workbook
-     * @throws IOException
+     * @throws IOException If reading the file fails
      */
     public static XSSFWorkbook readBack(ByteArrayOutputStream out) throws IOException {
-        InputStream is = new ByteArrayInputStream(out.toByteArray());
-        out.close();
-        try {
+        try (InputStream is = new ByteArrayInputStream(out.toByteArray())) {
+            out.close();
             return new XSSFWorkbook(is);
         }
-        finally {
-            is.close();
-        }
     }
 
     /**
diff --git a/src/ooxml/testcases/org/apache/poi/xwpf/extractor/TestXWPFWordExtractor.java b/src/ooxml/testcases/org/apache/poi/xwpf/extractor/TestXWPFWordExtractor.java
index 0fcccd1..30d9430 100644
--- a/src/ooxml/testcases/org/apache/poi/xwpf/extractor/TestXWPFWordExtractor.java
+++ b/src/ooxml/testcases/org/apache/poi/xwpf/extractor/TestXWPFWordExtractor.java
@@ -452,4 +452,12 @@
         //once we add processing for this, we can change this to contains
         assertNotContained(txt, "table rows");
     }
+
+    public void testPartsInTemplate() throws IOException {
+        XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("60316b.dotx");
+        XWPFWordExtractor extractor = new XWPFWordExtractor(doc);
+        String txt = extractor.getText();
+        assertContains(txt, "header 2");
+        assertContains(txt, "footer 1");
+    }
 }
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/CurrentUserAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/CurrentUserAtom.java
index cedfbb0..26ba59d 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/record/CurrentUserAtom.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/record/CurrentUserAtom.java
@@ -20,6 +20,8 @@
 
 package org.apache.poi.hslf.record;
 
+import static org.apache.poi.hslf.usermodel.HSLFSlideShow.PP95_DOCUMENT;
+
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
@@ -143,7 +145,7 @@
 		// See how long it is. If it's under 28 bytes long, we can't
 		//  read it
 		if(_contents.length < 28) {
-		    boolean isPP95 = dir.hasEntry("PP40");
+		    boolean isPP95 = dir.hasEntry(PP95_DOCUMENT);
 		    // PPT95 has 4 byte size, then data
 			if (!isPP95 && _contents.length >= 4) {
 				int size = LittleEndian.getInt(_contents);
diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShow.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShow.java
index ecc360e..2f0a116 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShow.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShow.java
@@ -71,6 +71,7 @@
 
 	/** Powerpoint document entry/stream name */
     public static final String POWERPOINT_DOCUMENT = "PowerPoint Document";
+	public static final String PP95_DOCUMENT = "PP40";
 
     enum LoadSavePhase {
         INIT, LOADED
diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShowImpl.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShowImpl.java
index 6f00755..420ae47 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShowImpl.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShowImpl.java
@@ -17,6 +17,9 @@
 
 package org.apache.poi.hslf.usermodel;
 
+import static org.apache.poi.hslf.usermodel.HSLFSlideShow.POWERPOINT_DOCUMENT;
+import static org.apache.poi.hslf.usermodel.HSLFSlideShow.PP95_DOCUMENT;
+
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.Closeable;
@@ -35,6 +38,7 @@
 import org.apache.poi.POIDocument;
 import org.apache.poi.hslf.exceptions.CorruptPowerPointFileException;
 import org.apache.poi.hslf.exceptions.HSLFException;
+import org.apache.poi.hslf.exceptions.OldPowerPointFormatException;
 import org.apache.poi.hslf.record.CurrentUserAtom;
 import org.apache.poi.hslf.record.DocumentEncryptionAtom;
 import org.apache.poi.hslf.record.ExOleObjStg;
@@ -183,13 +187,18 @@
      * @throws IOException when the powerpoint can't be read
      */
     private void readPowerPointStream() throws IOException {
+        final DirectoryNode dir = getDirectory();
+
+        if (!dir.hasEntry(POWERPOINT_DOCUMENT) && dir.hasEntry(PP95_DOCUMENT)) {
+            throw new OldPowerPointFormatException("You seem to have supplied a PowerPoint95 file, which isn't supported");
+        }
+
         // Get the main document stream
-        DocumentEntry docProps =
-                (DocumentEntry) getDirectory().getEntry(HSLFSlideShow.POWERPOINT_DOCUMENT);
+        DocumentEntry docProps = (DocumentEntry)dir.getEntry(POWERPOINT_DOCUMENT);
 
         // Grab the document stream
         int len = docProps.getSize();
-        try (InputStream is = getDirectory().createDocumentInputStream(HSLFSlideShow.POWERPOINT_DOCUMENT)) {
+        try (InputStream is = dir.createDocumentInputStream(docProps)) {
             _docstream = IOUtils.toByteArray(is, len);
         }
     }
@@ -665,8 +674,8 @@
 
         // Write the PPT stream into the POIFS layer
         ByteArrayInputStream bais = new ByteArrayInputStream(_docstream);
-        outFS.createOrUpdateDocument(bais, HSLFSlideShow.POWERPOINT_DOCUMENT);
-        writtenEntries.add(HSLFSlideShow.POWERPOINT_DOCUMENT);
+        outFS.createOrUpdateDocument(bais, POWERPOINT_DOCUMENT);
+        writtenEntries.add(POWERPOINT_DOCUMENT);
 
         currentUser.setEncrypted(encryptedSS.getDocumentEncryptionAtom() != null);
         currentUser.writeToFS(outFS);
diff --git a/src/testcases/org/apache/poi/poifs/filesystem/TestFileMagic.java b/src/testcases/org/apache/poi/poifs/filesystem/TestFileMagic.java
new file mode 100644
index 0000000..4dba721
--- /dev/null
+++ b/src/testcases/org/apache/poi/poifs/filesystem/TestFileMagic.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.poifs.filesystem;
+
+import org.apache.commons.codec.Charsets;
+import org.apache.poi.POIDataSamples;
+import org.junit.Test;
+
+import java.io.BufferedInputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import static org.junit.Assert.*;
+
+public class TestFileMagic {
+    @Test
+    public void testFileMagic() {
+        assertEquals(FileMagic.XML, FileMagic.valueOf("XML"));
+        assertEquals(FileMagic.XML, FileMagic.valueOf("<?xml".getBytes(Charsets.UTF_8)));
+
+        assertEquals(FileMagic.HTML, FileMagic.valueOf("HTML"));
+        assertEquals(FileMagic.HTML, FileMagic.valueOf("<!DOCTYP".getBytes(Charsets.UTF_8)));
+        assertEquals(FileMagic.HTML, FileMagic.valueOf("<!DOCTYPE".getBytes(Charsets.UTF_8)));
+        assertEquals(FileMagic.HTML, FileMagic.valueOf("<html".getBytes(Charsets.UTF_8)));
+
+        try {
+            FileMagic.valueOf("some string");
+            fail("Should catch exception here");
+        } catch (IllegalArgumentException e) {
+            // expected here
+        }
+    }
+
+    @Test
+    public void testFileMagicFile() throws IOException {
+        assertEquals(FileMagic.OLE2, FileMagic.valueOf(POIDataSamples.getSpreadSheetInstance().getFile("SampleSS.xls")));
+        assertEquals(FileMagic.OOXML, FileMagic.valueOf(POIDataSamples.getSpreadSheetInstance().getFile("SampleSS.xlsx")));
+    }
+
+    @Test
+    public void testFileMagicStream() throws IOException {
+        try (InputStream stream = new BufferedInputStream(new FileInputStream(POIDataSamples.getSpreadSheetInstance().getFile("SampleSS.xls")))) {
+            assertEquals(FileMagic.OLE2, FileMagic.valueOf(stream));
+        }
+        try (InputStream stream = new BufferedInputStream(new FileInputStream(POIDataSamples.getSpreadSheetInstance().getFile("SampleSS.xlsx")))) {
+            assertEquals(FileMagic.OOXML, FileMagic.valueOf(stream));
+        }
+    }
+
+    @Test
+    public void testPrepare() throws IOException {
+        try (InputStream stream = new BufferedInputStream(new FileInputStream(POIDataSamples.getSpreadSheetInstance().getFile("SampleSS.xlsx")))) {
+            assertSame(stream, FileMagic.prepareToCheckMagic(stream));
+        }
+
+        try (InputStream stream = new InputStream() {
+            @Override
+            public int read() {
+                return 0;
+            }
+        }) {
+            assertNotSame(stream, FileMagic.prepareToCheckMagic(stream));
+        }
+    }
+}
diff --git a/src/testcases/org/apache/poi/poifs/filesystem/TestPOIFSFileSystem.java b/src/testcases/org/apache/poi/poifs/filesystem/TestPOIFSFileSystem.java
index 83b085b..1fffcea 100644
--- a/src/testcases/org/apache/poi/poifs/filesystem/TestPOIFSFileSystem.java
+++ b/src/testcases/org/apache/poi/poifs/filesystem/TestPOIFSFileSystem.java
@@ -17,6 +17,7 @@
 
 package org.apache.poi.poifs.filesystem;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -278,4 +279,18 @@
 	private static InputStream openSampleStream(String sampleFileName) {
 		return HSSFTestDataSamples.openSampleFileStream(sampleFileName);
 	}
+
+	@Test
+	public void fileMagics() {
+		for (FileMagic fm : FileMagic.values()) {
+			if (fm == FileMagic.UNKNOWN) {
+				continue;
+			}
+			for (byte[] b : fm.magic) {
+				assertEquals(fm, FileMagic.valueOf(b));
+			}
+		}
+
+		assertEquals(FileMagic.UNKNOWN, FileMagic.valueOf("foobaa".getBytes(UTF_8)));
+	}
 }
diff --git a/src/testcases/org/apache/poi/ss/formula/SheetRangeAndWorkbookIndexFormatterTest.java b/src/testcases/org/apache/poi/ss/formula/SheetRangeAndWorkbookIndexFormatterTest.java
new file mode 100644
index 0000000..43dd236
--- /dev/null
+++ b/src/testcases/org/apache/poi/ss/formula/SheetRangeAndWorkbookIndexFormatterTest.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.ss.formula;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class SheetRangeAndWorkbookIndexFormatterTest {
+    @Test
+    public void noDelimiting_ifASingleSheetNameDoesntNeedDelimiting() {
+        StringBuilder sb = new StringBuilder();
+        String result = SheetRangeAndWorkbookIndexFormatter.format(sb, 0, "noDelimiting", null);
+        assertEquals("[0]noDelimiting", result);
+    }
+
+    @Test
+    public void everythingIsScreened_ifASingleSheetNameNeedsDelimiting() {
+        StringBuilder sb = new StringBuilder();
+        String result = SheetRangeAndWorkbookIndexFormatter.format(sb, 0, "1delimiting", null);
+        assertEquals("'[0]1delimiting'", result);
+    }
+
+    @Test
+    public void noDelimiting_ifBothSheetNamesDontNeedDelimiting() {
+        StringBuilder sb = new StringBuilder();
+        String result = SheetRangeAndWorkbookIndexFormatter.format(sb, 0, "noDelimiting1", "noDelimiting2");
+        assertEquals("[0]noDelimiting1:noDelimiting2", result);
+    }
+
+    @Test
+    public void everythingIsScreened_ifFirstSheetNamesNeedsDelimiting() {
+        StringBuilder sb = new StringBuilder();
+        String result = SheetRangeAndWorkbookIndexFormatter.format(sb, 0, "1delimiting", "noDelimiting");
+        assertEquals("'[0]1delimiting:noDelimiting'", result);
+    }
+
+    @Test
+    public void everythingIsScreened_ifLastSheetNamesNeedsDelimiting() {
+        StringBuilder sb = new StringBuilder();
+        String result = SheetRangeAndWorkbookIndexFormatter.format(sb, 0, "noDelimiting", "1delimiting");
+        assertEquals("'[0]noDelimiting:1delimiting'", result);
+    }
+
+    @Test
+    public void everythingIsScreened_ifBothSheetNamesNeedDelimiting() {
+        StringBuilder sb = new StringBuilder();
+        String result = SheetRangeAndWorkbookIndexFormatter.format(sb, 0, "1delimiting", "2delimiting");
+        assertEquals("'[0]1delimiting:2delimiting'", result);
+    }
+}
diff --git a/test-data/document/60316.docx b/test-data/document/60316.docx
new file mode 100644
index 0000000..78b2b1d
--- /dev/null
+++ b/test-data/document/60316.docx
Binary files differ
diff --git a/test-data/document/60316b.dotx b/test-data/document/60316b.dotx
new file mode 100644
index 0000000..e0e0d58
--- /dev/null
+++ b/test-data/document/60316b.dotx
Binary files differ
diff --git a/test-data/slideshow/keyframes.pptx b/test-data/slideshow/keyframes.pptx
new file mode 100644
index 0000000..e653d64
--- /dev/null
+++ b/test-data/slideshow/keyframes.pptx
Binary files differ
diff --git a/test-data/slideshow/missing-blip-fill.pptx b/test-data/slideshow/missing-blip-fill.pptx
new file mode 100644
index 0000000..62e5f67
--- /dev/null
+++ b/test-data/slideshow/missing-blip-fill.pptx
Binary files differ
diff --git a/test-data/slideshow/pp40only.ppt b/test-data/slideshow/pp40only.ppt
new file mode 100644
index 0000000..bd53490
--- /dev/null
+++ b/test-data/slideshow/pp40only.ppt
Binary files differ