diff --git a/src/java/org/apache/poi/sl/usermodel/Shape.java b/src/java/org/apache/poi/sl/usermodel/Shape.java
index 8afc26fcf..a8ed1cfaa 100644
--- a/src/java/org/apache/poi/sl/usermodel/Shape.java
+++ b/src/java/org/apache/poi/sl/usermodel/Shape.java
@@ -38,7 +38,14 @@ public interface Shape<
* @return the anchor of this shape
*/
Rectangle2D getAnchor();
-
+
+ /**
+ * @return human-readable name of this shape, e.g. "Rectange 3"
+ *
+ * @since POI 4.0.0
+ */
+ String getShapeName();
+
/**
* Convenience method to draw a single shape
*
diff --git a/src/java/org/apache/poi/sl/usermodel/SlideShowFactory.java b/src/java/org/apache/poi/sl/usermodel/SlideShowFactory.java
index 02e9fcb14..5f75258b9 100644
--- a/src/java/org/apache/poi/sl/usermodel/SlideShowFactory.java
+++ b/src/java/org/apache/poi/sl/usermodel/SlideShowFactory.java
@@ -44,7 +44,10 @@ public class SlideShowFactory {
*
* @throws IOException if an error occurs while reading the data
*/
- public static SlideShow,?> create(NPOIFSFileSystem fs) throws IOException {
+ public static <
+ S extends Shape,
+ P extends TextParagraph
+ > SlideShow create(NPOIFSFileSystem fs) throws IOException {
return create(fs, null);
}
@@ -59,7 +62,10 @@ public class SlideShowFactory {
*
* @throws IOException if an error occurs while reading the data
*/
- public static SlideShow,?> create(final NPOIFSFileSystem fs, String password) throws IOException {
+ public static <
+ S extends Shape,
+ P extends TextParagraph
+ > SlideShow create(final NPOIFSFileSystem fs, String password) throws IOException {
return create(fs.getRoot(), password);
}
@@ -72,7 +78,10 @@ public class SlideShowFactory {
*
* @throws IOException if an error occurs while reading the data
*/
- public static SlideShow,?> create(final DirectoryNode root) throws IOException {
+ public static <
+ S extends Shape,
+ P extends TextParagraph
+ > SlideShow create(final DirectoryNode root) throws IOException {
return create(root, null);
}
@@ -88,7 +97,10 @@ public class SlideShowFactory {
*
* @throws IOException if an error occurs while reading the data
*/
- public static SlideShow,?> create(final DirectoryNode root, String password) throws IOException {
+ public static <
+ S extends Shape,
+ P extends TextParagraph
+ > SlideShow create(final DirectoryNode root, String password) throws IOException {
// Encrypted OOXML files go inside OLE2 containers, is this one?
if (root.hasEntry(Decryptor.DEFAULT_POIFS_ENTRY)) {
InputStream stream = null;
@@ -136,7 +148,10 @@ public class SlideShowFactory {
* @throws IOException if an error occurs while reading the data
* @throws EncryptedDocumentException If the SlideShow given is password protected
*/
- public static SlideShow,?> create(InputStream inp) throws IOException, EncryptedDocumentException {
+ public static <
+ S extends Shape,
+ P extends TextParagraph
+ > SlideShow create(InputStream inp) throws IOException, EncryptedDocumentException {
return create(inp, null);
}
@@ -160,7 +175,10 @@ public class SlideShowFactory {
* @throws IOException if an error occurs while reading the data
* @throws EncryptedDocumentException If the wrong password is given for a protected file
*/
- public static SlideShow,?> create(InputStream inp, String password) throws IOException, EncryptedDocumentException {
+ public static <
+ S extends Shape,
+ P extends TextParagraph
+ > SlideShow create(InputStream inp, String password) throws IOException, EncryptedDocumentException {
InputStream is = FileMagic.prepareToCheckMagic(inp);
FileMagic fm = FileMagic.valueOf(is);
@@ -188,7 +206,10 @@ public class SlideShowFactory {
* @throws IOException if an error occurs while reading the data
* @throws EncryptedDocumentException If the SlideShow given is password protected
*/
- public static SlideShow,?> create(File file) throws IOException, EncryptedDocumentException {
+ public static <
+ S extends Shape,
+ P extends TextParagraph
+ > SlideShow create(File file) throws IOException, EncryptedDocumentException {
return create(file, null);
}
@@ -207,7 +228,10 @@ public class SlideShowFactory {
* @throws IOException if an error occurs while reading the data
* @throws EncryptedDocumentException If the wrong password is given for a protected file
*/
- public static SlideShow,?> create(File file, String password) throws IOException, EncryptedDocumentException {
+ public static <
+ S extends Shape,
+ P extends TextParagraph
+ > SlideShow create(File file, String password) throws IOException, EncryptedDocumentException {
return create(file, password, false);
}
@@ -228,7 +252,10 @@ public class SlideShowFactory {
* @throws IOException if an error occurs while reading the data
* @throws EncryptedDocumentException If the wrong password is given for a protected file
*/
- public static SlideShow,?> create(File file, String password, boolean readOnly) throws IOException, EncryptedDocumentException {
+ public static <
+ S extends Shape,
+ P extends TextParagraph
+ > SlideShow create(File file, String password, boolean readOnly) throws IOException, EncryptedDocumentException {
if (!file.exists()) {
throw new FileNotFoundException(file.toString());
}
@@ -246,15 +273,24 @@ public class SlideShowFactory {
}
}
- protected static SlideShow,?> createHSLFSlideShow(Object... args) throws IOException, EncryptedDocumentException {
+ private static <
+ S extends Shape,
+ P extends TextParagraph
+ > SlideShow createHSLFSlideShow(Object... args) throws IOException, EncryptedDocumentException {
return createSlideShow("org.apache.poi.hslf.usermodel.HSLFSlideShowFactory", args);
}
- protected static SlideShow,?> createXSLFSlideShow(Object... args) throws IOException, EncryptedDocumentException {
+ private static <
+ S extends Shape,
+ P extends TextParagraph
+ > SlideShow createXSLFSlideShow(Object... args) throws IOException, EncryptedDocumentException {
return createSlideShow("org.apache.poi.xslf.usermodel.XSLFSlideShowFactory", args);
}
-
- protected static SlideShow,?> createSlideShow(String factoryClass, Object args[]) throws IOException, EncryptedDocumentException {
+
+ private static <
+ S extends Shape,
+ P extends TextParagraph
+ > SlideShow createSlideShow(String factoryClass, Object args[]) throws IOException, EncryptedDocumentException {
try {
Class> clazz = Thread.currentThread().getContextClassLoader().loadClass(factoryClass);
Class> argsClz[] = new Class>[args.length];
@@ -269,7 +305,7 @@ public class SlideShowFactory {
argsClz[i++] = c;
}
Method m = clazz.getMethod("createSlideShow", argsClz);
- return (SlideShow,?>)m.invoke(null, args);
+ return (SlideShow)m.invoke(null, args);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof IOException) {
diff --git a/src/java/org/apache/poi/util/StringUtil.java b/src/java/org/apache/poi/util/StringUtil.java
index ee0fe2721..302e53257 100644
--- a/src/java/org/apache/poi/util/StringUtil.java
+++ b/src/java/org/apache/poi/util/StringUtil.java
@@ -652,4 +652,76 @@ public class StringUtil {
}
return count;
}
+
+
+ /**
+ * Given a byte array of 16-bit unicode characters in Little Endian
+ * format (most important byte last), return a Java String representation
+ * of it.
+ *
+ * Scans the byte array for two continous 0 bytes and returns the string before.
+ *
+ *
+ * #61881: there seem to be programs out there, which write the 0-termination also
+ * at the beginning of the string. Check if the next two bytes contain a valid ascii char
+ * and correct the _recdata with a '?' char
+ *
+ *
+ * @param string the byte array to be converted
+ * @param offset the initial offset into the
+ * byte array. it is assumed that string[ offset ] and string[ offset +
+ * 1 ] contain the first 16-bit unicode character
+ * @param len the max. length of the final string
+ * @return the converted string, never null
.
+ * @throws ArrayIndexOutOfBoundsException if offset is out of bounds for
+ * the byte array (i.e., is negative or is greater than or equal to
+ * string.length)
+ * @throws IllegalArgumentException if len is too large (i.e.,
+ * there is not enough data in string to create a String of that
+ * length)
+ */
+ public static String getFromUnicodeLE0Terminated(
+ final byte[] string,
+ final int offset,
+ final int len)
+ throws ArrayIndexOutOfBoundsException, IllegalArgumentException {
+ if ((offset < 0) || (offset >= string.length)) {
+ throw new ArrayIndexOutOfBoundsException("Illegal offset " + offset + " (String data is of length " + string.length + ")");
+ }
+
+ if ((len < 0) || (((string.length - offset) / 2) < len)) {
+ throw new IllegalArgumentException("Illegal length " + len);
+ }
+
+ final int newOffset;
+ final int newMaxLen;
+ final String prefix;
+
+ // #61881 - for now we only check the first char
+ if (len > 0 && string[offset] == 0 && string[offset+1] == 0) {
+ newOffset = offset+2;
+ prefix = "?";
+
+ // check if the next char is garbage and limit the len if necessary
+ final int cp = (len > 1) ? LittleEndian.getShort(string, offset+2) : 0;
+ newMaxLen = Character.isJavaIdentifierPart(cp) ? len-1 : 0;
+ } else {
+ newOffset = offset;
+ prefix = "";
+ newMaxLen = len;
+ }
+
+ int newLen = 0;
+
+ // loop until we find a null-terminated end
+ for(; newLen < newMaxLen; newLen++) {
+ if (string[newOffset + newLen * 2] == 0 && string[newOffset + newLen * 2 + 1] == 0) {
+ break;
+ }
+ }
+ newLen = Math.min(newLen, newMaxLen);
+
+ return prefix + ((newLen == 0) ? "" : new String(string, newOffset, newLen * 2, UTF16LE));
+ }
+
}
diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java
index 35a54e96e..82449d846 100644
--- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java
+++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java
@@ -97,9 +97,7 @@ public abstract class XSLFShape implements Shape