#61459 - HSLFShape.getShapeName() returns name of shapeType and not the shape name

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1829656 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Andreas Beeker 2018-04-20 13:45:18 +00:00
parent e816131759
commit 7ddef62cc4
10 changed files with 161 additions and 55 deletions

View File

@ -39,6 +39,13 @@ public interface 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
*

View 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) {

View 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));
}
}

View 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();
}

View 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);
}

View 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);
}
/**

View 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(){

View 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());
}
}
}
}

Binary file not shown.

Binary file not shown.