]> source.dussan.org Git - poi.git/commitdiff
#61459 - HSLFShape.getShapeName() returns name of shapeType and not the shape name
authorAndreas Beeker <kiwiwings@apache.org>
Fri, 20 Apr 2018 13:45:18 +0000 (13:45 +0000)
committerAndreas Beeker <kiwiwings@apache.org>
Fri, 20 Apr 2018 13:45:18 +0000 (13:45 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1829656 13f79535-47bb-0310-9956-ffa450edef68

src/java/org/apache/poi/sl/usermodel/Shape.java
src/java/org/apache/poi/sl/usermodel/SlideShowFactory.java
src/java/org/apache/poi/util/StringUtil.java
src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java
src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlideShowFactory.java
src/scratchpad/src/org/apache/poi/hslf/record/FontEntityAtom.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShape.java
src/testcases/org/apache/poi/sl/usermodel/BaseTestSlideShow.java
test-data/slideshow/SampleShow.ppt
test-data/slideshow/SampleShow.pptx

index 8afc26fcfdc8bfead2e8446b9f96ce22595fcf21..a8ed1cfaaa2e6162f397b3e2752d9ccbbd1aca22 100644 (file)
@@ -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
     *
index 02e9fcb1466b1d60d9f19a51ade75b4220bc1214..5f75258b9adfd363789b14c7839245e6224a2794 100644 (file)
@@ -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<S,P>,
+        P extends TextParagraph<S,P,? extends TextRun>
+    > SlideShow<S,P> 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<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);
     }
 
@@ -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<S,P>,
+        P extends TextParagraph<S,P,? extends TextRun>
+    > SlideShow<S,P> 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<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;
@@ -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<S,P>,
+        P extends TextParagraph<S,P,? extends TextRun>
+    > SlideShow<S,P> 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<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);
         
@@ -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<S,P>,
+        P extends TextParagraph<S,P,? extends TextRun>
+    > SlideShow<S,P> 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<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);
     }
 
@@ -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<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());
         }
@@ -246,15 +273,24 @@ public class SlideShowFactory {
         }
     }
     
-    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];
@@ -269,7 +305,7 @@ public class SlideShowFactory {
                 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) {
index ee0fe27215db39f9cddec77c3582a146ba1c4805..302e532570aa1d2d4b75af6029b7048387cd4cc0 100644 (file)
@@ -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.
+     * <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));
+    }
+
 }
index 35a54e96ed19256d4e0e7c760a671f6243326c21..82449d846a299c5de16ea74be96986c489ae87f7 100644 (file)
@@ -97,9 +97,7 @@ public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> {
         return _sheet;
     }
     
-    /**
-     * @return human-readable name of this shape, e.g. "Rectange 3"
-     */
+    @Override
     public String getShapeName(){
         return getCNvPr().getName();
     }
index 71378e70ec025e3c11b409484de0d8364be38507..501132c00c5ac96ea3904b4596565bd98f8691ea 100644 (file)
@@ -25,7 +25,6 @@ import org.apache.poi.EncryptedDocumentException;
 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;
 
@@ -45,7 +44,7 @@ public class XSLFSlideShowFactory extends SlideShowFactory {
      *  @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) {
@@ -72,7 +71,7 @@ public class XSLFSlideShowFactory extends SlideShowFactory {
      *  @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);
@@ -92,7 +91,7 @@ public class XSLFSlideShowFactory extends SlideShowFactory {
      * @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);
     }
index 118fc1d3bfb9c19d072ed552708c8a7fb26682e7..4f3cdd89bc4800e3b3db411bd41373df11c4ea31 100644 (file)
@@ -86,38 +86,8 @@ public final class FontEntityAtom extends RecordAtom {
      * @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);
     }
 
     /**
index a61bbaedc60307339f2256b4d86be387f8ab3a8a..4edcb53603708fb3e5c350725253069b4eb96a34 100644 (file)
@@ -30,6 +30,7 @@ import org.apache.poi.ddf.EscherClientDataRecord;
 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;
@@ -49,6 +50,7 @@ import org.apache.poi.sl.usermodel.ShapeContainer;
 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;
 
 /**
@@ -123,8 +125,15 @@ public abstract class HSLFShape implements Shape<HSLFShape,HSLFTextParagraph> {
     /**
      * @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(){
index 47b886e174494f613a22e99f181715b6d8242d39..347ea73a6c11f1f0d038d4356fa56ba65c938e54 100644 (file)
@@ -157,4 +157,19 @@ public abstract class BaseTestSlideShow {
             }
         }        
     }
+
+    @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());
+            }
+        }
+    }
 }
index 81b99e100f926c1cc3fde2e90aef8c5838aa791f..7af347a5df883e4096849323830f8d07eb3f86ad 100644 (file)
Binary files a/test-data/slideshow/SampleShow.ppt and b/test-data/slideshow/SampleShow.ppt differ
index df3a26debdc27989bddd8257a278727974a75424..7db4d06e9ebd4d9e15381ea9df0d873446231b47 100644 (file)
Binary files a/test-data/slideshow/SampleShow.pptx and b/test-data/slideshow/SampleShow.pptx differ