* @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
*
*
* @throws IOException if an error occurs while reading the data
*/
- public static SlideShow<?,?> create(NPOIFSFileSystem fs) throws IOException {
+ public static <
+ S extends Shape<S,P>,
+ P extends TextParagraph<S,P,? extends TextRun>
+ > SlideShow<S,P> create(NPOIFSFileSystem fs) throws IOException {
return create(fs, null);
}
*
* @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<S,P>,
+ P extends TextParagraph<S,P,? extends TextRun>
+ > SlideShow<S,P> create(final NPOIFSFileSystem fs, String password) throws IOException {
return create(fs.getRoot(), password);
}
*
* @throws IOException if an error occurs while reading the data
*/
- public static SlideShow<?,?> create(final DirectoryNode root) throws IOException {
+ public static <
+ S extends Shape<S,P>,
+ P extends TextParagraph<S,P,? extends TextRun>
+ > SlideShow<S,P> create(final DirectoryNode root) throws IOException {
return create(root, null);
}
*
* @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<S,P>,
+ P extends TextParagraph<S,P,? extends TextRun>
+ > SlideShow<S,P> 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;
* @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<S,P>,
+ P extends TextParagraph<S,P,? extends TextRun>
+ > SlideShow<S,P> create(InputStream inp) throws IOException, EncryptedDocumentException {
return create(inp, null);
}
* @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<S,P>,
+ P extends TextParagraph<S,P,? extends TextRun>
+ > SlideShow<S,P> create(InputStream inp, String password) throws IOException, EncryptedDocumentException {
InputStream is = FileMagic.prepareToCheckMagic(inp);
FileMagic fm = FileMagic.valueOf(is);
* @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<S,P>,
+ P extends TextParagraph<S,P,? extends TextRun>
+ > SlideShow<S,P> create(File file) throws IOException, EncryptedDocumentException {
return create(file, null);
}
* @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<S,P>,
+ P extends TextParagraph<S,P,? extends TextRun>
+ > SlideShow<S,P> create(File file, String password) throws IOException, EncryptedDocumentException {
return create(file, password, false);
}
* @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<S,P>,
+ P extends TextParagraph<S,P,? extends TextRun>
+ > SlideShow<S,P> create(File file, String password, boolean readOnly) throws IOException, EncryptedDocumentException {
if (!file.exists()) {
throw new FileNotFoundException(file.toString());
}
}
}
- protected static SlideShow<?,?> createHSLFSlideShow(Object... args) throws IOException, EncryptedDocumentException {
+ private static <
+ S extends Shape<S,P>,
+ P extends TextParagraph<S,P,? extends TextRun>
+ > SlideShow<S,P> 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<S,P>,
+ P extends TextParagraph<S,P,? extends TextRun>
+ > SlideShow<S,P> 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<S,P>,
+ P extends TextParagraph<S,P,? extends TextRun>
+ > SlideShow<S,P> createSlideShow(String factoryClass, Object args[]) throws IOException, EncryptedDocumentException {
try {
Class<?> clazz = Thread.currentThread().getContextClassLoader().loadClass(factoryClass);
Class<?> argsClz[] = new Class<?>[args.length];
argsClz[i++] = c;
}
Method m = clazz.getMethod("createSlideShow", argsClz);
- return (SlideShow<?,?>)m.invoke(null, args);
+ return (SlideShow<S,P>)m.invoke(null, args);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof IOException) {
}
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.
+ * <p>
+ *
+ * #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 <code>null</code>.
+ * @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));
+ }
+
}
return _sheet;
}
- /**
- * @return human-readable name of this shape, e.g. "Rectange 3"
- */
+ @Override
public String getShapeName(){
return getCNvPr().getName();
}
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.opc.PackageAccess;
-import org.apache.poi.sl.usermodel.SlideShow;
import org.apache.poi.sl.usermodel.SlideShowFactory;
import org.apache.poi.util.Internal;
* @throws IOException if an error occurs while reading the data
* @throws InvalidFormatException
*/
- public static SlideShow<?,?> createSlideShow(OPCPackage pkg) throws IOException {
+ public static XMLSlideShow createSlideShow(OPCPackage pkg) throws IOException {
try {
return new XMLSlideShow(pkg);
} catch (IllegalArgumentException ioe) {
* @throws EncryptedDocumentException If the wrong password is given for a protected file
*/
@SuppressWarnings("resource")
- public static SlideShow<?,?> createSlideShow(File file, boolean readOnly)
+ public static XMLSlideShow createSlideShow(File file, boolean readOnly)
throws IOException, InvalidFormatException {
OPCPackage pkg = OPCPackage.open(file, readOnly ? PackageAccess.READ : PackageAccess.READ_WRITE);
return createSlideShow(pkg);
* @throws InvalidFormatException
*/
@SuppressWarnings("resource")
- public static SlideShow<?,?> createSlideShow(InputStream stream) throws IOException, InvalidFormatException {
+ public static XMLSlideShow createSlideShow(InputStream stream) throws IOException, InvalidFormatException {
OPCPackage pkg = OPCPackage.open(stream);
return createSlideShow(pkg);
}
* @return font name
*/
public String getFontName(){
- final int maxLen = Math.min(_recdata.length,64);
- for(int i = 0; i+1 < maxLen; i+=2){
- //loop until find null-terminated end of the font name
- if(_recdata[i] == 0 && _recdata[i + 1] == 0 && !isFontNamePremature0terminated(i)) {
- return StringUtil.getFromUnicodeLE(_recdata, 0, i/2);
- }
- }
- return null;
- }
-
- /**
- * #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
- */
- private boolean isFontNamePremature0terminated(final int index) {
- if (index > 0) {
- // for now we only check the first char
- return false;
- }
-
- if (_recdata.length < index+4) {
- return false;
- }
-
- final int cp = LittleEndian.getShort(_recdata, index+2);
- if (!Character.isJavaIdentifierPart(cp)) {
- return false;
- }
-
- _recdata[index] = '?';
- return true;
+ final int maxLen = Math.min(_recdata.length,64)/2;
+ return StringUtil.getFromUnicodeLE0Terminated(_recdata, 0, maxLen);
}
/**
import org.apache.poi.ddf.EscherColorRef;
import org.apache.poi.ddf.EscherColorRef.SysIndexProcedure;
import org.apache.poi.ddf.EscherColorRef.SysIndexSource;
+import org.apache.poi.ddf.EscherComplexProperty;
import org.apache.poi.ddf.EscherContainerRecord;
import org.apache.poi.ddf.EscherProperties;
import org.apache.poi.ddf.EscherProperty;
import org.apache.poi.sl.usermodel.ShapeType;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
+import org.apache.poi.util.StringUtil;
import org.apache.poi.util.Units;
/**
/**
* @return name of the shape.
*/
+ @Override
public String getShapeName(){
- return getShapeType().nativeName;
+ final EscherComplexProperty ep = getEscherProperty(getEscherOptRecord(), EscherProperties.GROUPSHAPE__SHAPENAME);
+ if (ep != null) {
+ final byte[] cd = ep.getComplexData();
+ return StringUtil.getFromUnicodeLE0Terminated(cd, 0, cd.length/2);
+ } else {
+ return getShapeType().nativeName+" "+getShapeId();
+ }
}
public ShapeType getShapeType(){
}
}
}
+
+ @Test
+ public void shapeName() throws IOException {
+ final String file = "SampleShow.ppt"+(getClass().getSimpleName().contains("XML")?"x":"");
+ try (final InputStream is = slTests.openResourceAsStream(file)) {
+ try (final SlideShow<? extends Shape, ?> ppt = SlideShowFactory.create(is)) {
+ final List<? extends Shape> shapes1 = ppt.getSlides().get(0).getShapes();
+ assertEquals("The Title", shapes1.get(0).getShapeName());
+ assertEquals("Another Subtitle", shapes1.get(1).getShapeName());
+ final List<? extends Shape> shapes2 = ppt.getSlides().get(1).getShapes();
+ assertEquals("Title 1", shapes2.get(0).getShapeName());
+ assertEquals("Content Placeholder 2", shapes2.get(1).getShapeName());
+ }
+ }
+ }
}