convert tabs to spaces

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1890122 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
PJ Fanning 2021-05-22 21:37:08 +00:00
parent db53f9b860
commit 33f6381c43
212 changed files with 12904 additions and 12904 deletions

View File

@ -42,113 +42,113 @@ import org.apache.poi.util.LocaleUtil;
* http://www.gnome.ru/projects/docs/slide2.png
*/
public final class HDGFDiagram extends POIReadOnlyDocument {
private static final String VISIO_HEADER = "Visio (TM) Drawing\r\n";
private static final String VISIO_HEADER = "Visio (TM) Drawing\r\n";
private long docSize;
private long docSize;
private Pointer trailerPointer;
private TrailerStream trailer;
private Pointer trailerPointer;
private TrailerStream trailer;
public HDGFDiagram(POIFSFileSystem fs) throws IOException {
this(fs.getRoot());
}
public HDGFDiagram(POIFSFileSystem fs) throws IOException {
this(fs.getRoot());
}
public HDGFDiagram(DirectoryNode dir) throws IOException {
super(dir);
public HDGFDiagram(DirectoryNode dir) throws IOException {
super(dir);
// Grab the document stream
final byte[] _docstream;
try (InputStream is = dir.createDocumentInputStream("VisioDocument")) {
_docstream = IOUtils.toByteArray(is);
}
// Grab the document stream
final byte[] _docstream;
try (InputStream is = dir.createDocumentInputStream("VisioDocument")) {
_docstream = IOUtils.toByteArray(is);
}
// Check it's really visio
String typeString = new String(_docstream, 0, 20, LocaleUtil.CHARSET_1252 );
if(! typeString.equals(VISIO_HEADER)) {
throw new IllegalArgumentException("Wasn't a valid visio document, started with " + typeString);
}
// Check it's really visio
String typeString = new String(_docstream, 0, 20, LocaleUtil.CHARSET_1252 );
if(! typeString.equals(VISIO_HEADER)) {
throw new IllegalArgumentException("Wasn't a valid visio document, started with " + typeString);
}
// Grab the version number, 0x1a -> 0x1b
short version = LittleEndian.getShort(_docstream, 0x1a);
// Grab the document size, 0x1c -> 0x1f
docSize = LittleEndian.getUInt(_docstream, 0x1c);
// ??? 0x20 -> 0x23
// Grab the version number, 0x1a -> 0x1b
short version = LittleEndian.getShort(_docstream, 0x1a);
// Grab the document size, 0x1c -> 0x1f
docSize = LittleEndian.getUInt(_docstream, 0x1c);
// ??? 0x20 -> 0x23
// Create the Chunk+Pointer Factories for the document version
PointerFactory ptrFactory = new PointerFactory(version);
ChunkFactory chunkFactory = new ChunkFactory(version);
// Create the Chunk+Pointer Factories for the document version
PointerFactory ptrFactory = new PointerFactory(version);
ChunkFactory chunkFactory = new ChunkFactory(version);
// Grab the pointer to the trailer
trailerPointer = ptrFactory.createPointer(_docstream, 0x24);
// Grab the pointer to the trailer
trailerPointer = ptrFactory.createPointer(_docstream, 0x24);
// Now grab the trailer
trailer = (TrailerStream)
Stream.createStream(trailerPointer, _docstream, chunkFactory, ptrFactory);
// Now grab the trailer
trailer = (TrailerStream)
Stream.createStream(trailerPointer, _docstream, chunkFactory, ptrFactory);
// Finally, find all our streams
trailer.findChildren(_docstream);
}
// Finally, find all our streams
trailer.findChildren(_docstream);
}
/**
* Returns the TrailerStream, which is at the root of the
* tree of Streams.
*
* @return the TrailerStream
*/
public TrailerStream getTrailerStream() { return trailer; }
/**
* Returns the TrailerStream, which is at the root of the
* tree of Streams.
*
* @return the TrailerStream
*/
public TrailerStream getTrailerStream() { return trailer; }
/**
* Returns all the top level streams, which are the streams
* pointed to by the TrailerStream.
*
* @return the top level streams
*/
public Stream[] getTopLevelStreams() { return trailer.getPointedToStreams(); }
/**
* Returns all the top level streams, which are the streams
* pointed to by the TrailerStream.
*
* @return the top level streams
*/
public Stream[] getTopLevelStreams() { return trailer.getPointedToStreams(); }
public long getDocumentSize() { return docSize; }
public long getDocumentSize() { return docSize; }
/**
* Prints out some simple debug on the base contents of the file.
* @see org.apache.poi.hdgf.dev.VSDDumper
*/
public void debug() {
System.err.println("Trailer is at " + trailerPointer.getOffset());
System.err.println("Trailer has type " + trailerPointer.getType());
System.err.println("Trailer has length " + trailerPointer.getLength());
System.err.println("Trailer has format " + trailerPointer.getFormat());
/**
* Prints out some simple debug on the base contents of the file.
* @see org.apache.poi.hdgf.dev.VSDDumper
*/
public void debug() {
System.err.println("Trailer is at " + trailerPointer.getOffset());
System.err.println("Trailer has type " + trailerPointer.getType());
System.err.println("Trailer has length " + trailerPointer.getLength());
System.err.println("Trailer has format " + trailerPointer.getFormat());
for(int i=0; i<trailer.getPointedToStreams().length; i++) {
Stream stream = trailer.getPointedToStreams()[i];
Pointer ptr = stream.getPointer();
for(int i=0; i<trailer.getPointedToStreams().length; i++) {
Stream stream = trailer.getPointedToStreams()[i];
Pointer ptr = stream.getPointer();
System.err.println("Looking at pointer " + i);
System.err.println("\tType is " + ptr.getType() + "\t\t" + Integer.toHexString(ptr.getType()));
System.err.println("\tOffset is " + ptr.getOffset() + "\t\t" + Long.toHexString(ptr.getOffset()));
System.err.println("\tAddress is " + ptr.getAddress() + "\t" + Long.toHexString(ptr.getAddress()));
System.err.println("\tLength is " + ptr.getLength() + "\t\t" + Long.toHexString(ptr.getLength()));
System.err.println("\tFormat is " + ptr.getFormat() + "\t\t" + Long.toHexString(ptr.getFormat()));
System.err.println("\tCompressed is " + ptr.destinationCompressed());
System.err.println("\tStream is " + stream.getClass());
System.err.println("Looking at pointer " + i);
System.err.println("\tType is " + ptr.getType() + "\t\t" + Integer.toHexString(ptr.getType()));
System.err.println("\tOffset is " + ptr.getOffset() + "\t\t" + Long.toHexString(ptr.getOffset()));
System.err.println("\tAddress is " + ptr.getAddress() + "\t" + Long.toHexString(ptr.getAddress()));
System.err.println("\tLength is " + ptr.getLength() + "\t\t" + Long.toHexString(ptr.getLength()));
System.err.println("\tFormat is " + ptr.getFormat() + "\t\t" + Long.toHexString(ptr.getFormat()));
System.err.println("\tCompressed is " + ptr.destinationCompressed());
System.err.println("\tStream is " + stream.getClass());
if(stream instanceof PointerContainingStream) {
PointerContainingStream pcs = (PointerContainingStream)stream;
if(stream instanceof PointerContainingStream) {
PointerContainingStream pcs = (PointerContainingStream)stream;
if(pcs.getPointedToStreams() != null && pcs.getPointedToStreams().length > 0) {
System.err.println("\tContains " + pcs.getPointedToStreams().length + " other pointers/streams");
for(int j=0; j<pcs.getPointedToStreams().length; j++) {
Stream ss = pcs.getPointedToStreams()[j];
Pointer sptr = ss.getPointer();
System.err.println("\t\t" + j + " - Type is " + sptr.getType() + "\t\t" + Integer.toHexString(sptr.getType()));
System.err.println("\t\t" + j + " - Length is " + sptr.getLength() + "\t\t" + Long.toHexString(sptr.getLength()));
}
}
}
if(pcs.getPointedToStreams() != null && pcs.getPointedToStreams().length > 0) {
System.err.println("\tContains " + pcs.getPointedToStreams().length + " other pointers/streams");
for(int j=0; j<pcs.getPointedToStreams().length; j++) {
Stream ss = pcs.getPointedToStreams()[j];
Pointer sptr = ss.getPointer();
System.err.println("\t\t" + j + " - Type is " + sptr.getType() + "\t\t" + Integer.toHexString(sptr.getType()));
System.err.println("\t\t" + j + " - Length is " + sptr.getLength() + "\t\t" + Long.toHexString(sptr.getLength()));
}
}
}
if(stream instanceof StringsStream) {
System.err.println("\t\t**strings**");
StringsStream ss = (StringsStream)stream;
System.err.println("\t\t" + ss._getContentsLength());
}
}
}
if(stream instanceof StringsStream) {
System.err.println("\t\t**strings**");
StringsStream ss = (StringsStream)stream;
System.err.println("\t\t" + ss._getContentsLength());
}
}
}
}

View File

@ -286,11 +286,11 @@ public final class Chunk {
* process CommandDefinitions, and so doesn't actually exist
* in the chunk
*/
// public static class VirtualCommand extends Command {
// private VirtualCommand(CommandDefinition definition) {
// super(definition);
// }
// }
// public static class VirtualCommand extends Command {
// private VirtualCommand(CommandDefinition definition) {
// super(definition);
// }
// }
/**
* A special kind of command that holds the offset to
* a block

View File

@ -42,182 +42,182 @@ import static org.apache.logging.log4j.util.Unbox.box;
*/
public final class ChunkFactory {
//arbitrarily selected; may need to increase
private static final int MAX_RECORD_LENGTH = 1_000_000;
//arbitrarily selected; may need to increase
private static final int MAX_RECORD_LENGTH = 1_000_000;
/** The version of the currently open document */
private int version;
/**
* Key is a Chunk's type, value is an array of its CommandDefinitions
*/
private final Map<Integer, CommandDefinition[]> chunkCommandDefinitions =
/** The version of the currently open document */
private int version;
/**
* Key is a Chunk's type, value is an array of its CommandDefinitions
*/
private final Map<Integer, CommandDefinition[]> chunkCommandDefinitions =
new HashMap<>();
/**
* What the name is of the chunk table definitions file?
* This file comes from the scratchpad resources directory.
*/
private static final String chunkTableName =
"/org/apache/poi/hdgf/chunks_parse_cmds.tbl";
/**
* What the name is of the chunk table definitions file?
* This file comes from the scratchpad resources directory.
*/
private static final String chunkTableName =
"/org/apache/poi/hdgf/chunks_parse_cmds.tbl";
/** For logging problems we spot with the file */
private static final Logger LOG = LogManager.getLogger(ChunkFactory.class);
/** For logging problems we spot with the file */
private static final Logger LOG = LogManager.getLogger(ChunkFactory.class);
public ChunkFactory(int version) throws IOException {
this.version = version;
public ChunkFactory(int version) throws IOException {
this.version = version;
processChunkParseCommands();
}
processChunkParseCommands();
}
/**
* Open chunks_parse_cmds.tbl and process it, to get the definitions
* of all the different possible chunk commands.
*/
private void processChunkParseCommands() throws IOException {
try (InputStream cpd = ChunkFactory.class.getResourceAsStream(chunkTableName)) {
if(cpd == null) {
throw new IllegalStateException("Unable to find HDGF chunk definition on the classpath - " + chunkTableName);
}
/**
* Open chunks_parse_cmds.tbl and process it, to get the definitions
* of all the different possible chunk commands.
*/
private void processChunkParseCommands() throws IOException {
try (InputStream cpd = ChunkFactory.class.getResourceAsStream(chunkTableName)) {
if(cpd == null) {
throw new IllegalStateException("Unable to find HDGF chunk definition on the classpath - " + chunkTableName);
}
try (BufferedReader inp = new BufferedReader(new InputStreamReader(cpd, LocaleUtil.CHARSET_1252))) {
String line;
while ((line = inp.readLine()) != null) {
if (line.isEmpty() || "# \t".contains(line.substring(0, 1))) {
continue;
}
try (BufferedReader inp = new BufferedReader(new InputStreamReader(cpd, LocaleUtil.CHARSET_1252))) {
String line;
while ((line = inp.readLine()) != null) {
if (line.isEmpty() || "# \t".contains(line.substring(0, 1))) {
continue;
}
// Start xxx
if (!line.matches("^start [0-9]+$")) {
throw new IllegalStateException("Expecting start xxx, found " + line);
}
int chunkType = Integer.parseInt(line.substring(6));
ArrayList<CommandDefinition> defsL = new ArrayList<>();
// Start xxx
if (!line.matches("^start [0-9]+$")) {
throw new IllegalStateException("Expecting start xxx, found " + line);
}
int chunkType = Integer.parseInt(line.substring(6));
ArrayList<CommandDefinition> defsL = new ArrayList<>();
// Data entries
while ((line = inp.readLine()) != null) {
if (line.startsWith("end")) {
break;
}
StringTokenizer st = new StringTokenizer(line, " ");
int defType = Integer.parseInt(st.nextToken());
int offset = Integer.parseInt(st.nextToken());
String name = st.nextToken("\uffff").substring(1);
// Data entries
while ((line = inp.readLine()) != null) {
if (line.startsWith("end")) {
break;
}
StringTokenizer st = new StringTokenizer(line, " ");
int defType = Integer.parseInt(st.nextToken());
int offset = Integer.parseInt(st.nextToken());
String name = st.nextToken("\uffff").substring(1);
CommandDefinition def = new CommandDefinition(defType, offset, name);
defsL.add(def);
}
CommandDefinition def = new CommandDefinition(defType, offset, name);
defsL.add(def);
}
CommandDefinition[] defs = defsL.toArray(new CommandDefinition[0]);
CommandDefinition[] defs = defsL.toArray(new CommandDefinition[0]);
// Add to the map
chunkCommandDefinitions.put(chunkType, defs);
}
}
}
}
// Add to the map
chunkCommandDefinitions.put(chunkType, defs);
}
}
}
}
public int getVersion() { return version; }
public int getVersion() { return version; }
/**
* Creates the appropriate chunk at the given location.
*
* @param data the chunk bytes
* @param offset the offset into the chunk bytes array to start reading from
*
* @return the new Chunk
*/
public Chunk createChunk(byte[] data, int offset) {
// Create the header
ChunkHeader header =
ChunkHeader.createChunkHeader(version, data, offset);
// Sanity check
if(header.getLength() < 0) {
throw new IllegalArgumentException("Found a chunk with a negative length, which isn't allowed");
}
/**
* Creates the appropriate chunk at the given location.
*
* @param data the chunk bytes
* @param offset the offset into the chunk bytes array to start reading from
*
* @return the new Chunk
*/
public Chunk createChunk(byte[] data, int offset) {
// Create the header
ChunkHeader header =
ChunkHeader.createChunkHeader(version, data, offset);
// Sanity check
if(header.getLength() < 0) {
throw new IllegalArgumentException("Found a chunk with a negative length, which isn't allowed");
}
// How far up to look
int endOfDataPos = offset + header.getLength() + header.getSizeInBytes();
// How far up to look
int endOfDataPos = offset + header.getLength() + header.getSizeInBytes();
// Check we have enough data, and tweak the header size
// as required
if(endOfDataPos > data.length) {
LOG.atWarn().log("Header called for {} bytes, but that would take us past the end of the data!", box(header.getLength()));
// Check we have enough data, and tweak the header size
// as required
if(endOfDataPos > data.length) {
LOG.atWarn().log("Header called for {} bytes, but that would take us past the end of the data!", box(header.getLength()));
endOfDataPos = data.length;
header.setLength(data.length - offset - header.getSizeInBytes());
endOfDataPos = data.length;
header.setLength(data.length - offset - header.getSizeInBytes());
if(header.hasTrailer()) {
header.setLength(header.getLength() - 8);
endOfDataPos -= 8;
}
if(header.hasSeparator()) {
if(header.hasTrailer()) {
header.setLength(header.getLength() - 8);
endOfDataPos -= 8;
}
if(header.hasSeparator()) {
header.setLength(header.getLength() - 4);
endOfDataPos -= 4;
}
}
endOfDataPos -= 4;
}
}
// Create the trailer and separator, if required
ChunkTrailer trailer = null;
ChunkSeparator separator = null;
if(header.hasTrailer()) {
if(endOfDataPos <= data.length-8) {
trailer = new ChunkTrailer(
data, endOfDataPos);
endOfDataPos += 8;
} else {
LOG.atError().log("Header claims a length to {} there's then no space for the trailer in the data ({})", box(endOfDataPos),box(data.length));
}
}
if(header.hasSeparator()) {
if(endOfDataPos <= data.length-4) {
separator = new ChunkSeparator(
data, endOfDataPos);
} else {
LOG.atError().log("Header claims a length to {} there's then no space for the separator in the data ({})", box(endOfDataPos),box(data.length));
}
}
// Create the trailer and separator, if required
ChunkTrailer trailer = null;
ChunkSeparator separator = null;
if(header.hasTrailer()) {
if(endOfDataPos <= data.length-8) {
trailer = new ChunkTrailer(
data, endOfDataPos);
endOfDataPos += 8;
} else {
LOG.atError().log("Header claims a length to {} there's then no space for the trailer in the data ({})", box(endOfDataPos),box(data.length));
}
}
if(header.hasSeparator()) {
if(endOfDataPos <= data.length-4) {
separator = new ChunkSeparator(
data, endOfDataPos);
} else {
LOG.atError().log("Header claims a length to {} there's then no space for the separator in the data ({})", box(endOfDataPos),box(data.length));
}
}
// Now, create the chunk
byte[] contents = IOUtils.safelyClone(data, offset+header.getSizeInBytes(), header.getLength(), MAX_RECORD_LENGTH);
Chunk chunk = new Chunk(header, trailer, separator, contents);
// Now, create the chunk
byte[] contents = IOUtils.safelyClone(data, offset+header.getSizeInBytes(), header.getLength(), MAX_RECORD_LENGTH);
Chunk chunk = new Chunk(header, trailer, separator, contents);
// Feed in the stuff from chunks_parse_cmds.tbl
CommandDefinition[] defs = chunkCommandDefinitions.get(header.getType());
if (defs == null) {
defs = new CommandDefinition[0];
}
chunk.setCommandDefinitions(defs);
// Feed in the stuff from chunks_parse_cmds.tbl
CommandDefinition[] defs = chunkCommandDefinitions.get(header.getType());
if (defs == null) {
defs = new CommandDefinition[0];
}
chunk.setCommandDefinitions(defs);
// Now get the chunk to process its commands
chunk.processCommands();
// Now get the chunk to process its commands
chunk.processCommands();
// All done
return chunk;
}
// All done
return chunk;
}
/**
* The definition of a Command, which a chunk may hold.
* The Command holds the value, this describes it.
*/
public static class CommandDefinition {
private int type;
private int offset;
private String name;
public CommandDefinition(int type, int offset, String name) {
this.type = type;
this.offset = offset;
this.name = name;
}
/**
* The definition of a Command, which a chunk may hold.
* The Command holds the value, this describes it.
*/
public static class CommandDefinition {
private int type;
private int offset;
private String name;
public CommandDefinition(int type, int offset, String name) {
this.type = type;
this.offset = offset;
this.name = name;
}
public String getName() {
return name;
}
public int getOffset() {
return offset;
}
public int getType() {
return type;
}
}
public String getName() {
return name;
}
public int getOffset() {
return offset;
}
public int getType() {
return type;
}
}
}

View File

@ -25,98 +25,98 @@ import org.apache.poi.util.LittleEndian;
* A chunk header
*/
public abstract class ChunkHeader {
private int type;
private int id;
private int length;
private int unknown1;
private int type;
private int id;
private int length;
private int unknown1;
/**
* Creates the appropriate ChunkHeader for the Chunk Header at
* the given location, for the given document version.
*
* @param documentVersion the documentVersion - 4 and higher is supported
* @param data the chunk data
* @param offset the start offset in the chunk data
* @return the ChunkHeader
*/
public static ChunkHeader createChunkHeader(int documentVersion, byte[] data, int offset) {
if(documentVersion >= 6) {
ChunkHeaderV6 ch;
if(documentVersion > 6) {
ch = new ChunkHeaderV11();
} else {
ch = new ChunkHeaderV6();
}
ch.setType((int)LittleEndian.getUInt(data, offset + 0));
ch.setId((int)LittleEndian.getUInt(data, offset + 4));
ch.setUnknown1((int)LittleEndian.getUInt(data, offset + 8));
ch.setLength((int)LittleEndian.getUInt(data, offset + 12));
ch.setUnknown2(LittleEndian.getShort(data, offset + 16));
ch.setUnknown3(LittleEndian.getUByte(data, offset + 18));
/**
* Creates the appropriate ChunkHeader for the Chunk Header at
* the given location, for the given document version.
*
* @param documentVersion the documentVersion - 4 and higher is supported
* @param data the chunk data
* @param offset the start offset in the chunk data
* @return the ChunkHeader
*/
public static ChunkHeader createChunkHeader(int documentVersion, byte[] data, int offset) {
if(documentVersion >= 6) {
ChunkHeaderV6 ch;
if(documentVersion > 6) {
ch = new ChunkHeaderV11();
} else {
ch = new ChunkHeaderV6();
}
ch.setType((int)LittleEndian.getUInt(data, offset + 0));
ch.setId((int)LittleEndian.getUInt(data, offset + 4));
ch.setUnknown1((int)LittleEndian.getUInt(data, offset + 8));
ch.setLength((int)LittleEndian.getUInt(data, offset + 12));
ch.setUnknown2(LittleEndian.getShort(data, offset + 16));
ch.setUnknown3(LittleEndian.getUByte(data, offset + 18));
return ch;
} else if(documentVersion == 5 || documentVersion == 4) {
ChunkHeaderV4V5 ch = new ChunkHeaderV4V5();
return ch;
} else if(documentVersion == 5 || documentVersion == 4) {
ChunkHeaderV4V5 ch = new ChunkHeaderV4V5();
ch.setType(LittleEndian.getShort(data, offset + 0));
ch.setId(LittleEndian.getShort(data, offset + 2));
ch.setUnknown2(LittleEndian.getUByte(data, offset + 4));
ch.setUnknown3(LittleEndian.getUByte(data, offset + 5));
ch.setUnknown1(LittleEndian.getShort(data, offset + 6));
ch.setLength(Math.toIntExact(LittleEndian.getUInt(data, offset + 8)));
ch.setType(LittleEndian.getShort(data, offset + 0));
ch.setId(LittleEndian.getShort(data, offset + 2));
ch.setUnknown2(LittleEndian.getUByte(data, offset + 4));
ch.setUnknown3(LittleEndian.getUByte(data, offset + 5));
ch.setUnknown1(LittleEndian.getShort(data, offset + 6));
ch.setLength(Math.toIntExact(LittleEndian.getUInt(data, offset + 8)));
return ch;
} else {
throw new IllegalArgumentException("Visio files with versions below 4 are not supported, yours was " + documentVersion);
}
}
return ch;
} else {
throw new IllegalArgumentException("Visio files with versions below 4 are not supported, yours was " + documentVersion);
}
}
/**
* Returns the size of a chunk header for the given document version.
*
* @param documentVersion the documentVersion - 4 and higher is supported
*
* @return the header size
*/
public static int getHeaderSize(int documentVersion) {
return documentVersion >= 6 ? ChunkHeaderV6.getHeaderSize() : ChunkHeaderV4V5.getHeaderSize();
}
/**
* Returns the size of a chunk header for the given document version.
*
* @param documentVersion the documentVersion - 4 and higher is supported
*
* @return the header size
*/
public static int getHeaderSize(int documentVersion) {
return documentVersion >= 6 ? ChunkHeaderV6.getHeaderSize() : ChunkHeaderV4V5.getHeaderSize();
}
public abstract int getSizeInBytes();
public abstract boolean hasTrailer();
public abstract boolean hasSeparator();
public abstract Charset getChunkCharset();
public abstract int getSizeInBytes();
public abstract boolean hasTrailer();
public abstract boolean hasSeparator();
public abstract Charset getChunkCharset();
/**
* @return the ID/IX of the chunk
*/
public int getId() {
return id;
}
/**
* @return the ID/IX of the chunk
*/
public int getId() {
return id;
}
/**
* Returns the length of the trunk, excluding the length
* of the header, trailer or separator.
*
* @return the length of the trunk
*/
public int getLength() {
return length;
}
/**
* Returns the length of the trunk, excluding the length
* of the header, trailer or separator.
*
* @return the length of the trunk
*/
public int getLength() {
return length;
}
/**
* Returns the type of the chunk, which affects the
* mandatory information
*
* @return the type of the chunk
*/
public int getType() {
return type;
}
/**
* Returns the type of the chunk, which affects the
* mandatory information
*
* @return the type of the chunk
*/
public int getType() {
return type;
}
public int getUnknown1() {
return unknown1;
}
public int getUnknown1() {
return unknown1;
}
void setType(int type) {
this.type = type;

View File

@ -23,46 +23,46 @@ import java.nio.charset.Charset;
* A chunk header from v4 or v5
*/
public final class ChunkHeaderV4V5 extends ChunkHeader {
private short unknown2;
private short unknown3;
private short unknown2;
private short unknown3;
public short getUnknown2() {
return unknown2;
}
public short getUnknown3() {
return unknown3;
}
public short getUnknown2() {
return unknown2;
}
public short getUnknown3() {
return unknown3;
}
protected static int getHeaderSize() {
return 12;
}
protected static int getHeaderSize() {
return 12;
}
public int getSizeInBytes() {
return getHeaderSize();
}
public int getSizeInBytes() {
return getHeaderSize();
}
/**
* Does the chunk have a trailer?
*/
public boolean hasTrailer() {
// V4 and V5 never has trailers
return false;
}
/**
* Does the chunk have a trailer?
*/
public boolean hasTrailer() {
// V4 and V5 never has trailers
return false;
}
/**
* Does the chunk have a separator?
*/
public boolean hasSeparator() {
// V4 and V5 never has separators
return false;
}
/**
* Does the chunk have a separator?
*/
public boolean hasSeparator() {
// V4 and V5 never has separators
return false;
}
@Override
public Charset getChunkCharset() {
return Charset.forName("ASCII");
}
@Override
public Charset getChunkCharset() {
return Charset.forName("ASCII");
}
void setUnknown2(short unknown2) {
void setUnknown2(short unknown2) {
this.unknown2 = unknown2;
}

View File

@ -23,49 +23,49 @@ import java.nio.charset.Charset;
* A chunk header from v6
*/
public class ChunkHeaderV6 extends ChunkHeader {
private short unknown2;
private short unknown3;
private short unknown2;
private short unknown3;
public short getUnknown2() {
return unknown2;
}
public short getUnknown3() {
return unknown3;
}
public short getUnknown2() {
return unknown2;
}
public short getUnknown3() {
return unknown3;
}
protected static int getHeaderSize() {
// Looks like it ought to be 19...
return 19;
}
public int getSizeInBytes() {
return getHeaderSize();
}
protected static int getHeaderSize() {
// Looks like it ought to be 19...
return 19;
}
public int getSizeInBytes() {
return getHeaderSize();
}
/**
* Does the chunk have a trailer?
*/
public boolean hasTrailer() {
switch (getType()) {
case 0x2c: case 0x65: case 0x66: case 0x69:
case 0x6a: case 0x6b: case 0x70: case 0x71:
return true;
/**
* Does the chunk have a trailer?
*/
public boolean hasTrailer() {
switch (getType()) {
case 0x2c: case 0x65: case 0x66: case 0x69:
case 0x6a: case 0x6b: case 0x70: case 0x71:
return true;
default:
return (getUnknown1() != 0);
}
}
}
}
/**
* Does the chunk have a separator?
*/
public boolean hasSeparator() {
// V6 never has separators
return false;
}
/**
* Does the chunk have a separator?
*/
public boolean hasSeparator() {
// V6 never has separators
return false;
}
@Override
public Charset getChunkCharset() {
return Charset.forName("ASCII");
}
@Override
public Charset getChunkCharset() {
return Charset.forName("ASCII");
}
void setUnknown2(short unknown2) {
this.unknown2 = unknown2;

View File

@ -24,13 +24,13 @@ import java.util.Arrays;
* header of the next one
*/
public final class ChunkSeparator {
final byte[] separatorData;
final byte[] separatorData;
public ChunkSeparator(byte[] data, int offset) {
separatorData = Arrays.copyOfRange(data, offset, offset+4);
}
public ChunkSeparator(byte[] data, int offset) {
separatorData = Arrays.copyOfRange(data, offset, offset+4);
}
public String toString() {
return "<ChunkSeparator of length " + separatorData.length + ">";
}
public String toString() {
return "<ChunkSeparator of length " + separatorData.length + ">";
}
}

View File

@ -23,15 +23,15 @@ import java.util.Arrays;
* A trailer that follows a chunk
*/
public final class ChunkTrailer {
private final byte[] trailerData;
private final byte[] trailerData;
public ChunkTrailer(byte[] data, int offset) {
trailerData = Arrays.copyOfRange(data, offset, offset+8);
}
public ChunkTrailer(byte[] data, int offset) {
trailerData = Arrays.copyOfRange(data, offset, offset+8);
}
public String toString() {
return "<ChunkTrailer of length " + trailerData.length + ">";
}
public String toString() {
return "<ChunkTrailer of length " + trailerData.length + ">";
}
byte[] getTrailerData() {
return trailerData;

View File

@ -44,63 +44,63 @@ public final class VSDDumper {
this.hdgf = hdgf;
}
public static void main(String[] args) throws Exception {
if(args.length == 0) {
System.err.println("Use:");
System.err.println(" VSDDumper <filename>");
System.exit(1);
}
public static void main(String[] args) throws Exception {
if(args.length == 0) {
System.err.println("Use:");
System.err.println(" VSDDumper <filename>");
System.exit(1);
}
try (POIFSFileSystem poifs = new POIFSFileSystem(new File(args[0]));
HDGFDiagram hdgf = new HDGFDiagram(poifs)) {
try (POIFSFileSystem poifs = new POIFSFileSystem(new File(args[0]));
HDGFDiagram hdgf = new HDGFDiagram(poifs)) {
PrintStream ps = System.out;
ps.println("Opened " + args[0]);
VSDDumper vd = new VSDDumper(ps, hdgf);
vd.dumpFile();
}
}
}
public void dumpFile() {
public void dumpFile() {
dumpVal("Claimed document size", hdgf.getDocumentSize(), 0);
ps.println();
dumpStream(hdgf.getTrailerStream(), 0);
}
}
private void dumpStream(Stream stream, int indent) {
Pointer ptr = stream.getPointer();
dumpVal("Stream at", ptr.getOffset(), indent);
dumpVal("Type is", ptr.getType(), indent+1);
dumpVal("Format is", ptr.getFormat(), indent+1);
private void dumpStream(Stream stream, int indent) {
Pointer ptr = stream.getPointer();
dumpVal("Stream at", ptr.getOffset(), indent);
dumpVal("Type is", ptr.getType(), indent+1);
dumpVal("Format is", ptr.getFormat(), indent+1);
dumpVal("Length is", ptr.getLength(), indent+1);
if(ptr.destinationCompressed()) {
dumpVal("DC.Length is", stream._getContentsLength(), indent+1);
}
dumpVal("Compressed is", ptr.destinationCompressed(), indent+1);
dumpVal("Stream is", stream.getClass().getName(), indent+1);
if(ptr.destinationCompressed()) {
dumpVal("DC.Length is", stream._getContentsLength(), indent+1);
}
dumpVal("Compressed is", ptr.destinationCompressed(), indent+1);
dumpVal("Stream is", stream.getClass().getName(), indent+1);
byte[] db = stream._getStore()._getContents();
String ds = (db.length >= 8) ? Arrays.toString(db) : "[]";
dumpVal("First few bytes are", ds, indent+1);
byte[] db = stream._getStore()._getContents();
String ds = (db.length >= 8) ? Arrays.toString(db) : "[]";
dumpVal("First few bytes are", ds, indent+1);
if (stream instanceof PointerContainingStream) {
if (stream instanceof PointerContainingStream) {
Stream[] streams = ((PointerContainingStream) stream).getPointedToStreams();
dumpVal("Nbr of children", streams.length, indent+1);
dumpVal("Nbr of children", streams.length, indent+1);
for(Stream s : streams) {
dumpStream(s, indent+1);
}
}
if(stream instanceof ChunkStream) {
for(Stream s : streams) {
dumpStream(s, indent+1);
}
}
if(stream instanceof ChunkStream) {
Chunk[] chunks = ((ChunkStream) stream).getChunks();
dumpVal("Nbr of chunks", chunks.length, indent+1);
dumpVal("Nbr of chunks", chunks.length, indent+1);
for(Chunk chunk : chunks) {
dumpChunk(chunk, indent+1);
}
}
}
for(Chunk chunk : chunks) {
dumpChunk(chunk, indent+1);
}
}
}
private void dumpChunk(Chunk chunk, int indent) {
private void dumpChunk(Chunk chunk, int indent) {
dumpVal(chunk.getName(), "", indent);
dumpVal("Length is", chunk._getContents().length, indent);
dumpVal("OD Size is", chunk.getOnDiskSize(), indent);
@ -110,9 +110,9 @@ public final class VSDDumper {
for(Command command : commands) {
dumpVal(command.getDefinition().getName(), ""+command.getValue(), indent+1);
}
}
}
private void dumpVal(String label, long value, int indent) {
private void dumpVal(String label, long value, int indent) {
ps.print(tabs.substring(0,indent));
ps.print(label);
ps.print('\t');

View File

@ -23,11 +23,11 @@ package org.apache.poi.hdgf.exceptions;
public final class HDGFException extends RuntimeException {
public HDGFException() {
super();
super();
}
public HDGFException(String message) {
super(message);
super(message);
}
public HDGFException(String message, Throwable cause) {

View File

@ -38,103 +38,103 @@ import org.apache.poi.poifs.filesystem.POIFSFileSystem;
* can return the text for you (example: for use with Lucene).
*/
public final class VisioTextExtractor implements POIOLE2TextExtractor {
private HDGFDiagram hdgf;
private boolean doCloseFilesystem = true;
private HDGFDiagram hdgf;
private boolean doCloseFilesystem = true;
public VisioTextExtractor(HDGFDiagram hdgf) {
this.hdgf = hdgf;
}
public VisioTextExtractor(POIFSFileSystem fs) throws IOException {
this(fs.getRoot());
}
public VisioTextExtractor(HDGFDiagram hdgf) {
this.hdgf = hdgf;
}
public VisioTextExtractor(POIFSFileSystem fs) throws IOException {
this(fs.getRoot());
}
public VisioTextExtractor(DirectoryNode dir) throws IOException {
this(new HDGFDiagram(dir));
}
public VisioTextExtractor(DirectoryNode dir) throws IOException {
this(new HDGFDiagram(dir));
}
public VisioTextExtractor(InputStream inp) throws IOException {
this(new POIFSFileSystem(inp));
}
public VisioTextExtractor(InputStream inp) throws IOException {
this(new POIFSFileSystem(inp));
}
/**
* Locates all the text entries in the file, and returns their
* contents.
*
* @return An array of each Text item in the document
*/
public String[] getAllText() {
List<String> text = new ArrayList<>();
for(Stream stream : hdgf.getTopLevelStreams()) {
findText(stream, text);
}
return text.toArray(new String[0]);
}
private void findText(Stream stream, List<String> text) {
if(stream instanceof PointerContainingStream) {
PointerContainingStream ps = (PointerContainingStream)stream;
for(final Stream substream : ps.getPointedToStreams()) {
findText(substream, text);
}
}
if(stream instanceof ChunkStream) {
ChunkStream cs = (ChunkStream)stream;
for(final Chunk chunk : cs.getChunks()) {
if(chunk != null &&
chunk.getName() != null &&
"Text".equals(chunk.getName()) &&
chunk.getCommands().length > 0) {
/**
* Locates all the text entries in the file, and returns their
* contents.
*
* @return An array of each Text item in the document
*/
public String[] getAllText() {
List<String> text = new ArrayList<>();
for(Stream stream : hdgf.getTopLevelStreams()) {
findText(stream, text);
}
return text.toArray(new String[0]);
}
private void findText(Stream stream, List<String> text) {
if(stream instanceof PointerContainingStream) {
PointerContainingStream ps = (PointerContainingStream)stream;
for(final Stream substream : ps.getPointedToStreams()) {
findText(substream, text);
}
}
if(stream instanceof ChunkStream) {
ChunkStream cs = (ChunkStream)stream;
for(final Chunk chunk : cs.getChunks()) {
if(chunk != null &&
chunk.getName() != null &&
"Text".equals(chunk.getName()) &&
chunk.getCommands().length > 0) {
// First command
Command cmd = chunk.getCommands()[0];
if(cmd != null && cmd.getValue() != null) {
// Capture the text, as long as it isn't
// simply an empty string
String str = cmd.getValue().toString();
if (!(str.isEmpty() || "\n".equals(str))) {
text.add( str );
}
}
}
}
}
}
// First command
Command cmd = chunk.getCommands()[0];
if(cmd != null && cmd.getValue() != null) {
// Capture the text, as long as it isn't
// simply an empty string
String str = cmd.getValue().toString();
if (!(str.isEmpty() || "\n".equals(str))) {
text.add( str );
}
}
}
}
}
}
/**
* Returns the textual contents of the file.
* Each textual object's text will be separated
* by a newline
*
* @return All text contained in this document, separated by <code>\n</code>
*/
@Override
public String getText() {
StringBuilder text = new StringBuilder();
for(String t : getAllText()) {
text.append(t);
if(!t.endsWith("\r") && !t.endsWith("\n")) {
text.append('\n');
}
}
return text.toString();
}
/**
* Returns the textual contents of the file.
* Each textual object's text will be separated
* by a newline
*
* @return All text contained in this document, separated by <code>\n</code>
*/
@Override
public String getText() {
StringBuilder text = new StringBuilder();
for(String t : getAllText()) {
text.append(t);
if(!t.endsWith("\r") && !t.endsWith("\n")) {
text.append('\n');
}
}
return text.toString();
}
@Override
public HDGFDiagram getDocument() {
return hdgf;
}
@Override
public HDGFDiagram getDocument() {
return hdgf;
}
@Override
public void setCloseFilesystem(boolean doCloseFilesystem) {
this.doCloseFilesystem = doCloseFilesystem;
}
@Override
public void setCloseFilesystem(boolean doCloseFilesystem) {
this.doCloseFilesystem = doCloseFilesystem;
}
@Override
public boolean isCloseFilesystem() {
return doCloseFilesystem;
}
@Override
public boolean isCloseFilesystem() {
return doCloseFilesystem;
}
@Override
public HDGFDiagram getFilesystem() {
return hdgf;
}
@Override
public HDGFDiagram getFilesystem() {
return hdgf;
}
}

View File

@ -29,55 +29,55 @@ import org.apache.poi.hdgf.pointers.Pointer;
import static org.apache.logging.log4j.util.Unbox.box;
public final class ChunkStream extends Stream {
private static final Logger LOG = LogManager.getLogger(ChunkStream.class);
private static final Logger LOG = LogManager.getLogger(ChunkStream.class);
private final ChunkFactory chunkFactory;
/** All the Chunks we contain */
private Chunk[] chunks;
private final ChunkFactory chunkFactory;
/** All the Chunks we contain */
private Chunk[] chunks;
ChunkStream(Pointer pointer, StreamStore store, ChunkFactory chunkFactory) {
super(pointer, store);
this.chunkFactory = chunkFactory;
ChunkStream(Pointer pointer, StreamStore store, ChunkFactory chunkFactory) {
super(pointer, store);
this.chunkFactory = chunkFactory;
// For compressed stores, we require all of the data
store.copyBlockHeaderToContents();
}
// For compressed stores, we require all of the data
store.copyBlockHeaderToContents();
}
public Chunk[] getChunks() { return chunks; }
public Chunk[] getChunks() { return chunks; }
/**
* Process the contents of the stream out into chunks
*/
public void findChunks() {
ArrayList<Chunk> chunksA = new ArrayList<>();
/**
* Process the contents of the stream out into chunks
*/
public void findChunks() {
ArrayList<Chunk> chunksA = new ArrayList<>();
if(getPointer().getOffset() == 0x64b3) {
int i = 0;
i++;
}
if(getPointer().getOffset() == 0x64b3) {
int i = 0;
i++;
}
int pos = 0;
byte[] contents = getStore().getContents();
try {
while(pos < contents.length) {
// Ensure we have enough data to create a chunk from
int headerSize = ChunkHeader.getHeaderSize(chunkFactory.getVersion());
if(pos+headerSize <= contents.length) {
Chunk chunk = chunkFactory.createChunk(contents, pos);
chunksA.add(chunk);
int pos = 0;
byte[] contents = getStore().getContents();
try {
while(pos < contents.length) {
// Ensure we have enough data to create a chunk from
int headerSize = ChunkHeader.getHeaderSize(chunkFactory.getVersion());
if(pos+headerSize <= contents.length) {
Chunk chunk = chunkFactory.createChunk(contents, pos);
chunksA.add(chunk);
pos += chunk.getOnDiskSize();
} else {
LOG.atWarn().log("Needed {} bytes to create the next chunk header, but only found {} bytes, ignoring rest of data", box(headerSize),box(contents.length - pos));
pos = contents.length;
}
}
}
catch (Exception e)
{
LOG.atError().withThrowable(e).log("Failed to create chunk at {}, ignoring rest of data.", box(pos));
}
pos += chunk.getOnDiskSize();
} else {
LOG.atWarn().log("Needed {} bytes to create the next chunk header, but only found {} bytes, ignoring rest of data", box(headerSize),box(contents.length - pos));
pos = contents.length;
}
}
}
catch (Exception e)
{
LOG.atError().withThrowable(e).log("Failed to create chunk at {}, ignoring rest of data.", box(pos));
}
chunks = chunksA.toArray(new Chunk[0]);
}
chunks = chunksA.toArray(new Chunk[0]);
}
}

View File

@ -29,69 +29,69 @@ import org.apache.poi.util.IOUtils;
*/
public final class CompressedStreamStore extends StreamStore {
//arbitrarily selected; may need to increase
private static final int MAX_RECORD_LENGTH = 64_000_000;
//arbitrarily selected; may need to increase
private static final int MAX_RECORD_LENGTH = 64_000_000;
/** The raw, compressed contents */
private byte[] compressedContents;
/**
* We're not sure what this is, but it comes before the
* real contents in the de-compressed data
*/
private final byte[] blockHeader;
private boolean blockHeaderInContents;
/** The raw, compressed contents */
private byte[] compressedContents;
/**
* We're not sure what this is, but it comes before the
* real contents in the de-compressed data
*/
private final byte[] blockHeader;
private boolean blockHeaderInContents;
byte[] _getCompressedContents() { return compressedContents; }
byte[] _getBlockHeader() { return blockHeader; }
byte[] _getCompressedContents() { return compressedContents; }
byte[] _getBlockHeader() { return blockHeader; }
/**
* Creates a new compressed StreamStore, which will handle
* the decompression.
*/
CompressedStreamStore(byte[] data, int offset, int length) throws IOException {
this(decompress(data,offset,length));
compressedContents = IOUtils.safelyClone(data, offset, length, MAX_RECORD_LENGTH);
}
/**
* Handles passing the de-compressed data onto our superclass.
*/
private CompressedStreamStore(byte[][] decompressedData) {
super(decompressedData[1], 0, decompressedData[1].length);
blockHeader = decompressedData[0];
}
/**
* Creates a new compressed StreamStore, which will handle
* the decompression.
*/
CompressedStreamStore(byte[] data, int offset, int length) throws IOException {
this(decompress(data,offset,length));
compressedContents = IOUtils.safelyClone(data, offset, length, MAX_RECORD_LENGTH);
}
/**
* Handles passing the de-compressed data onto our superclass.
*/
private CompressedStreamStore(byte[][] decompressedData) {
super(decompressedData[1], 0, decompressedData[1].length);
blockHeader = decompressedData[0];
}
/**
* Some kinds of streams expect their 4 byte header to be
* on the front of the contents.
* They can call this to have it sorted.
*/
protected void copyBlockHeaderToContents() {
if(blockHeaderInContents) return;
/**
* Some kinds of streams expect their 4 byte header to be
* on the front of the contents.
* They can call this to have it sorted.
*/
protected void copyBlockHeaderToContents() {
if(blockHeaderInContents) return;
prependContentsWith(blockHeader);
blockHeaderInContents = true;
}
prependContentsWith(blockHeader);
blockHeaderInContents = true;
}
/**
* Decompresses the given data, returning it as header + contents
*/
public static byte[][] decompress(byte[] data, int offset, int length) throws IOException {
ByteArrayInputStream bais = new ByteArrayInputStream(data, offset, length);
/**
* Decompresses the given data, returning it as header + contents
*/
public static byte[][] decompress(byte[] data, int offset, int length) throws IOException {
ByteArrayInputStream bais = new ByteArrayInputStream(data, offset, length);
// Decompress
HDGFLZW lzw = new HDGFLZW();
byte[] decompressed = lzw.decompress(bais);
// Decompress
HDGFLZW lzw = new HDGFLZW();
byte[] decompressed = lzw.decompress(bais);
// Split into header and contents
byte[][] ret = new byte[2][];
ret[0] = new byte[4];
ret[1] = new byte[decompressed.length - 4];
// Split into header and contents
byte[][] ret = new byte[2][];
ret[0] = new byte[4];
ret[1] = new byte[decompressed.length - 4];
System.arraycopy(decompressed, 0, ret[0], 0, 4);
System.arraycopy(decompressed, 4, ret[1], 0, ret[1].length);
System.arraycopy(decompressed, 0, ret[0], 0, 4);
System.arraycopy(decompressed, 4, ret[1], 0, ret[1].length);
// All done
return ret;
}
// All done
return ret;
}
}

View File

@ -26,56 +26,56 @@ import org.apache.poi.hdgf.pointers.PointerFactory;
* other data too.
*/
public class PointerContainingStream extends Stream { // TODO - instantiable superclass
private Pointer[] childPointers;
private Stream[] childStreams;
private Pointer[] childPointers;
private Stream[] childStreams;
private ChunkFactory chunkFactory;
private PointerFactory pointerFactory;
private ChunkFactory chunkFactory;
private PointerFactory pointerFactory;
protected PointerContainingStream(Pointer pointer, StreamStore store, ChunkFactory chunkFactory, PointerFactory pointerFactory) {
super(pointer, store);
this.chunkFactory = chunkFactory;
this.pointerFactory = pointerFactory;
// Have the child pointers identified and created
childPointers = pointerFactory.createContainerPointers(pointer, store.getContents());
}
protected PointerContainingStream(Pointer pointer, StreamStore store, ChunkFactory chunkFactory, PointerFactory pointerFactory) {
super(pointer, store);
this.chunkFactory = chunkFactory;
this.pointerFactory = pointerFactory;
// Have the child pointers identified and created
childPointers = pointerFactory.createContainerPointers(pointer, store.getContents());
}
/**
* Returns all the pointers that we contain
*/
protected Pointer[] getChildPointers() { return childPointers; }
/**
* Returns all the "child" streams.
* These are all the streams pointed to by the pointers
* that we contain.
*/
public Stream[] getPointedToStreams() { return childStreams; }
/**
* Returns all the pointers that we contain
*/
protected Pointer[] getChildPointers() { return childPointers; }
/**
* Returns all the "child" streams.
* These are all the streams pointed to by the pointers
* that we contain.
*/
public Stream[] getPointedToStreams() { return childStreams; }
/**
* Performs a recursive search, identifying the pointers we contain,
* creating the Streams for where they point to, then searching
* those if appropriate.
*/
public void findChildren(byte[] documentData) {
// For each pointer, generate the Stream it points to
childStreams = new Stream[childPointers.length];
for(int i=0; i<childPointers.length; i++) {
Pointer ptr = childPointers[i];
childStreams[i] = Stream.createStream(ptr, documentData, chunkFactory, pointerFactory);
/**
* Performs a recursive search, identifying the pointers we contain,
* creating the Streams for where they point to, then searching
* those if appropriate.
*/
public void findChildren(byte[] documentData) {
// For each pointer, generate the Stream it points to
childStreams = new Stream[childPointers.length];
for(int i=0; i<childPointers.length; i++) {
Pointer ptr = childPointers[i];
childStreams[i] = Stream.createStream(ptr, documentData, chunkFactory, pointerFactory);
// Process chunk streams into their chunks
if(childStreams[i] instanceof ChunkStream) {
ChunkStream child = (ChunkStream)childStreams[i];
child.findChunks();
}
// Process chunk streams into their chunks
if(childStreams[i] instanceof ChunkStream) {
ChunkStream child = (ChunkStream)childStreams[i];
child.findChunks();
}
// Recurse into pointer containing streams
if(childStreams[i] instanceof PointerContainingStream) {
PointerContainingStream child =
(PointerContainingStream)childStreams[i];
child.findChildren(documentData);
}
}
}
// Recurse into pointer containing streams
if(childStreams[i] instanceof PointerContainingStream) {
PointerContainingStream child =
(PointerContainingStream)childStreams[i];
child.findChildren(documentData);
}
}
}
}

View File

@ -32,62 +32,62 @@ import org.apache.poi.hdgf.exceptions.HDGFException;
* disk, but that doesn't appear to change their use.
*/
public abstract class Stream {
private Pointer pointer;
private StreamStore store;
private Pointer pointer;
private StreamStore store;
public Pointer getPointer() { return pointer; }
protected StreamStore getStore() { return store; }
public StreamStore _getStore() { return store; }
public int _getContentsLength() { return store.getContents().length; }
public Pointer getPointer() { return pointer; }
protected StreamStore getStore() { return store; }
public StreamStore _getStore() { return store; }
public int _getContentsLength() { return store.getContents().length; }
/**
* Creates a new Stream, having already used the pointer
* to build a store
*/
protected Stream(Pointer pointer, StreamStore store) {
this.pointer = pointer;
this.store = store;
}
/**
* Creates a new Stream, having already used the pointer
* to build a store
*/
protected Stream(Pointer pointer, StreamStore store) {
this.pointer = pointer;
this.store = store;
}
/**
* Uses the pointer to locate a Stream within the document
* data, and creates it.
* @param pointer The Pointer to create a stream for
* @param documentData The raw document data
*/
public static Stream createStream(Pointer pointer, byte[] documentData, ChunkFactory chunkFactory, PointerFactory pointerFactory) {
// Create the store
StreamStore store;
if(pointer.destinationCompressed()) {
try {
store = new CompressedStreamStore(
documentData, pointer.getOffset(), pointer.getLength()
);
} catch(IOException e) {
// Should never occur
throw new HDGFException(e);
}
} else {
store = new StreamStore(
documentData, pointer.getOffset(), pointer.getLength()
);
}
/**
* Uses the pointer to locate a Stream within the document
* data, and creates it.
* @param pointer The Pointer to create a stream for
* @param documentData The raw document data
*/
public static Stream createStream(Pointer pointer, byte[] documentData, ChunkFactory chunkFactory, PointerFactory pointerFactory) {
// Create the store
StreamStore store;
if(pointer.destinationCompressed()) {
try {
store = new CompressedStreamStore(
documentData, pointer.getOffset(), pointer.getLength()
);
} catch(IOException e) {
// Should never occur
throw new HDGFException(e);
}
} else {
store = new StreamStore(
documentData, pointer.getOffset(), pointer.getLength()
);
}
// Figure out what sort of Stream to create, create and return it
if(pointer.getType() == 20) {
return new TrailerStream(pointer, store, chunkFactory, pointerFactory);
}
else if(pointer.destinationHasPointers()) {
return new PointerContainingStream(pointer, store, chunkFactory, pointerFactory);
}
else if(pointer.destinationHasChunks()) {
return new ChunkStream(pointer, store, chunkFactory);
}
else if(pointer.destinationHasStrings()) {
return new StringsStream(pointer, store, chunkFactory);
}
// Figure out what sort of Stream to create, create and return it
if(pointer.getType() == 20) {
return new TrailerStream(pointer, store, chunkFactory, pointerFactory);
}
else if(pointer.destinationHasPointers()) {
return new PointerContainingStream(pointer, store, chunkFactory, pointerFactory);
}
else if(pointer.destinationHasChunks()) {
return new ChunkStream(pointer, store, chunkFactory);
}
else if(pointer.destinationHasStrings()) {
return new StringsStream(pointer, store, chunkFactory);
}
// Give up and return a generic one
return new UnknownStream(pointer, store);
}
// Give up and return a generic one
return new UnknownStream(pointer, store);
}
}

View File

@ -25,26 +25,26 @@ import org.apache.poi.util.IOUtils;
* In future, may also handle writing it back out again
*/
public class StreamStore { // TODO - instantiable superclass
//arbitrarily selected; may need to increase
private static final int MAX_RECORD_LENGTH = 10_000_000;
//arbitrarily selected; may need to increase
private static final int MAX_RECORD_LENGTH = 10_000_000;
private byte[] contents;
private byte[] contents;
/**
* Creates a new, non compressed Stream Store
*/
protected StreamStore(byte[] data, int offset, int length) {
contents = IOUtils.safelyClone(data, offset, length, MAX_RECORD_LENGTH);
}
/**
* Creates a new, non compressed Stream Store
*/
protected StreamStore(byte[] data, int offset, int length) {
contents = IOUtils.safelyClone(data, offset, length, MAX_RECORD_LENGTH);
}
protected void prependContentsWith(byte[] b) {
byte[] newContents = IOUtils.safelyAllocate(contents.length + (long)b.length, MAX_RECORD_LENGTH);
System.arraycopy(b, 0, newContents, 0, b.length);
System.arraycopy(contents, 0, newContents, b.length, contents.length);
contents = newContents;
}
protected void copyBlockHeaderToContents() {}
protected void prependContentsWith(byte[] b) {
byte[] newContents = IOUtils.safelyAllocate(contents.length + (long)b.length, MAX_RECORD_LENGTH);
System.arraycopy(b, 0, newContents, 0, b.length);
System.arraycopy(contents, 0, newContents, b.length, contents.length);
contents = newContents;
}
protected void copyBlockHeaderToContents() {}
protected byte[] getContents() { return contents; }
public byte[] _getContents() { return contents; }
protected byte[] getContents() { return contents; }
public byte[] _getContents() { return contents; }
}

View File

@ -25,8 +25,8 @@ import org.apache.poi.hdgf.pointers.Pointer;
* of ChunkStream, it seems
*/
public final class StringsStream extends Stream {
protected StringsStream(Pointer pointer, StreamStore store, ChunkFactory chunkFactory) {
super(pointer, store);
// super(pointer, store, chunkFactory);
}
protected StringsStream(Pointer pointer, StreamStore store, ChunkFactory chunkFactory) {
super(pointer, store);
// super(pointer, store, chunkFactory);
}
}

View File

@ -28,7 +28,7 @@ import org.apache.poi.hdgf.pointers.PointerFactory;
* a special series of byte near the start of the file.
*/
public class TrailerStream extends PointerContainingStream { // TODO - instantiable superclass
protected TrailerStream(Pointer pointer, StreamStore store, ChunkFactory chunkFactory, PointerFactory pointerFactory) {
super(pointer, store, chunkFactory, pointerFactory);
}
protected TrailerStream(Pointer pointer, StreamStore store, ChunkFactory chunkFactory, PointerFactory pointerFactory) {
super(pointer, store, chunkFactory, pointerFactory);
}
}

View File

@ -24,7 +24,7 @@ import org.apache.poi.hdgf.pointers.Pointer;
* about how to process / handle it
*/
public final class UnknownStream extends Stream {
protected UnknownStream(Pointer pointer, StreamStore store) {
super(pointer, store);
}
protected UnknownStream(Pointer pointer, StreamStore store) {
super(pointer, store);
}
}

View File

@ -24,9 +24,9 @@ import org.apache.poi.poifs.filesystem.DirectoryNode;
*
*/
public final class EscherDelayStm extends EscherPart {
private static final String[] PATH = { "Escher", "EscherDelayStm", };
private static final String[] PATH = { "Escher", "EscherDelayStm", };
public EscherDelayStm(DirectoryNode baseDir) throws IOException {
super(baseDir, PATH);
}
public EscherDelayStm(DirectoryNode baseDir) throws IOException {
super(baseDir, PATH);
}
}

View File

@ -30,58 +30,58 @@ import org.apache.poi.util.IOUtils;
*/
public abstract class EscherPart extends HPBFPart {
//arbitrarily selected; may need to increase
private static final int MAX_RECORD_LENGTH = 1_000_000;
//arbitrarily selected; may need to increase
private static final int MAX_RECORD_LENGTH = 1_000_000;
private EscherRecord[] records;
private EscherRecord[] records;
/**
* Creates the Escher Part, and finds our child
* escher records
*/
public EscherPart(DirectoryNode baseDir, String[] parts) throws IOException {
super(baseDir, parts);
/**
* Creates the Escher Part, and finds our child
* escher records
*/
public EscherPart(DirectoryNode baseDir, String[] parts) throws IOException {
super(baseDir, parts);
// Now create our Escher children
DefaultEscherRecordFactory erf =
new DefaultEscherRecordFactory();
// Now create our Escher children
DefaultEscherRecordFactory erf =
new DefaultEscherRecordFactory();
ArrayList<EscherRecord> ec = new ArrayList<>();
ArrayList<EscherRecord> ec = new ArrayList<>();
byte[] data = getData();
int left = data.length;
while(left > 0) {
EscherRecord er = erf.createRecord(data, 0);
er.fillFields(data, 0, erf);
left -= er.getRecordSize();
int left = data.length;
while(left > 0) {
EscherRecord er = erf.createRecord(data, 0);
er.fillFields(data, 0, erf);
left -= er.getRecordSize();
ec.add(er);
}
ec.add(er);
}
records = ec.toArray(new EscherRecord[0]);
}
records = ec.toArray(new EscherRecord[0]);
}
public EscherRecord[] getEscherRecords() {
return records;
}
public EscherRecord[] getEscherRecords() {
return records;
}
/**
* Serialises our Escher children back
* into bytes.
*/
protected void generateData() {
int size = 0;
for(int i=0; i<records.length; i++) {
size += records[i].getRecordSize();
}
/**
* Serialises our Escher children back
* into bytes.
*/
protected void generateData() {
int size = 0;
for(int i=0; i<records.length; i++) {
size += records[i].getRecordSize();
}
byte[] data = IOUtils.safelyAllocate(size, MAX_RECORD_LENGTH);
size = 0;
for(int i=0; i<records.length; i++) {
int thisSize =
records[i].serialize(size, data);
size += thisSize;
}
setData(data);
}
size = 0;
for(int i=0; i<records.length; i++) {
int thisSize =
records[i].serialize(size, data);
size += thisSize;
}
setData(data);
}
}

View File

@ -24,8 +24,8 @@ import org.apache.poi.poifs.filesystem.DirectoryNode;
*
*/
public final class EscherStm extends EscherPart {
private static final String[] PATH = { "Escher", "EscherStm", };
public EscherStm(DirectoryNode baseDir) throws IOException {
super(baseDir, PATH);
}
private static final String[] PATH = { "Escher", "EscherStm", };
public EscherStm(DirectoryNode baseDir) throws IOException {
super(baseDir, PATH);
}
}

View File

@ -31,85 +31,85 @@ import org.apache.poi.util.IOUtils;
* for all of them.
*/
public abstract class HPBFPart {
private byte[] data;
private final String[] path;
private byte[] data;
private final String[] path;
/**
* @param path the path to the part, eg Contents or Quill, QuillSub, CONTENTS
*/
public HPBFPart(DirectoryNode baseDir, String[] path) throws IOException {
this.path = path;
/**
* @param path the path to the part, eg Contents or Quill, QuillSub, CONTENTS
*/
public HPBFPart(DirectoryNode baseDir, String[] path) throws IOException {
this.path = path;
DirectoryNode dir = getDir(baseDir, path);
String name = path[path.length-1];
DirectoryNode dir = getDir(baseDir, path);
String name = path[path.length-1];
if (!dir.hasEntry(name)) {
if (!dir.hasEntry(name)) {
throw new IllegalArgumentException("File invalid - failed to find document entry '" + name + "'");
}
}
// Grab the data from the part stream
try (InputStream is = dir.createDocumentInputStream(name)) {
data = IOUtils.toByteArray(is);
}
}
// Grab the data from the part stream
try (InputStream is = dir.createDocumentInputStream(name)) {
data = IOUtils.toByteArray(is);
}
}
private static DirectoryNode getDir(DirectoryNode baseDir, String[] path) {
DirectoryNode dir = baseDir;
for(int i=0; i<path.length-1; i++) {
try {
dir = (DirectoryNode)dir.getEntry(path[i]);
} catch (FileNotFoundException e) {
throw new IllegalArgumentException("File invalid - failed to find directory entry '"
+ path[i] + "': " + e);
}
}
return dir;
}
private static DirectoryNode getDir(DirectoryNode baseDir, String[] path) {
DirectoryNode dir = baseDir;
for(int i=0; i<path.length-1; i++) {
try {
dir = (DirectoryNode)dir.getEntry(path[i]);
} catch (FileNotFoundException e) {
throw new IllegalArgumentException("File invalid - failed to find directory entry '"
+ path[i] + "': " + e);
}
}
return dir;
}
public void writeOut(DirectoryNode baseDir) throws IOException {
String[] path = getPath();
public void writeOut(DirectoryNode baseDir) throws IOException {
String[] path = getPath();
// Ensure that all parent directories exist
DirectoryNode dir = baseDir;
for(int i=0; i<path.length-1; i++) {
try {
dir = (DirectoryNode)dir.getEntry(path[i]);
} catch(FileNotFoundException e) {
dir.createDirectory(path[i]);
}
}
// Ensure that all parent directories exist
DirectoryNode dir = baseDir;
for(int i=0; i<path.length-1; i++) {
try {
dir = (DirectoryNode)dir.getEntry(path[i]);
} catch(FileNotFoundException e) {
dir.createDirectory(path[i]);
}
}
// Update the byte array with the latest data
generateData();
// Update the byte array with the latest data
generateData();
// Write out
ByteArrayInputStream bais = new ByteArrayInputStream(data);
dir.createDocument(path[path.length-1], bais);
}
// Write out
ByteArrayInputStream bais = new ByteArrayInputStream(data);
dir.createDocument(path[path.length-1], bais);
}
/**
* Called just before writing out, to trigger
* the data byte array to be updated with the
* latest contents.
*/
protected abstract void generateData();
/**
* Called just before writing out, to trigger
* the data byte array to be updated with the
* latest contents.
*/
protected abstract void generateData();
/**
* Returns the raw data that makes up
* this document part.
*/
public final byte[] getData() {
return data;
}
/**
* Returns the raw data that makes up
* this document part.
*/
public final byte[] getData() {
return data;
}
protected final void setData(byte[] data) {
this.data = data.clone();
}
protected final void setData(byte[] data) {
this.data = data.clone();
}
/**
* Returns
*/
public final String[] getPath() {
return path;
}
/**
* Returns
*/
public final String[] getPath() {
return path;
}
}

View File

@ -25,14 +25,14 @@ import org.apache.poi.poifs.filesystem.DirectoryNode;
* The main Contents. Not yet understood
*/
public final class MainContents extends HPBFPart {
private static final String[] PATH = { "Contents", };
private static final String[] PATH = { "Contents", };
public MainContents(DirectoryNode baseDir) throws IOException {
super(baseDir, PATH);
}
public MainContents(DirectoryNode baseDir) throws IOException {
super(baseDir, PATH);
}
protected void generateData() {
// We don't parse the contents, so
// nothing will have changed
}
protected void generateData() {
// We don't parse the contents, so
// nothing will have changed
}
}

View File

@ -34,73 +34,73 @@ import org.apache.poi.util.LocaleUtil;
* Read Quill Contents (/Quill/QuillSub/CONTENTS) from an HPBF (Publisher .pub) document
*/
public final class QuillContents extends HPBFPart {
private static final Logger LOG = LogManager.getLogger(QuillContents.class);
//arbitrarily selected; may need to increase
private static final int MAX_RECORD_LENGTH = 1_000_000;
private static final Logger LOG = LogManager.getLogger(QuillContents.class);
//arbitrarily selected; may need to increase
private static final int MAX_RECORD_LENGTH = 1_000_000;
private static final String[] PATH = { "Quill", "QuillSub", "CONTENTS", };
private final QCBit[] bits;
private static final String[] PATH = { "Quill", "QuillSub", "CONTENTS", };
private final QCBit[] bits;
public QuillContents(DirectoryNode baseDir) throws IOException {
super(baseDir, PATH);
public QuillContents(DirectoryNode baseDir) throws IOException {
super(baseDir, PATH);
// Now parse the first 512 bytes, and produce
// all our bits
// Now parse the first 512 bytes, and produce
// all our bits
byte[] data = getData();
// Check first 8 bytes
String f8 = new String(data, 0, 8, LocaleUtil.CHARSET_1252);
if(! f8.equals("CHNKINK ")) {
throw new IllegalArgumentException("Expecting 'CHNKINK ' but was '"+f8+"'");
}
// Ignore the next 24, for now at least
// Check first 8 bytes
String f8 = new String(data, 0, 8, LocaleUtil.CHARSET_1252);
if(! f8.equals("CHNKINK ")) {
throw new IllegalArgumentException("Expecting 'CHNKINK ' but was '"+f8+"'");
}
// Ignore the next 24, for now at least
// Now, parse all our QC Bits
bits = new QCBit[20];
for(int i=0; i<20; i++) {
int offset = 0x20 + i*24;
if(data[offset] == 0x18 && data[offset+1] == 0x00) {
// Has some data
String thingType = new String(data, offset+2, 4, LocaleUtil.CHARSET_1252);
int optA = LittleEndian.getUShort(data, offset+6);
int optB = LittleEndian.getUShort(data, offset+8);
int optC = LittleEndian.getUShort(data, offset+10);
String bitType = new String(data, offset+12, 4, LocaleUtil.CHARSET_1252);
int from = (int)LittleEndian.getUInt(data, offset+16);
int len = (int)LittleEndian.getUInt(data, offset+20);
// Now, parse all our QC Bits
bits = new QCBit[20];
for(int i=0; i<20; i++) {
int offset = 0x20 + i*24;
if(data[offset] == 0x18 && data[offset+1] == 0x00) {
// Has some data
String thingType = new String(data, offset+2, 4, LocaleUtil.CHARSET_1252);
int optA = LittleEndian.getUShort(data, offset+6);
int optB = LittleEndian.getUShort(data, offset+8);
int optC = LittleEndian.getUShort(data, offset+10);
String bitType = new String(data, offset+12, 4, LocaleUtil.CHARSET_1252);
int from = (int)LittleEndian.getUInt(data, offset+16);
int len = (int)LittleEndian.getUInt(data, offset+20);
byte[] bitData = IOUtils.safelyClone(data, from, len, MAX_RECORD_LENGTH);
byte[] bitData = IOUtils.safelyClone(data, from, len, MAX_RECORD_LENGTH);
// Create
if(bitType.equals("TEXT")) {
bits[i] = new QCTextBit(thingType, bitType, bitData);
} else if(bitType.equals("PLC ")) {
try {
bits[i] = QCPLCBit.createQCPLCBit(thingType, bitType, bitData);
} catch (ArrayIndexOutOfBoundsException e) {
// bug 60685: fall back so that the rest of the document can be read
LOG.atWarn().log("Unable to read Quill Contents PLC Bit record. Ignoring this record.");
bits[i] = new UnknownQCBit(thingType, bitType, bitData);
}
} else {
bits[i] = new UnknownQCBit(thingType, bitType, bitData);
}
bits[i].setOptA(optA);
bits[i].setOptB(optB);
bits[i].setOptC(optC);
bits[i].setDataOffset(from);
} else {
// Doesn't have data
}
}
}
// Create
if(bitType.equals("TEXT")) {
bits[i] = new QCTextBit(thingType, bitType, bitData);
} else if(bitType.equals("PLC ")) {
try {
bits[i] = QCPLCBit.createQCPLCBit(thingType, bitType, bitData);
} catch (ArrayIndexOutOfBoundsException e) {
// bug 60685: fall back so that the rest of the document can be read
LOG.atWarn().log("Unable to read Quill Contents PLC Bit record. Ignoring this record.");
bits[i] = new UnknownQCBit(thingType, bitType, bitData);
}
} else {
bits[i] = new UnknownQCBit(thingType, bitType, bitData);
}
bits[i].setOptA(optA);
bits[i].setOptB(optB);
bits[i].setOptC(optC);
bits[i].setDataOffset(from);
} else {
// Doesn't have data
}
}
}
public QCBit[] getBits() {
return bits;
}
public QCBit[] getBits() {
return bits;
}
protected void generateData() {
// TODO
throw new IllegalStateException("Not done yet!");
}
protected void generateData() {
// TODO
throw new IllegalStateException("Not done yet!");
}
}

View File

@ -21,66 +21,66 @@ package org.apache.poi.hpbf.model.qcbits;
* Parent of all Quill CONTENTS bits
*/
public abstract class QCBit {
private String thingType;
private String bitType;
private byte[] data;
private String thingType;
private String bitType;
private byte[] data;
private int optA;
private int optB;
private int optC;
private int optA;
private int optB;
private int optC;
private int dataOffset;
private int dataOffset;
public QCBit(String thingType, String bitType, byte[] data) {
this.thingType = thingType;
this.bitType = bitType;
this.data = data.clone();
}
public QCBit(String thingType, String bitType, byte[] data) {
this.thingType = thingType;
this.bitType = bitType;
this.data = data.clone();
}
/**
* Returns the type of the thing, eg TEXT, FONT
* or TOKN
*/
public String getThingType() { return thingType; }
/**
* Returns the type of the bit data, eg TEXT
* or PLC
*/
public String getBitType() { return bitType; }
public final byte[] getData() { return data; }
protected final void setData(byte[] data) {
this.data = data.clone();
}
/**
* Returns the type of the thing, eg TEXT, FONT
* or TOKN
*/
public String getThingType() { return thingType; }
/**
* Returns the type of the bit data, eg TEXT
* or PLC
*/
public String getBitType() { return bitType; }
public final byte[] getData() { return data; }
protected final void setData(byte[] data) {
this.data = data.clone();
}
public int getOptA() {
return optA;
}
public void setOptA(int optA) {
this.optA = optA;
}
public int getOptA() {
return optA;
}
public void setOptA(int optA) {
this.optA = optA;
}
public int getOptB() {
return optB;
}
public void setOptB(int optB) {
this.optB = optB;
}
public int getOptB() {
return optB;
}
public void setOptB(int optB) {
this.optB = optB;
}
public int getOptC() {
return optC;
}
public void setOptC(int optC) {
this.optC = optC;
}
public int getOptC() {
return optC;
}
public void setOptC(int optC) {
this.optC = optC;
}
public int getDataOffset() {
return dataOffset;
}
public void setDataOffset(int offset) {
this.dataOffset = offset;
}
public int getDataOffset() {
return dataOffset;
}
public void setDataOffset(int offset) {
this.dataOffset = offset;
}
public int getLength() {
return data.length;
}
public int getLength() {
return data.length;
}
}

View File

@ -26,155 +26,155 @@ import org.apache.poi.util.StringUtil;
* format is determined by the type of the PLCs.
*/
public abstract class QCPLCBit extends QCBit {
private int numberOfPLCs;
private int typeOfPLCS;
/**
* The data which goes before the main PLC entries.
* This is apparently always made up of 2 byte
* un-signed ints..
*/
private int[] preData;
/** The first value of each PLC, normally 4 bytes */
private long[] plcValA;
/** The second value of each PLC, normally 4 bytes */
private long[] plcValB;
private int numberOfPLCs;
private int typeOfPLCS;
/**
* The data which goes before the main PLC entries.
* This is apparently always made up of 2 byte
* un-signed ints..
*/
private int[] preData;
/** The first value of each PLC, normally 4 bytes */
private long[] plcValA;
/** The second value of each PLC, normally 4 bytes */
private long[] plcValB;
private QCPLCBit(String thingType, String bitType, byte[] data) {
super(thingType, bitType, data);
private QCPLCBit(String thingType, String bitType, byte[] data) {
super(thingType, bitType, data);
// First four bytes are the number
numberOfPLCs = (int)LittleEndian.getUInt(data, 0);
// First four bytes are the number
numberOfPLCs = (int)LittleEndian.getUInt(data, 0);
// Next four bytes are the type
typeOfPLCS = (int)LittleEndian.getUInt(data, 4);
// Next four bytes are the type
typeOfPLCS = (int)LittleEndian.getUInt(data, 4);
// Init the arrays that we can
plcValA = new long[numberOfPLCs];
plcValB = new long[numberOfPLCs];
}
// Init the arrays that we can
plcValA = new long[numberOfPLCs];
plcValB = new long[numberOfPLCs];
}
public int getNumberOfPLCs() {
return numberOfPLCs;
}
public int getTypeOfPLCS() {
return typeOfPLCS;
}
public int getNumberOfPLCs() {
return numberOfPLCs;
}
public int getTypeOfPLCS() {
return typeOfPLCS;
}
public int[] getPreData() {
return preData;
}
public int[] getPreData() {
return preData;
}
public long[] getPlcValA() {
return plcValA;
}
public long[] getPlcValB() {
return plcValB;
}
public long[] getPlcValA() {
return plcValA;
}
public long[] getPlcValB() {
return plcValB;
}
final void setPreData(int[] preData) {
this.preData = preData.clone();
}
final void setPreData(int[] preData) {
this.preData = preData.clone();
}
final void setPlcValA(long[] plcValA) {
final void setPlcValA(long[] plcValA) {
this.plcValA = plcValA.clone();
}
final void setPlcValB(long[] plcValB) {
final void setPlcValB(long[] plcValB) {
this.plcValB = plcValB.clone();
}
public static QCPLCBit createQCPLCBit(String thingType, String bitType, byte[] data) {
// Grab the type
int type = (int)LittleEndian.getUInt(data, 4);
switch(type) {
case 0:
return new Type0(thingType, bitType, data);
case 4:
return new Type4(thingType, bitType, data);
case 8:
return new Type8(thingType, bitType, data);
case 12: // 0xc
return new Type12(thingType, bitType, data);
default:
throw new IllegalArgumentException("Sorry, I don't know how to deal with PLCs of type " + type);
}
}
public static QCPLCBit createQCPLCBit(String thingType, String bitType, byte[] data) {
// Grab the type
int type = (int)LittleEndian.getUInt(data, 4);
switch(type) {
case 0:
return new Type0(thingType, bitType, data);
case 4:
return new Type4(thingType, bitType, data);
case 8:
return new Type8(thingType, bitType, data);
case 12: // 0xc
return new Type12(thingType, bitType, data);
default:
throw new IllegalArgumentException("Sorry, I don't know how to deal with PLCs of type " + type);
}
}
/**
* Type 0 seem to be somewhat rare. They have 8 bytes of pre-data,
* then 2x 2 byte values.
*/
public static class Type0 extends QCPLCBit {
private Type0(String thingType, String bitType, byte[] data) {
super(thingType, bitType, data);
/**
* Type 0 seem to be somewhat rare. They have 8 bytes of pre-data,
* then 2x 2 byte values.
*/
public static class Type0 extends QCPLCBit {
private Type0(String thingType, String bitType, byte[] data) {
super(thingType, bitType, data);
// Grab our 4x pre-data
// Grab our 4x pre-data
int[] preData = {
LittleEndian.getUShort(data, 8 + 0),
LittleEndian.getUShort(data, 8 + 2),
LittleEndian.getUShort(data, 8 + 4),
LittleEndian.getUShort(data, 8 + 6)
};
setPreData(preData);
setPreData(preData);
// And grab the 2 byte values
int cntPlcs = getNumberOfPLCs();
long[] plcValA = new long[cntPlcs];
long[] plcValB = new long[cntPlcs];
for(int i=0; i<cntPlcs; i++) {
plcValA[i] = LittleEndian.getUShort(data, 16+(4*i));
plcValB[i] = LittleEndian.getUShort(data, 16+(4*i)+2);
}
setPlcValA(plcValA);
setPlcValB(plcValB);
}
}
/**
* Type 4 is quite common. They have 8 bytes of pre-data,
* then 2x 4 byte values.
*/
public static class Type4 extends QCPLCBit {
private Type4(String thingType, String bitType, byte[] data) {
super(thingType, bitType, data);
// Grab our 4x pre-data
int[] preData = {
LittleEndian.getUShort(data, 8 + 0),
LittleEndian.getUShort(data, 8 + 2),
LittleEndian.getUShort(data, 8 + 4),
LittleEndian.getUShort(data, 8 + 6)
};
setPreData(preData);
// And grab the 4 byte values
int cntPlcs = getNumberOfPLCs();
long[] plcValA = new long[cntPlcs];
long[] plcValB = new long[cntPlcs];
for(int i=0; i<cntPlcs; i++) {
plcValA[i] = LittleEndian.getUInt(data, 16+(8*i));
plcValB[i] = LittleEndian.getUInt(data, 16+(8*i)+4);
}
plcValA[i] = LittleEndian.getUShort(data, 16+(4*i));
plcValB[i] = LittleEndian.getUShort(data, 16+(4*i)+2);
}
setPlcValA(plcValA);
setPlcValB(plcValB);
}
}
}
}
/**
* Type 8 is quite common. They have 14 bytes of pre-data,
* then 2x 4 byte values.
*/
public static class Type8 extends QCPLCBit {
private Type8(String thingType, String bitType, byte[] data) {
super(thingType, bitType, data);
/**
* Type 4 is quite common. They have 8 bytes of pre-data,
* then 2x 4 byte values.
*/
public static class Type4 extends QCPLCBit {
private Type4(String thingType, String bitType, byte[] data) {
super(thingType, bitType, data);
// Grab our 7x pre-data
// Grab our 4x pre-data
int[] preData = {
LittleEndian.getUShort(data, 8 + 0),
LittleEndian.getUShort(data, 8 + 2),
LittleEndian.getUShort(data, 8 + 4),
LittleEndian.getUShort(data, 8 + 6)
};
setPreData(preData);
// And grab the 4 byte values
int cntPlcs = getNumberOfPLCs();
long[] plcValA = new long[cntPlcs];
long[] plcValB = new long[cntPlcs];
for(int i=0; i<cntPlcs; i++) {
plcValA[i] = LittleEndian.getUInt(data, 16+(8*i));
plcValB[i] = LittleEndian.getUInt(data, 16+(8*i)+4);
}
setPlcValA(plcValA);
setPlcValB(plcValB);
}
}
/**
* Type 8 is quite common. They have 14 bytes of pre-data,
* then 2x 4 byte values.
*/
public static class Type8 extends QCPLCBit {
private Type8(String thingType, String bitType, byte[] data) {
super(thingType, bitType, data);
// Grab our 7x pre-data
int[] preData = {
LittleEndian.getUShort(data, 8 + 0),
LittleEndian.getUShort(data, 8 + 2),
@ -186,124 +186,124 @@ public abstract class QCPLCBit extends QCBit {
};
setPreData(preData);
// And grab the 4 byte values
// And grab the 4 byte values
int cntPlcs = getNumberOfPLCs();
long[] plcValA = new long[cntPlcs];
long[] plcValB = new long[cntPlcs];
for(int i=0; i<cntPlcs; i++) {
plcValA[i] = LittleEndian.getUInt(data, 22+(8*i));
plcValB[i] = LittleEndian.getUInt(data, 22+(8*i)+4);
}
plcValA[i] = LittleEndian.getUInt(data, 22+(8*i));
plcValB[i] = LittleEndian.getUInt(data, 22+(8*i)+4);
}
setPlcValA(plcValA);
setPlcValB(plcValB);
}
}
}
}
/**
* Type 12 holds hyperlinks, and is very complex.
* There is normally one of these for each text
* area that contains at least one hyperlinks.
* The character offsets are relative to the start
* of the text area that this applies to.
*/
public static class Type12 extends QCPLCBit {
private String[] hyperlinks;
/**
* Type 12 holds hyperlinks, and is very complex.
* There is normally one of these for each text
* area that contains at least one hyperlinks.
* The character offsets are relative to the start
* of the text area that this applies to.
*/
public static class Type12 extends QCPLCBit {
private String[] hyperlinks;
private static final int oneStartsAt = 0x4c;
private static final int twoStartsAt = 0x68;
private static final int threePlusIncrement = 22;
private static final int oneStartsAt = 0x4c;
private static final int twoStartsAt = 0x68;
private static final int threePlusIncrement = 22;
private Type12(String thingType, String bitType, byte[] data) {
super(thingType, bitType, data);
private Type12(String thingType, String bitType, byte[] data) {
super(thingType, bitType, data);
int cntPlcs = getNumberOfPLCs();
// How many hyperlinks do we really have?
// (zero hyperlinks gets numberOfPLCs=1)
// How many hyperlinks do we really have?
// (zero hyperlinks gets numberOfPLCs=1)
hyperlinks = new String[data.length == 0x34 ? 0 : cntPlcs];
// We have 4 bytes, then the start point of each
// hyperlink, then the end point of the text.
// We have 4 bytes, then the start point of each
// hyperlink, then the end point of the text.
int[] preData = new int[1 + cntPlcs + 1];
for(int i=0; i<preData.length; i++) {
preData[i] = (int)LittleEndian.getUInt(data, 8+(i*4));
}
setPreData(preData);
for(int i=0; i<preData.length; i++) {
preData[i] = (int)LittleEndian.getUInt(data, 8+(i*4));
}
setPreData(preData);
// Then we have a whole bunch of stuff, which grows
// with the number of hyperlinks
// For now, we think these are shorts
int at = 8+4+(cntPlcs*4)+4;
int until = 0x34;
if(cntPlcs == 1 && hyperlinks.length == 1) {
until = oneStartsAt;
} else if(cntPlcs >= 2) {
until = twoStartsAt + (cntPlcs-2)*threePlusIncrement;
}
// Then we have a whole bunch of stuff, which grows
// with the number of hyperlinks
// For now, we think these are shorts
int at = 8+4+(cntPlcs*4)+4;
int until = 0x34;
if(cntPlcs == 1 && hyperlinks.length == 1) {
until = oneStartsAt;
} else if(cntPlcs >= 2) {
until = twoStartsAt + (cntPlcs-2)*threePlusIncrement;
}
long[] plcValA = new long[(until - at) / 2];
long[] plcValB = new long[0];
for(int i=0; i<plcValA.length; i++) {
plcValA[i] = LittleEndian.getUShort(data, at+(i*2));
}
for(int i=0; i<plcValA.length; i++) {
plcValA[i] = LittleEndian.getUShort(data, at+(i*2));
}
setPlcValA(plcValA);
setPlcValB(plcValB);
// Finally, we have a series of lengths + hyperlinks
at = until;
for(int i=0; i<hyperlinks.length; i++) {
int len = LittleEndian.getUShort(data, at);
int first = LittleEndian.getUShort(data, at+2);
if(first == 0) {
// Crazy special case
// Length is in bytes, from the start
// Hyperlink appears to be empty
hyperlinks[i] = "";
at += len;
} else {
// Normal case. Length is in characters
hyperlinks[i] = StringUtil.getFromUnicodeLE(data, at+2, len);
at += 2 + (2*len);
}
}
}
// Finally, we have a series of lengths + hyperlinks
at = until;
for(int i=0; i<hyperlinks.length; i++) {
int len = LittleEndian.getUShort(data, at);
int first = LittleEndian.getUShort(data, at+2);
if(first == 0) {
// Crazy special case
// Length is in bytes, from the start
// Hyperlink appears to be empty
hyperlinks[i] = "";
at += len;
} else {
// Normal case. Length is in characters
hyperlinks[i] = StringUtil.getFromUnicodeLE(data, at+2, len);
at += 2 + (2*len);
}
}
}
/**
* Returns the number of hyperlinks, which should
* either be zero, or the number of PLC bits
*/
public int getNumberOfHyperlinks() {
return hyperlinks.length;
}
/**
* Returns the number of hyperlinks, which should
* either be zero, or the number of PLC bits
*/
public int getNumberOfHyperlinks() {
return hyperlinks.length;
}
/**
* Returns the URL of the hyperlink at the
* given index.
* @param number The hyperlink number, zero based
*/
public String getHyperlink(int number) {
return hyperlinks[number];
}
/**
* Returns where in the text (in characters) the
* hyperlink at the given index starts
* applying to.
* This position is relative to the text area that this
* PLCBit applies to.
* @param number The hyperlink number, zero based
*/
public int getTextStartAt(int number) {
return getPreData()[1+number];
}
/**
* Returns where in the text that this block
* of hyperlinks stops applying to. Normally,
* but not always the end of the text.
* This position is relative to the text area that this
* PLCBit applies to.
*/
public int getAllTextEndAt() {
return getPreData()[getNumberOfPLCs()+1];
}
}
/**
* Returns the URL of the hyperlink at the
* given index.
* @param number The hyperlink number, zero based
*/
public String getHyperlink(int number) {
return hyperlinks[number];
}
/**
* Returns where in the text (in characters) the
* hyperlink at the given index starts
* applying to.
* This position is relative to the text area that this
* PLCBit applies to.
* @param number The hyperlink number, zero based
*/
public int getTextStartAt(int number) {
return getPreData()[1+number];
}
/**
* Returns where in the text that this block
* of hyperlinks stops applying to. Normally,
* but not always the end of the text.
* This position is relative to the text area that this
* PLCBit applies to.
*/
public int getAllTextEndAt() {
return getPreData()[getNumberOfPLCs()+1];
}
}
}

View File

@ -25,24 +25,24 @@ import org.apache.poi.util.StringUtil;
*/
public final class QCTextBit extends QCBit {
//arbitrarily selected; may need to increase
private static final int MAX_RECORD_LENGTH = 1_000_000;
//arbitrarily selected; may need to increase
private static final int MAX_RECORD_LENGTH = 1_000_000;
public QCTextBit(String thingType, String bitType, byte[] data) {
super(thingType, bitType, data);
}
public QCTextBit(String thingType, String bitType, byte[] data) {
super(thingType, bitType, data);
}
/**
* Returns the text. Note that line endings
* are \r and not \n
*/
public String getText() {
return StringUtil.getFromUnicodeLE(getData());
}
/**
* Returns the text. Note that line endings
* are \r and not \n
*/
public String getText() {
return StringUtil.getFromUnicodeLE(getData());
}
public void setText(String text) {
public void setText(String text) {
byte[] data = IOUtils.safelyAllocate(text.length() * 2L, MAX_RECORD_LENGTH);
StringUtil.putUnicodeLE(text, data, 0);
setData(data);
}
StringUtil.putUnicodeLE(text, data, 0);
setData(data);
}
}

View File

@ -22,7 +22,7 @@ package org.apache.poi.hpbf.model.qcbits;
* how to handle explicitly
*/
public final class UnknownQCBit extends QCBit {
public UnknownQCBit(String thingType, String bitType, byte[] data) {
super(thingType, bitType, data);
}
public UnknownQCBit(String thingType, String bitType, byte[] data) {
super(thingType, bitType, data);
}
}

View File

@ -110,7 +110,7 @@ public final class DIB extends Bitmap {
int fileSize = data.length + HEADER_SIZE;
int offset = fileSize - imageSize;
// specifies the size, in bytes, of the bitmap file - must add the length of the header
// specifies the size, in bytes, of the bitmap file - must add the length of the header
LittleEndian.putInt(header, 2, fileSize);
// Reserved; set to zero
LittleEndian.putInt(header, 6, 0);

View File

@ -33,56 +33,56 @@ import org.apache.poi.hslf.usermodel.HSLFSlideShowImpl;
* searches those for text. Prints out any text it finds
*/
public final class PPDrawingTextListing {
public static void main(String[] args) throws IOException {
if(args.length < 1) {
System.err.println("Need to give a filename");
System.exit(1);
}
public static void main(String[] args) throws IOException {
if(args.length < 1) {
System.err.println("Need to give a filename");
System.exit(1);
}
try (HSLFSlideShowImpl ss = new HSLFSlideShowImpl(args[0])) {
try (HSLFSlideShowImpl ss = new HSLFSlideShowImpl(args[0])) {
// Find PPDrawings at any second level position
Record[] records = ss.getRecords();
for (int i = 0; i < records.length; i++) {
Record[] children = records[i].getChildRecords();
if (children != null && children.length != 0) {
for (int j = 0; j < children.length; j++) {
if (children[j] instanceof PPDrawing) {
System.out.println("Found PPDrawing at " + j + " in top level record " + i + " (" + records[i].getRecordType() + ")");
// Find PPDrawings at any second level position
Record[] records = ss.getRecords();
for (int i = 0; i < records.length; i++) {
Record[] children = records[i].getChildRecords();
if (children != null && children.length != 0) {
for (int j = 0; j < children.length; j++) {
if (children[j] instanceof PPDrawing) {
System.out.println("Found PPDrawing at " + j + " in top level record " + i + " (" + records[i].getRecordType() + ")");
// Look for EscherTextboxWrapper's
PPDrawing ppd = (PPDrawing) children[j];
EscherTextboxWrapper[] wrappers = ppd.getTextboxWrappers();
System.out.println(" Has " + wrappers.length + " textbox wrappers within");
// Look for EscherTextboxWrapper's
PPDrawing ppd = (PPDrawing) children[j];
EscherTextboxWrapper[] wrappers = ppd.getTextboxWrappers();
System.out.println(" Has " + wrappers.length + " textbox wrappers within");
// Loop over the wrappers, showing what they contain
for (int k = 0; k < wrappers.length; k++) {
EscherTextboxWrapper tbw = wrappers[k];
System.out.println(" " + k + " has " + tbw.getChildRecords().length + " PPT atoms within");
// Loop over the wrappers, showing what they contain
for (int k = 0; k < wrappers.length; k++) {
EscherTextboxWrapper tbw = wrappers[k];
System.out.println(" " + k + " has " + tbw.getChildRecords().length + " PPT atoms within");
// Loop over the records, printing the text
Record[] pptatoms = tbw.getChildRecords();
for (Record pptatom : pptatoms) {
String text = null;
if (pptatom instanceof TextBytesAtom) {
TextBytesAtom tba = (TextBytesAtom) pptatom;
text = tba.getText();
}
if (pptatom instanceof TextCharsAtom) {
TextCharsAtom tca = (TextCharsAtom) pptatom;
text = tca.getText();
}
// Loop over the records, printing the text
Record[] pptatoms = tbw.getChildRecords();
for (Record pptatom : pptatoms) {
String text = null;
if (pptatom instanceof TextBytesAtom) {
TextBytesAtom tba = (TextBytesAtom) pptatom;
text = tba.getText();
}
if (pptatom instanceof TextCharsAtom) {
TextCharsAtom tca = (TextCharsAtom) pptatom;
text = tca.getText();
}
if (text != null) {
text = text.replace('\r', '\n');
System.out.println(" ''" + text + "''");
}
}
}
}
}
}
}
}
}
if (text != null) {
text = text.replace('\r', '\n');
System.out.println(" ''" + text + "''");
}
}
}
}
}
}
}
}
}
}

View File

@ -131,7 +131,7 @@ public final class PPTXMLDump {
out.write("\"");
}
out.write(">" + CR);
padding++;
padding++;
//this check works both for Escher and PowerPoint records
boolean isContainer = (info & 0x000F) == 0x000F;
if (isContainer) {
@ -141,7 +141,7 @@ public final class PPTXMLDump {
//dump first 100 bytes of the atom data
dump(out, data, pos, Math.min(size, data.length-pos), padding, true);
}
padding--;
padding--;
write(out, "</"+recname + ">" + CR, padding);
pos += size;

View File

@ -31,63 +31,63 @@ import org.apache.poi.hslf.usermodel.HSLFSlideShowImpl;
* and reports how many, and what sorts of things they contain
*/
public final class SLWTListing {
public static void main(String[] args) throws IOException {
if(args.length < 1) {
System.err.println("Need to give a filename");
System.exit(1);
}
public static void main(String[] args) throws IOException {
if(args.length < 1) {
System.err.println("Need to give a filename");
System.exit(1);
}
HSLFSlideShowImpl ss = new HSLFSlideShowImpl(args[0]);
HSLFSlideShowImpl ss = new HSLFSlideShowImpl(args[0]);
// Find the documents, and then their SLWT
Record[] records = ss.getRecords();
for(int i=0; i<records.length; i++) {
if(records[i] instanceof Document) {
Document doc = (Document)records[i];
SlideListWithText[] slwts = doc.getSlideListWithTexts();
// Find the documents, and then their SLWT
Record[] records = ss.getRecords();
for(int i=0; i<records.length; i++) {
if(records[i] instanceof Document) {
Document doc = (Document)records[i];
SlideListWithText[] slwts = doc.getSlideListWithTexts();
System.out.println("Document at " + i + " had " + slwts.length + " SlideListWithTexts");
if(slwts.length == 0) {
System.err.println("** Warning: Should have had at least 1! **");
}
if(slwts.length > 3) {
System.err.println("** Warning: Shouldn't have more than 3!");
}
System.out.println("Document at " + i + " had " + slwts.length + " SlideListWithTexts");
if(slwts.length == 0) {
System.err.println("** Warning: Should have had at least 1! **");
}
if(slwts.length > 3) {
System.err.println("** Warning: Shouldn't have more than 3!");
}
// Check the SLWTs contain what we'd expect
for(int j=0; j<slwts.length; j++) {
SlideListWithText slwt = slwts[j];
Record[] children = slwt.getChildRecords();
// Check the SLWTs contain what we'd expect
for(int j=0; j<slwts.length; j++) {
SlideListWithText slwt = slwts[j];
Record[] children = slwt.getChildRecords();
System.out.println(" - SLWT at " + j + " had " + children.length + " children:");
System.out.println(" - SLWT at " + j + " had " + children.length + " children:");
// Should only have SlideAtomSets if the second one
int numSAS = slwt.getSlideAtomsSets().length;
if(j == 1) {
if(numSAS == 0) {
System.err.println(" ** 2nd SLWT didn't have any SlideAtomSets!");
} else {
System.out.println(" - Contains " + numSAS + " SlideAtomSets");
}
} else {
if(numSAS > 0) {
System.err.println(" ** SLWT " + j + " had " + numSAS + " SlideAtomSets! (expected 0)");
}
}
// Should only have SlideAtomSets if the second one
int numSAS = slwt.getSlideAtomsSets().length;
if(j == 1) {
if(numSAS == 0) {
System.err.println(" ** 2nd SLWT didn't have any SlideAtomSets!");
} else {
System.out.println(" - Contains " + numSAS + " SlideAtomSets");
}
} else {
if(numSAS > 0) {
System.err.println(" ** SLWT " + j + " had " + numSAS + " SlideAtomSets! (expected 0)");
}
}
// Report the first 5 children, to give a flavour
int upTo = 5;
if(children.length < 5) { upTo = children.length; }
for(int k=0; k<upTo; k++) {
Record r = children[k];
int typeID = (int)r.getRecordType();
String typeName = RecordTypes.forTypeID(typeID).name();
System.out.println(" - " + typeID + " (" + typeName + ")");
}
}
}
}
ss.close();
}
// Report the first 5 children, to give a flavour
int upTo = 5;
if(children.length < 5) { upTo = children.length; }
for(int k=0; k<upTo; k++) {
Record r = children[k];
int typeID = (int)r.getRecordType();
String typeName = RecordTypes.forTypeID(typeID).name();
System.out.println(" - " + typeID + " (" + typeName + ")");
}
}
}
}
ss.close();
}
}

View File

@ -33,62 +33,62 @@ import org.apache.poi.hslf.usermodel.HSLFSlideShowImpl;
* what it finds.
*/
public final class SLWTTextListing {
public static void main(String[] args) throws IOException {
if(args.length < 1) {
System.err.println("Need to give a filename");
System.exit(1);
}
public static void main(String[] args) throws IOException {
if(args.length < 1) {
System.err.println("Need to give a filename");
System.exit(1);
}
HSLFSlideShowImpl ss = new HSLFSlideShowImpl(args[0]);
HSLFSlideShowImpl ss = new HSLFSlideShowImpl(args[0]);
// Find the documents, and then their SLWT
Record[] records = ss.getRecords();
for(int i=0; i<records.length; i++) {
if(records[i] instanceof Document) {
Record docRecord = records[i];
Record[] docChildren = docRecord.getChildRecords();
for(int j=0; j<docChildren.length; j++) {
if(docChildren[j] instanceof SlideListWithText) {
System.out.println("Found SLWT at pos " + j + " in the Document at " + i);
System.out.println(" Has " + docChildren[j].getChildRecords().length + " children");
// Find the documents, and then their SLWT
Record[] records = ss.getRecords();
for(int i=0; i<records.length; i++) {
if(records[i] instanceof Document) {
Record docRecord = records[i];
Record[] docChildren = docRecord.getChildRecords();
for(int j=0; j<docChildren.length; j++) {
if(docChildren[j] instanceof SlideListWithText) {
System.out.println("Found SLWT at pos " + j + " in the Document at " + i);
System.out.println(" Has " + docChildren[j].getChildRecords().length + " children");
// Grab the SlideAtomSet's, which contain
// a SlidePersistAtom and then a bunch of text
// + related records
SlideListWithText slwt = (SlideListWithText)docChildren[j];
SlideListWithText.SlideAtomsSet[] thisSets = slwt.getSlideAtomsSets();
System.out.println(" Has " + thisSets.length + " AtomSets in it");
// Grab the SlideAtomSet's, which contain
// a SlidePersistAtom and then a bunch of text
// + related records
SlideListWithText slwt = (SlideListWithText)docChildren[j];
SlideListWithText.SlideAtomsSet[] thisSets = slwt.getSlideAtomsSets();
System.out.println(" Has " + thisSets.length + " AtomSets in it");
// Loop over the sets, showing what they contain
for(int k=0; k<thisSets.length; k++) {
SlidePersistAtom spa = thisSets[k].getSlidePersistAtom();
System.out.println(" " + k + " has slide id " + spa.getSlideIdentifier() );
System.out.println(" " + k + " has ref id " + spa.getRefID() );
// Loop over the sets, showing what they contain
for(int k=0; k<thisSets.length; k++) {
SlidePersistAtom spa = thisSets[k].getSlidePersistAtom();
System.out.println(" " + k + " has slide id " + spa.getSlideIdentifier() );
System.out.println(" " + k + " has ref id " + spa.getRefID() );
// Loop over the records, printing the text
Record[] slwtc = thisSets[k].getSlideRecords();
for(int l=0; l<slwtc.length; l++) {
String text = null;
if(slwtc[l] instanceof TextBytesAtom) {
TextBytesAtom tba = (TextBytesAtom)slwtc[l];
text = tba.getText();
}
if(slwtc[l] instanceof TextCharsAtom) {
TextCharsAtom tca = (TextCharsAtom)slwtc[l];
text = tca.getText();
}
// Loop over the records, printing the text
Record[] slwtc = thisSets[k].getSlideRecords();
for(int l=0; l<slwtc.length; l++) {
String text = null;
if(slwtc[l] instanceof TextBytesAtom) {
TextBytesAtom tba = (TextBytesAtom)slwtc[l];
text = tba.getText();
}
if(slwtc[l] instanceof TextCharsAtom) {
TextCharsAtom tca = (TextCharsAtom)slwtc[l];
text = tca.getText();
}
if(text != null) {
text = text.replace('\r','\n');
System.out.println(" ''" + text + "''");
}
}
}
}
}
}
}
ss.close();
}
if(text != null) {
text = text.replace('\r','\n');
System.out.println(" ''" + text + "''");
}
}
}
}
}
}
}
ss.close();
}
}

View File

@ -34,38 +34,38 @@ import org.apache.poi.hslf.usermodel.HSLFSlideShowImpl;
* Slides, Master Slides and Notes
*/
public final class SlideAndNotesAtomListing {
public static void main(String[] args) throws IOException {
if(args.length < 1) {
System.err.println("Need to give a filename");
System.exit(1);
}
public static void main(String[] args) throws IOException {
if(args.length < 1) {
System.err.println("Need to give a filename");
System.exit(1);
}
HSLFSlideShowImpl ss = new HSLFSlideShowImpl(args[0]);
System.out.println();
HSLFSlideShowImpl ss = new HSLFSlideShowImpl(args[0]);
System.out.println();
// Find either Slides or Notes
Record[] records = ss.getRecords();
for(int i=0; i<records.length; i++) {
Record r = records[i];
// Find either Slides or Notes
Record[] records = ss.getRecords();
for(int i=0; i<records.length; i++) {
Record r = records[i];
// When we find them, print out their IDs
if(r instanceof Slide) {
Slide s = (Slide)r;
SlideAtom sa = s.getSlideAtom();
System.out.println("Found Slide at " + i);
System.out.println(" Slide's master ID is " + sa.getMasterID());
System.out.println(" Slide's notes ID is " + sa.getNotesID());
System.out.println();
}
if(r instanceof Notes) {
Notes n = (Notes)r;
NotesAtom na = n.getNotesAtom();
System.out.println("Found Notes at " + i);
System.out.println(" Notes ID is " + na.getSlideID());
System.out.println();
}
}
ss.close();
}
// When we find them, print out their IDs
if(r instanceof Slide) {
Slide s = (Slide)r;
SlideAtom sa = s.getSlideAtom();
System.out.println("Found Slide at " + i);
System.out.println(" Slide's master ID is " + sa.getMasterID());
System.out.println(" Slide's notes ID is " + sa.getNotesID());
System.out.println();
}
if(r instanceof Notes) {
Notes n = (Notes)r;
NotesAtom na = n.getNotesAtom();
System.out.println("Found Notes at " + i);
System.out.println(" Notes ID is " + na.getSlideID());
System.out.println();
}
}
ss.close();
}
}

View File

@ -49,10 +49,10 @@ import org.apache.poi.util.LittleEndian;
*/
public final class SlideShowDumper {
//arbitrarily selected; may need to increase
private static final int MAX_RECORD_LENGTH = 100_000;
//arbitrarily selected; may need to increase
private static final int MAX_RECORD_LENGTH = 100_000;
private byte[] docstream;
private byte[] docstream;
/** Do we try to use DDF to understand the escher objects? */
private boolean ddfEscher;
@ -67,29 +67,29 @@ public final class SlideShowDumper {
*/
public static void main(String[] args) throws IOException
{
if(args.length == 0) {
System.err.println("Usage: SlideShowDumper [-escher|-basicescher] <filename>");
return;
}
if(args.length == 0) {
System.err.println("Usage: SlideShowDumper [-escher|-basicescher] <filename>");
return;
}
String filename = args[0];
if(args.length > 1) {
filename = args[1];
}
String filename = args[0];
if(args.length > 1) {
filename = args[1];
}
try (POIFSFileSystem poifs = new POIFSFileSystem(new File(filename))) {
SlideShowDumper foo = new SlideShowDumper(poifs, System.out);
try (POIFSFileSystem poifs = new POIFSFileSystem(new File(filename))) {
SlideShowDumper foo = new SlideShowDumper(poifs, System.out);
if(args.length > 1) {
if(args[0].equalsIgnoreCase("-escher")) {
foo.setDDFEscher(true);
} else {
foo.setBasicEscher(true);
}
}
if(args.length > 1) {
if(args[0].equalsIgnoreCase("-escher")) {
foo.setDDFEscher(true);
} else {
foo.setBasicEscher(true);
}
}
foo.printDump();
}
foo.printDump();
}
}
/**
@ -100,19 +100,19 @@ public final class SlideShowDumper {
* @throws IOException if there is a problem while parsing the document.
*/
public SlideShowDumper(POIFSFileSystem filesystem, PrintStream out) throws IOException {
// Grab the document stream
InputStream is = filesystem.createDocumentInputStream(HSLFSlideShow.POWERPOINT_DOCUMENT);
docstream = IOUtils.toByteArray(is);
is.close();
this.out = out;
// Grab the document stream
InputStream is = filesystem.createDocumentInputStream(HSLFSlideShow.POWERPOINT_DOCUMENT);
docstream = IOUtils.toByteArray(is);
is.close();
this.out = out;
}
/**
* Control dumping of any Escher records found - should DDF be used?
*/
public void setDDFEscher(boolean grok) {
ddfEscher = grok;
basicEscher = !(grok);
ddfEscher = grok;
basicEscher = !(grok);
}
/**
@ -120,205 +120,205 @@ public final class SlideShowDumper {
* basic groker be used?
*/
public void setBasicEscher(boolean grok) {
basicEscher = grok;
ddfEscher = !(grok);
basicEscher = grok;
ddfEscher = !(grok);
}
public void printDump() throws IOException {
// The format of records in a powerpoint file are:
// <little endian 2 byte "info">
// <little endian 2 byte "type">
// <little endian 4 byte "length">
// If it has a zero length, following it will be another record
// <xx xx yy yy 00 00 00 00> <xx xx yy yy zz zz zz zz>
// If it has a length, depending on its type it may have children or data
// If it has children, these will follow straight away
// <xx xx yy yy zz zz zz zz <xx xx yy yy zz zz zz zz>>
// If it has data, this will come straigh after, and run for the length
// <xx xx yy yy zz zz zz zz dd dd dd dd dd dd dd>
// All lengths given exclude the 8 byte record header
// (Data records are known as Atoms)
// The format of records in a powerpoint file are:
// <little endian 2 byte "info">
// <little endian 2 byte "type">
// <little endian 4 byte "length">
// If it has a zero length, following it will be another record
// <xx xx yy yy 00 00 00 00> <xx xx yy yy zz zz zz zz>
// If it has a length, depending on its type it may have children or data
// If it has children, these will follow straight away
// <xx xx yy yy zz zz zz zz <xx xx yy yy zz zz zz zz>>
// If it has data, this will come straigh after, and run for the length
// <xx xx yy yy zz zz zz zz dd dd dd dd dd dd dd>
// All lengths given exclude the 8 byte record header
// (Data records are known as Atoms)
// Document should start with:
// 0F 00 E8 03 ## ## ## ##
// Document should start with:
// 0F 00 E8 03 ## ## ## ##
// (type 1000 = document, info 00 0f is normal, rest is document length)
// 01 00 E9 03 28 00 00 00
// (type 1001 = document atom, info 00 01 normal, 28 bytes long)
// 01 00 E9 03 28 00 00 00
// (type 1001 = document atom, info 00 01 normal, 28 bytes long)
// When parsing a document, look to see if you know about that type
// of the current record. If you know it's a type that has children,
// process the record's data area looking for more records
// If you know about the type and it doesn't have children, either do
// something with the data (eg TextRun) or skip over it
// Otherwise, check the first byte. If you do a BINARY_AND on it with
// 0x0f (15) and get back 0x0f, you know it has children. Otherwise
// it doesn't
// When parsing a document, look to see if you know about that type
// of the current record. If you know it's a type that has children,
// process the record's data area looking for more records
// If you know about the type and it doesn't have children, either do
// something with the data (eg TextRun) or skip over it
// Otherwise, check the first byte. If you do a BINARY_AND on it with
// 0x0f (15) and get back 0x0f, you know it has children. Otherwise
// it doesn't
walkTree(0,0,docstream.length);
walkTree(0,0,docstream.length);
}
public void walkTree(int depth, int startPos, int maxLen) throws IOException {
int pos = startPos;
int endPos = startPos + maxLen;
final String ind = (depth == 0) ? "%1$s" : "%1$"+depth+"s";
while(pos <= endPos - 8) {
long type = LittleEndian.getUShort(docstream,pos+2);
long len = LittleEndian.getUInt(docstream,pos+4);
byte opt = docstream[pos];
int pos = startPos;
int endPos = startPos + maxLen;
final String ind = (depth == 0) ? "%1$s" : "%1$"+depth+"s";
while(pos <= endPos - 8) {
long type = LittleEndian.getUShort(docstream,pos+2);
long len = LittleEndian.getUInt(docstream,pos+4);
byte opt = docstream[pos];
String fmt = ind+"At position %2$d (%2$04x): type is %3$d (%3$04x), len is %4$d (%4$04x)";
out.println(String.format(Locale.ROOT, fmt, "", pos, type, len));
String fmt = ind+"At position %2$d (%2$04x): type is %3$d (%3$04x), len is %4$d (%4$04x)";
out.println(String.format(Locale.ROOT, fmt, "", pos, type, len));
// See if we know about the type of it
String recordName = RecordTypes.forTypeID((short)type).name();
// See if we know about the type of it
String recordName = RecordTypes.forTypeID((short)type).name();
// Jump over header, and think about going on more
pos += 8;
out.println(String.format(Locale.ROOT, ind+"That's a %2$s", "", recordName));
// Jump over header, and think about going on more
pos += 8;
out.println(String.format(Locale.ROOT, ind+"That's a %2$s", "", recordName));
// Now check if it's a container or not
int container = opt & 0x0f;
// Now check if it's a container or not
int container = opt & 0x0f;
// BinaryTagData seems to contain records, but it
// isn't tagged as doing so. Try stepping in anyway
if(type == 5003L && opt == 0L) {
container = 0x0f;
}
// BinaryTagData seems to contain records, but it
// isn't tagged as doing so. Try stepping in anyway
if(type == 5003L && opt == 0L) {
container = 0x0f;
}
out.println();
if (type != 0L && container == 0x0f) {
if (type == 1035L || type == 1036L) {
// Special Handling of 1035=PPDrawingGroup and 1036=PPDrawing
if(ddfEscher) {
// Seems to be:
walkEscherDDF((depth+3),pos+8,(int)len-8);
} else if(basicEscher) {
walkEscherBasic((depth+3),pos+8,(int)len-8);
}
} else {
// General container record handling code
walkTree((depth+2),pos,(int)len);
}
}
if (type != 0L && container == 0x0f) {
if (type == 1035L || type == 1036L) {
// Special Handling of 1035=PPDrawingGroup and 1036=PPDrawing
if(ddfEscher) {
// Seems to be:
walkEscherDDF((depth+3),pos+8,(int)len-8);
} else if(basicEscher) {
walkEscherBasic((depth+3),pos+8,(int)len-8);
}
} else {
// General container record handling code
walkTree((depth+2),pos,(int)len);
}
}
pos += (int)len;
}
pos += (int)len;
}
}
/**
* Use the DDF code to walk the Escher records
*/
public void walkEscherDDF(int indent, int pos, int len) {
if(len < 8) { return; }
if(len < 8) { return; }
final String ind = (indent == 0) ? "%1$s" : "%1$"+indent+"s";
final String ind = (indent == 0) ? "%1$s" : "%1$"+indent+"s";
byte[] contents = IOUtils.safelyClone(docstream, pos, len, MAX_RECORD_LENGTH);
DefaultEscherRecordFactory erf = new HSLFEscherRecordFactory();
EscherRecord record = erf.createRecord(contents,0);
DefaultEscherRecordFactory erf = new HSLFEscherRecordFactory();
EscherRecord record = erf.createRecord(contents,0);
// For now, try filling in the fields
record.fillFields(contents,0,erf);
// For now, try filling in the fields
record.fillFields(contents,0,erf);
long atomType = LittleEndian.getUShort(contents,2);
// This lacks the 8 byte header size
long atomLen = LittleEndian.getUShort(contents,4);
// This (should) include the 8 byte header size
int recordLen = record.getRecordSize();
long atomType = LittleEndian.getUShort(contents,2);
// This lacks the 8 byte header size
long atomLen = LittleEndian.getUShort(contents,4);
// This (should) include the 8 byte header size
int recordLen = record.getRecordSize();
String fmt = ind+"At position %2$d (%2$04x): type is %3$d (%3$04x), len is %4$d (%4$04x) (%5$d) - record claims %6$d";
out.println(String.format(Locale.ROOT, fmt, "", pos, atomType, atomLen, atomLen+8, recordLen));
// Check for corrupt / lying ones
if(recordLen != 8 && (recordLen != (atomLen+8))) {
out.println(String.format(Locale.ROOT, ind+"** Atom length of $2d ($3d) doesn't match record length of %4d", "", atomLen, atomLen+8, recordLen));
}
// Check for corrupt / lying ones
if(recordLen != 8 && (recordLen != (atomLen+8))) {
out.println(String.format(Locale.ROOT, ind+"** Atom length of $2d ($3d) doesn't match record length of %4d", "", atomLen, atomLen+8, recordLen));
}
// Print the record's details
// Print the record's details
String recordStr = record.toString().replace("\n", String.format(Locale.ROOT, "\n"+ind, ""));
out.println(String.format(Locale.ROOT, ind+"%2$s", "", recordStr));
if(record instanceof EscherContainerRecord) {
walkEscherDDF((indent+3), pos + 8, (int)atomLen );
}
walkEscherDDF((indent+3), pos + 8, (int)atomLen );
}
// Handle records that seem to lie
if(atomType == 61451L) {
// Normally claims a size of 8
recordLen = (int)atomLen + 8;
}
if(atomType == 61453L) {
// Returns EscherContainerRecord, but really msofbtClientTextbox
recordLen = (int)atomLen + 8;
record.fillFields( contents, 0, erf );
if(! (record instanceof EscherTextboxRecord)) {
out.println(String.format(Locale.ROOT, ind+"%2$s", "", "** Really a msofbtClientTextbox !"));
}
}
// Handle records that seem to lie
if(atomType == 61451L) {
// Normally claims a size of 8
recordLen = (int)atomLen + 8;
}
if(atomType == 61453L) {
// Returns EscherContainerRecord, but really msofbtClientTextbox
recordLen = (int)atomLen + 8;
record.fillFields( contents, 0, erf );
if(! (record instanceof EscherTextboxRecord)) {
out.println(String.format(Locale.ROOT, ind+"%2$s", "", "** Really a msofbtClientTextbox !"));
}
}
// Decide on what to do, based on how the lengths match up
if(recordLen == 8 && atomLen > 8 ) {
// Assume it has children, rather than being corrupted
walkEscherDDF((indent+3), pos + 8, (int)atomLen );
// Decide on what to do, based on how the lengths match up
if(recordLen == 8 && atomLen > 8 ) {
// Assume it has children, rather than being corrupted
walkEscherDDF((indent+3), pos + 8, (int)atomLen );
// Wind on our length + our header
pos += atomLen;
pos += 8;
len -= atomLen;
len -= 8;
} else {
// No children, wind on our real length
pos += atomLen;
pos += 8;
len -= atomLen;
len -= 8;
}
// Wind on our length + our header
pos += atomLen;
pos += 8;
len -= atomLen;
len -= 8;
} else {
// No children, wind on our real length
pos += atomLen;
pos += 8;
len -= atomLen;
len -= 8;
}
// Move on to the next one, if we're not at the end yet
if(len >= 8) {
walkEscherDDF(indent, pos, len );
}
// Move on to the next one, if we're not at the end yet
if(len >= 8) {
walkEscherDDF(indent, pos, len );
}
}
/**
* Use the basic record format groking code to walk the Escher records
*/
public void walkEscherBasic(int indent, int pos, int len) throws IOException {
if(len < 8) { return; }
if(len < 8) { return; }
final String ind = (indent == 0) ? "%1$s" : "%1$"+indent+"s";
final String ind = (indent == 0) ? "%1$s" : "%1$"+indent+"s";
long type = LittleEndian.getUShort(docstream,pos+2);
long atomlen = LittleEndian.getUInt(docstream,pos+4);
long type = LittleEndian.getUShort(docstream,pos+2);
long atomlen = LittleEndian.getUInt(docstream,pos+4);
String fmt = ind+"At position %2$d ($2$04x): type is %3$d (%3$04x), len is %4$d (%4$04x)";
out.println(String.format(Locale.ROOT, fmt, "", pos, type, atomlen));
String fmt = ind+"At position %2$d ($2$04x): type is %3$d (%3$04x), len is %4$d (%4$04x)";
out.println(String.format(Locale.ROOT, fmt, "", pos, type, atomlen));
String typeName = RecordTypes.forTypeID((short)type).name();
out.println(String.format(Locale.ROOT, ind+"%2$s", "That's an Escher Record: ", typeName));
String typeName = RecordTypes.forTypeID((short)type).name();
out.println(String.format(Locale.ROOT, ind+"%2$s", "That's an Escher Record: ", typeName));
// Record specific dumps
if(type == 61453L) {
// Text Box. Print out first 8 bytes of data, then 8 4 later
HexDump.dump(docstream, 0, out, pos+8, 8);
HexDump.dump(docstream, 0, out, pos+20, 8);
out.println();
}
// Record specific dumps
if(type == 61453L) {
// Text Box. Print out first 8 bytes of data, then 8 4 later
HexDump.dump(docstream, 0, out, pos+8, 8);
HexDump.dump(docstream, 0, out, pos+20, 8);
out.println();
}
// Blank line before next entry
out.println();
// Blank line before next entry
out.println();
// Look in children if we are a container
if(type == 61443L || type == 61444L) {
walkEscherBasic((indent+3), pos+8, (int)atomlen);
}
// Look in children if we are a container
if(type == 61443L || type == 61444L) {
walkEscherBasic((indent+3), pos+8, (int)atomlen);
}
// Keep going if not yet at end
if(atomlen < len) {
int atomleni = (int)atomlen;
walkEscherBasic(indent, pos+atomleni+8, len-atomleni-8);
}
// Keep going if not yet at end
if(atomlen < len) {
int atomleni = (int)atomlen;
walkEscherBasic(indent, pos+atomleni+8, len-atomleni-8);
}
}
}

View File

@ -165,14 +165,14 @@ public final class SlideShowRecordDumper {
}
public void printEscherRecord(EscherRecord er, int indent) {
if (er instanceof EscherContainerRecord) {
printEscherContainerRecord( (EscherContainerRecord)er, indent );
} else if (er instanceof EscherTextboxRecord) {
printEscherTextBox( (EscherTextboxRecord)er, indent );
} else {
ps.print( tabs.substring(0, indent) );
ps.println(er);
}
if (er instanceof EscherContainerRecord) {
printEscherContainerRecord( (EscherContainerRecord)er, indent );
} else if (er instanceof EscherTextboxRecord) {
printEscherTextBox( (EscherTextboxRecord)er, indent );
} else {
ps.print( tabs.substring(0, indent) );
ps.println(er);
}
}
private void printEscherTextBox( EscherTextboxRecord tbRecord, int indent ) {

View File

@ -35,84 +35,84 @@ import org.apache.poi.hslf.usermodel.HSLFSlideShowImpl;
* Having found them, it shows the contents
*/
public final class TextStyleListing {
public static void main(String[] args) throws IOException {
if(args.length < 1) {
System.err.println("Need to give a filename");
System.exit(1);
}
public static void main(String[] args) throws IOException {
if(args.length < 1) {
System.err.println("Need to give a filename");
System.exit(1);
}
try (HSLFSlideShowImpl ss = new HSLFSlideShowImpl(args[0])) {
// Find the documents, and then their SLWT
Record[] records = ss.getRecords();
for (org.apache.poi.hslf.record.Record record : records) {
if (record.getRecordType() == 1000L) {
Record[] docChildren = record.getChildRecords();
for (Record docChild : docChildren) {
if (docChild instanceof SlideListWithText) {
Record[] slwtChildren = docChild.getChildRecords();
try (HSLFSlideShowImpl ss = new HSLFSlideShowImpl(args[0])) {
// Find the documents, and then their SLWT
Record[] records = ss.getRecords();
for (org.apache.poi.hslf.record.Record record : records) {
if (record.getRecordType() == 1000L) {
Record[] docChildren = record.getChildRecords();
for (Record docChild : docChildren) {
if (docChild instanceof SlideListWithText) {
Record[] slwtChildren = docChild.getChildRecords();
int lastTextLen = -1;
for (Record slwtChild : slwtChildren) {
if (slwtChild instanceof TextCharsAtom) {
lastTextLen = ((TextCharsAtom) slwtChild).getText().length();
}
if (slwtChild instanceof TextBytesAtom) {
lastTextLen = ((TextBytesAtom) slwtChild).getText().length();
}
int lastTextLen = -1;
for (Record slwtChild : slwtChildren) {
if (slwtChild instanceof TextCharsAtom) {
lastTextLen = ((TextCharsAtom) slwtChild).getText().length();
}
if (slwtChild instanceof TextBytesAtom) {
lastTextLen = ((TextBytesAtom) slwtChild).getText().length();
}
if (slwtChild instanceof StyleTextPropAtom) {
StyleTextPropAtom stpa = (StyleTextPropAtom) slwtChild;
stpa.setParentTextSize(lastTextLen);
showStyleTextPropAtom(stpa);
}
}
}
}
}
}
}
}
if (slwtChild instanceof StyleTextPropAtom) {
StyleTextPropAtom stpa = (StyleTextPropAtom) slwtChild;
stpa.setParentTextSize(lastTextLen);
showStyleTextPropAtom(stpa);
}
}
}
}
}
}
}
}
public static void showStyleTextPropAtom(StyleTextPropAtom stpa) {
System.out.println("\nFound a StyleTextPropAtom");
public static void showStyleTextPropAtom(StyleTextPropAtom stpa) {
System.out.println("\nFound a StyleTextPropAtom");
List<TextPropCollection> paragraphStyles = stpa.getParagraphStyles();
System.out.println("Contains " + paragraphStyles.size() + " paragraph styles:");
for(int i=0; i<paragraphStyles.size(); i++) {
TextPropCollection tpc = paragraphStyles.get(i);
System.out.println(" In paragraph styling " + i + ":");
System.out.println(" Characters covered is " + tpc.getCharactersCovered());
showTextProps(tpc);
}
List<TextPropCollection> paragraphStyles = stpa.getParagraphStyles();
System.out.println("Contains " + paragraphStyles.size() + " paragraph styles:");
for(int i=0; i<paragraphStyles.size(); i++) {
TextPropCollection tpc = paragraphStyles.get(i);
System.out.println(" In paragraph styling " + i + ":");
System.out.println(" Characters covered is " + tpc.getCharactersCovered());
showTextProps(tpc);
}
List<TextPropCollection> charStyles = stpa.getCharacterStyles();
System.out.println("Contains " + charStyles.size() + " character styles:");
for(int i=0; i<charStyles.size(); i++) {
TextPropCollection tpc = charStyles.get(i);
System.out.println(" In character styling " + i + ":");
System.out.println(" Characters covered is " + tpc.getCharactersCovered());
showTextProps(tpc);
}
}
List<TextPropCollection> charStyles = stpa.getCharacterStyles();
System.out.println("Contains " + charStyles.size() + " character styles:");
for(int i=0; i<charStyles.size(); i++) {
TextPropCollection tpc = charStyles.get(i);
System.out.println(" In character styling " + i + ":");
System.out.println(" Characters covered is " + tpc.getCharactersCovered());
showTextProps(tpc);
}
}
public static void showTextProps(TextPropCollection tpc) {
List<TextProp> textProps = tpc.getTextPropList();
System.out.println(" Contains " + textProps.size() + " TextProps");
for(int i=0; i<textProps.size(); i++) {
TextProp tp = textProps.get(i);
System.out.println(" " + i + " - " + tp.getName());
System.out.println(" = " + tp.getValue());
System.out.println(" @ " + tp.getMask());
public static void showTextProps(TextPropCollection tpc) {
List<TextProp> textProps = tpc.getTextPropList();
System.out.println(" Contains " + textProps.size() + " TextProps");
for(int i=0; i<textProps.size(); i++) {
TextProp tp = textProps.get(i);
System.out.println(" " + i + " - " + tp.getName());
System.out.println(" = " + tp.getValue());
System.out.println(" @ " + tp.getMask());
if(tp instanceof BitMaskTextProp) {
BitMaskTextProp bmtp = (BitMaskTextProp)tp;
String[] subPropNames = bmtp.getSubPropNames();
boolean[] subPropMatches = bmtp.getSubPropMatches();
for(int j=0; j<subPropNames.length; j++) {
System.out.println(" -> " + j + " - " + subPropNames[j]);
System.out.println(" " + j + " = " + subPropMatches[j]);
}
}
}
}
if(tp instanceof BitMaskTextProp) {
BitMaskTextProp bmtp = (BitMaskTextProp)tp;
String[] subPropNames = bmtp.getSubPropNames();
boolean[] subPropMatches = bmtp.getSubPropMatches();
for(int j=0; j<subPropNames.length; j++) {
System.out.println(" -> " + j + " - " + subPropNames[j]);
System.out.println(" " + j + " = " + subPropMatches[j]);
}
}
}
}
}

View File

@ -35,93 +35,93 @@ import org.apache.poi.util.LittleEndian;
* illuminate quite what all the offsets mean
*/
public final class UserEditAndPersistListing {
private static byte[] fileContents;
private static byte[] fileContents;
public static void main(String[] args) throws IOException {
if(args.length < 1) {
System.err.println("Need to give a filename");
System.exit(1);
}
public static void main(String[] args) throws IOException {
if(args.length < 1) {
System.err.println("Need to give a filename");
System.exit(1);
}
// Create the slideshow object, for normal working with
try (HSLFSlideShowImpl ss = new HSLFSlideShowImpl(args[0])) {
fileContents = ss.getUnderlyingBytes();
System.out.println();
// Create the slideshow object, for normal working with
try (HSLFSlideShowImpl ss = new HSLFSlideShowImpl(args[0])) {
fileContents = ss.getUnderlyingBytes();
System.out.println();
// Find any persist ones first
int pos = 0;
for (org.apache.poi.hslf.record.Record r : ss.getRecords()) {
if (r.getRecordType() == 6001L) {
// PersistPtrFullBlock
System.out.println("Found PersistPtrFullBlock at " + pos + " (" + Integer.toHexString(pos) + ")");
}
if (r.getRecordType() == 6002L) {
// PersistPtrIncrementalBlock
System.out.println("Found PersistPtrIncrementalBlock at " + pos + " (" + Integer.toHexString(pos) + ")");
PersistPtrHolder pph = (PersistPtrHolder) r;
// Find any persist ones first
int pos = 0;
for (org.apache.poi.hslf.record.Record r : ss.getRecords()) {
if (r.getRecordType() == 6001L) {
// PersistPtrFullBlock
System.out.println("Found PersistPtrFullBlock at " + pos + " (" + Integer.toHexString(pos) + ")");
}
if (r.getRecordType() == 6002L) {
// PersistPtrIncrementalBlock
System.out.println("Found PersistPtrIncrementalBlock at " + pos + " (" + Integer.toHexString(pos) + ")");
PersistPtrHolder pph = (PersistPtrHolder) r;
// Check the sheet offsets
Map<Integer, Integer> sheetOffsets = pph.getSlideLocationsLookup();
for (int id : pph.getKnownSlideIDs()) {
Integer offset = sheetOffsets.get(id);
// Check the sheet offsets
Map<Integer, Integer> sheetOffsets = pph.getSlideLocationsLookup();
for (int id : pph.getKnownSlideIDs()) {
Integer offset = sheetOffsets.get(id);
System.out.println(" Knows about sheet " + id);
System.out.println(" That sheet lives at " + offset);
System.out.println(" Knows about sheet " + id);
System.out.println(" That sheet lives at " + offset);
Record atPos = findRecordAtPos(offset);
System.out.println(" The record at that pos is of type " + atPos.getRecordType());
System.out.println(" The record at that pos has class " + atPos.getClass().getName());
Record atPos = findRecordAtPos(offset);
System.out.println(" The record at that pos is of type " + atPos.getRecordType());
System.out.println(" The record at that pos has class " + atPos.getClass().getName());
if (!(atPos instanceof PositionDependentRecord)) {
System.out.println(" ** The record class isn't position aware! **");
}
}
}
if (!(atPos instanceof PositionDependentRecord)) {
System.out.println(" ** The record class isn't position aware! **");
}
}
}
// Increase the position by the on disk size
UnsynchronizedByteArrayOutputStream baos = new UnsynchronizedByteArrayOutputStream();
r.writeOut(baos);
pos += baos.size();
}
// Increase the position by the on disk size
UnsynchronizedByteArrayOutputStream baos = new UnsynchronizedByteArrayOutputStream();
r.writeOut(baos);
pos += baos.size();
}
System.out.println();
System.out.println();
pos = 0;
// Now look for UserEditAtoms
for (org.apache.poi.hslf.record.Record r : ss.getRecords()) {
if (r instanceof UserEditAtom) {
UserEditAtom uea = (UserEditAtom) r;
System.out.println("Found UserEditAtom at " + pos + " (" + Integer.toHexString(pos) + ")");
System.out.println(" lastUserEditAtomOffset = " + uea.getLastUserEditAtomOffset());
System.out.println(" persistPointersOffset = " + uea.getPersistPointersOffset());
System.out.println(" docPersistRef = " + uea.getDocPersistRef());
System.out.println(" maxPersistWritten = " + uea.getMaxPersistWritten());
}
pos = 0;
// Now look for UserEditAtoms
for (org.apache.poi.hslf.record.Record r : ss.getRecords()) {
if (r instanceof UserEditAtom) {
UserEditAtom uea = (UserEditAtom) r;
System.out.println("Found UserEditAtom at " + pos + " (" + Integer.toHexString(pos) + ")");
System.out.println(" lastUserEditAtomOffset = " + uea.getLastUserEditAtomOffset());
System.out.println(" persistPointersOffset = " + uea.getPersistPointersOffset());
System.out.println(" docPersistRef = " + uea.getDocPersistRef());
System.out.println(" maxPersistWritten = " + uea.getMaxPersistWritten());
}
// Increase the position by the on disk size
UnsynchronizedByteArrayOutputStream baos = new UnsynchronizedByteArrayOutputStream();
r.writeOut(baos);
pos += baos.size();
}
// Increase the position by the on disk size
UnsynchronizedByteArrayOutputStream baos = new UnsynchronizedByteArrayOutputStream();
r.writeOut(baos);
pos += baos.size();
}
System.out.println();
System.out.println();
// Query the CurrentUserAtom
CurrentUserAtom cua = ss.getCurrentUserAtom();
System.out.println("Checking Current User Atom");
System.out.println(" Thinks the CurrentEditOffset is " + cua.getCurrentEditOffset());
// Query the CurrentUserAtom
CurrentUserAtom cua = ss.getCurrentUserAtom();
System.out.println("Checking Current User Atom");
System.out.println(" Thinks the CurrentEditOffset is " + cua.getCurrentEditOffset());
System.out.println();
}
}
System.out.println();
}
}
// Finds the record at a given position
public static org.apache.poi.hslf.record.Record findRecordAtPos(int pos) {
long type = LittleEndian.getUShort(fileContents, pos+2);
long rlen = LittleEndian.getUInt(fileContents, pos+4);
// Finds the record at a given position
public static org.apache.poi.hslf.record.Record findRecordAtPos(int pos) {
long type = LittleEndian.getUShort(fileContents, pos+2);
long rlen = LittleEndian.getUInt(fileContents, pos+4);
return Record.createRecordForType(type,fileContents,pos,(int)rlen+8);
}
}
}

View File

@ -23,13 +23,13 @@ package org.apache.poi.hslf.exceptions;
*/
public final class CorruptPowerPointFileException extends IllegalStateException
{
public CorruptPowerPointFileException(String s) {
super(s);
}
public CorruptPowerPointFileException(String s) {
super(s);
}
public CorruptPowerPointFileException(String s, Throwable t) {
super(s,t);
}
public CorruptPowerPointFileException(String s, Throwable t) {
super(s,t);
}
public CorruptPowerPointFileException(Throwable t) {
super(t);

View File

@ -25,13 +25,13 @@ import org.apache.poi.EncryptedDocumentException;
*/
public final class EncryptedPowerPointFileException extends EncryptedDocumentException
{
public EncryptedPowerPointFileException(String s) {
super(s);
}
public EncryptedPowerPointFileException(String s, Throwable t) {
public EncryptedPowerPointFileException(String s) {
super(s);
}
public EncryptedPowerPointFileException(String s, Throwable t) {
super(s, t);
}
}
public EncryptedPowerPointFileException(Throwable t) {
super(t);

View File

@ -23,11 +23,11 @@ package org.apache.poi.hslf.exceptions;
public final class HSLFException extends RuntimeException {
public HSLFException() {
super();
super();
}
public HSLFException(String message) {
super(message);
super(message);
}
public HSLFException(String message, Throwable cause) {

View File

@ -24,7 +24,7 @@ import org.apache.poi.OldFileFormatException;
* it's too old for us.
*/
public class OldPowerPointFormatException extends OldFileFormatException {
public OldPowerPointFormatException(String s) {
super(s);
}
public OldPowerPointFormatException(String s) {
super(s);
}
}

View File

@ -37,83 +37,83 @@ import org.apache.poi.util.GenericRecordUtil;
public abstract class BitMaskTextProp extends TextProp {
protected static final Logger LOG = LogManager.getLogger(BitMaskTextProp.class);
private final String[] subPropNames;
private final int[] subPropMasks;
private final boolean[] subPropMatches;
private final String[] subPropNames;
private final int[] subPropMasks;
private final boolean[] subPropMatches;
/** Fetch the list of the names of the sub properties */
public String[] getSubPropNames() { return subPropNames; }
/** Fetch the list of if the sub properties match or not */
public boolean[] getSubPropMatches() { return subPropMatches; }
/** Fetch the list of the names of the sub properties */
public String[] getSubPropNames() { return subPropNames; }
/** Fetch the list of if the sub properties match or not */
public boolean[] getSubPropMatches() { return subPropMatches; }
protected BitMaskTextProp(BitMaskTextProp other) {
super(other);
subPropNames = (other.subPropNames == null) ? null : other.subPropNames.clone();
subPropMasks = (other.subPropMasks == null) ? null : other.subPropMasks.clone();
protected BitMaskTextProp(BitMaskTextProp other) {
super(other);
subPropNames = (other.subPropNames == null) ? null : other.subPropNames.clone();
subPropMasks = (other.subPropMasks == null) ? null : other.subPropMasks.clone();
// The old clone implementation didn't carry over matches, but keep everything else as it was
// this is failing unit tests
// subPropMatches = (other.subPropMatches == null) ? null : new boolean[other.subPropMatches.length];
subPropMatches = (other.subPropMatches == null) ? null : other.subPropMatches.clone();
}
// The old clone implementation didn't carry over matches, but keep everything else as it was
// this is failing unit tests
// subPropMatches = (other.subPropMatches == null) ? null : new boolean[other.subPropMatches.length];
subPropMatches = (other.subPropMatches == null) ? null : other.subPropMatches.clone();
}
protected BitMaskTextProp(int sizeOfDataBlock, int maskInHeader, String overallName, String... subPropNames) {
super(sizeOfDataBlock,maskInHeader,overallName);
this.subPropNames = subPropNames;
subPropMasks = new int[subPropNames.length];
subPropMatches = new boolean[subPropNames.length];
protected BitMaskTextProp(int sizeOfDataBlock, int maskInHeader, String overallName, String... subPropNames) {
super(sizeOfDataBlock,maskInHeader,overallName);
this.subPropNames = subPropNames;
subPropMasks = new int[subPropNames.length];
subPropMatches = new boolean[subPropNames.length];
int LSB = Integer.lowestOneBit(maskInHeader);
int LSB = Integer.lowestOneBit(maskInHeader);
// Initialise the masks list
for(int i=0; i<subPropMasks.length; i++) {
subPropMasks[i] = (LSB << i);
}
}
// Initialise the masks list
for(int i=0; i<subPropMasks.length; i++) {
subPropMasks[i] = (LSB << i);
}
}
/**
* Calculate mask from the subPropMatches.
*/
@Override
public int getWriteMask() {
/*
* The dataValue can't be taken as a mask, as sometimes certain properties
* are explicitly set to false, i.e. the mask says the property is defined
* but in the actually nibble the property is set to false
*/
int mask = 0, i = 0;
for (int subMask : subPropMasks) {
if (subPropMatches[i++]) mask |= subMask;
}
return mask;
}
/**
* Calculate mask from the subPropMatches.
*/
@Override
public int getWriteMask() {
/*
* The dataValue can't be taken as a mask, as sometimes certain properties
* are explicitly set to false, i.e. the mask says the property is defined
* but in the actually nibble the property is set to false
*/
int mask = 0, i = 0;
for (int subMask : subPropMasks) {
if (subPropMatches[i++]) mask |= subMask;
}
return mask;
}
/**
* Sets the write mask, i.e. which defines the text properties to be considered
*
* @param writeMask the mask, bit values outside the property mask range will be ignored
*/
public void setWriteMask(int writeMask) {
/**
* Sets the write mask, i.e. which defines the text properties to be considered
*
* @param writeMask the mask, bit values outside the property mask range will be ignored
*/
public void setWriteMask(int writeMask) {
int i = 0;
for (int subMask : subPropMasks) {
subPropMatches[i++] = ((writeMask & subMask) != 0);
}
}
}
/**
* Return the text property value.
* Clears all bits of the value, which are marked as unset.
*
* @return the text property value.
*/
@Override
public int getValue() {
return maskValue(super.getValue());
}
/**
* Return the text property value.
* Clears all bits of the value, which are marked as unset.
*
* @return the text property value.
*/
@Override
public int getValue() {
return maskValue(super.getValue());
}
private int maskValue(int pVal) {
private int maskValue(int pVal) {
int val = pVal, i = 0;
for (int mask : subPropMasks) {
if (!subPropMatches[i++]) {
@ -121,58 +121,58 @@ public abstract class BitMaskTextProp extends TextProp {
}
}
return val;
}
}
/**
* Set the value of the text property, and recompute the sub
* properties based on it, i.e. all unset subvalues will be cleared.
* Use {@link #setSubValue(boolean, int)} to explicitly set subvalues to {@code false}.
*/
@Override
public void setValue(int val) {
super.setValue(val);
/**
* Set the value of the text property, and recompute the sub
* properties based on it, i.e. all unset subvalues will be cleared.
* Use {@link #setSubValue(boolean, int)} to explicitly set subvalues to {@code false}.
*/
@Override
public void setValue(int val) {
super.setValue(val);
// Figure out the values of the sub properties
int i = 0;
for(int mask : subPropMasks) {
subPropMatches[i++] = ((val & mask) != 0);
}
}
// Figure out the values of the sub properties
int i = 0;
for(int mask : subPropMasks) {
subPropMatches[i++] = ((val & mask) != 0);
}
}
/**
* Convenience method to set a value with mask, without splitting it into the subvalues
*/
public void setValueWithMask(int val, int writeMask) {
setWriteMask(writeMask);
super.setValue(maskValue(val));
if (val != super.getValue()) {
LOG.atWarn().log("Style properties of '{}' don't match mask - output will be sanitized", getName());
LOG.atDebug().log(() -> {
StringBuilder sb = new StringBuilder("The following style attributes of the '")
.append(getName()).append("' property will be ignored:\n");
int i=0;
for (int mask : subPropMasks) {
if (!subPropMatches[i] && (val & mask) != 0) {
sb.append(subPropNames[i]).append(",");
}
i++;
}
return new SimpleMessage(sb);
});
}
}
/**
* Convenience method to set a value with mask, without splitting it into the subvalues
*/
public void setValueWithMask(int val, int writeMask) {
setWriteMask(writeMask);
super.setValue(maskValue(val));
if (val != super.getValue()) {
LOG.atWarn().log("Style properties of '{}' don't match mask - output will be sanitized", getName());
LOG.atDebug().log(() -> {
StringBuilder sb = new StringBuilder("The following style attributes of the '")
.append(getName()).append("' property will be ignored:\n");
int i=0;
for (int mask : subPropMasks) {
if (!subPropMatches[i] && (val & mask) != 0) {
sb.append(subPropNames[i]).append(",");
}
i++;
}
return new SimpleMessage(sb);
});
}
}
/**
* Fetch the true/false status of the subproperty with the given index
*/
public boolean getSubValue(int idx) {
return subPropMatches[idx] && ((super.getValue() & subPropMasks[idx]) != 0);
}
/**
* Fetch the true/false status of the subproperty with the given index
*/
public boolean getSubValue(int idx) {
return subPropMatches[idx] && ((super.getValue() & subPropMasks[idx]) != 0);
}
/**
* Set the true/false status of the subproperty with the given index
*/
public void setSubValue(boolean value, int idx) {
/**
* Set the true/false status of the subproperty with the given index
*/
public void setSubValue(boolean value, int idx) {
subPropMatches[idx] = true;
int newVal = super.getValue();
if (value) {
@ -181,27 +181,27 @@ public abstract class BitMaskTextProp extends TextProp {
newVal &= ~subPropMasks[idx];
}
super.setValue(newVal);
}
/**
* @return an identical copy of this, i.e. also the subPropMatches are copied
*/
public BitMaskTextProp cloneAll(){
BitMaskTextProp bmtp = copy();
if (subPropMatches != null) {
System.arraycopy(subPropMatches, 0, bmtp.subPropMatches, 0, subPropMatches.length);
}
return bmtp;
}
@Override
public Map<String, Supplier<?>> getGenericProperties() {
return GenericRecordUtil.getGenericProperties(
"base", super::getGenericProperties,
"flags", getBitsAsString(this::getValue, subPropMasks, subPropNames)
);
}
/**
* @return an identical copy of this, i.e. also the subPropMatches are copied
*/
public BitMaskTextProp cloneAll(){
BitMaskTextProp bmtp = copy();
if (subPropMatches != null) {
System.arraycopy(subPropMatches, 0, bmtp.subPropMatches, 0, subPropMatches.length);
}
return bmtp;
}
@Override
public abstract BitMaskTextProp copy();
@Override
public Map<String, Supplier<?>> getGenericProperties() {
return GenericRecordUtil.getGenericProperties(
"base", super::getGenericProperties,
"flags", getBitsAsString(this::getValue, subPropMasks, subPropNames)
);
}
@Override
public abstract BitMaskTextProp copy();
}

View File

@ -22,44 +22,44 @@ package org.apache.poi.hslf.model.textproperties;
* handles bold/italic/underline etc.
*/
public class CharFlagsTextProp extends BitMaskTextProp {
public static final int BOLD_IDX = 0;
public static final int ITALIC_IDX = 1;
public static final int UNDERLINE_IDX = 2;
public static final int SHADOW_IDX = 4;
public static final int STRIKETHROUGH_IDX = 8;
public static final int RELIEF_IDX = 9;
public static final int RESET_NUMBERING_IDX = 10;
public static final int ENABLE_NUMBERING_1_IDX = 11;
public static final int ENABLE_NUMBERING_2_IDX = 12;
public static final int BOLD_IDX = 0;
public static final int ITALIC_IDX = 1;
public static final int UNDERLINE_IDX = 2;
public static final int SHADOW_IDX = 4;
public static final int STRIKETHROUGH_IDX = 8;
public static final int RELIEF_IDX = 9;
public static final int RESET_NUMBERING_IDX = 10;
public static final int ENABLE_NUMBERING_1_IDX = 11;
public static final int ENABLE_NUMBERING_2_IDX = 12;
public static final String NAME = "char_flags";
public CharFlagsTextProp() {
super(2, 0xffff, NAME,
"bold", // 0x0001 A bit that specifies whether the characters are bold.
"italic", // 0x0002 A bit that specifies whether the characters are italicized.
"underline", // 0x0004 A bit that specifies whether the characters are underlined.
"unused1", // 0x0008 Undefined and MUST be ignored.
"shadow", // 0x0010 A bit that specifies whether the characters have a shadow effect.
"fehint", // 0x0020 A bit that specifies whether characters originated from double-byte input.
"unused2", // 0x0040 Undefined and MUST be ignored.
"kumi", // 0x0080 A bit that specifies whether Kumimoji are used for vertical text.
"strikethrough", // 0x0100 aka "unused3" - sometimes contains the strikethrough flag
"emboss", // 0x0200 A bit that specifies whether the characters are embossed.
public CharFlagsTextProp() {
super(2, 0xffff, NAME,
"bold", // 0x0001 A bit that specifies whether the characters are bold.
"italic", // 0x0002 A bit that specifies whether the characters are italicized.
"underline", // 0x0004 A bit that specifies whether the characters are underlined.
"unused1", // 0x0008 Undefined and MUST be ignored.
"shadow", // 0x0010 A bit that specifies whether the characters have a shadow effect.
"fehint", // 0x0020 A bit that specifies whether characters originated from double-byte input.
"unused2", // 0x0040 Undefined and MUST be ignored.
"kumi", // 0x0080 A bit that specifies whether Kumimoji are used for vertical text.
"strikethrough", // 0x0100 aka "unused3" - sometimes contains the strikethrough flag
"emboss", // 0x0200 A bit that specifies whether the characters are embossed.
"pp9rt_1", // 0x0400 An unsigned integer that specifies the run grouping of additional text properties in StyleTextProp9Atom record.
"pp9rt_2", // 0x0800
"pp9rt_3", // 0x1000
"pp9rt_4", // 0x2000
"unused4_1", // 0x4000 Undefined and MUST be ignored.
"unused4_2" // 0x8000 Undefined and MUST be ignored.
);
}
);
}
public CharFlagsTextProp(CharFlagsTextProp other) {
super(other);
}
public CharFlagsTextProp(CharFlagsTextProp other) {
super(other);
}
@Override
public CharFlagsTextProp copy() {
return new CharFlagsTextProp(this);
}
@Override
public CharFlagsTextProp copy() {
return new CharFlagsTextProp(this);
}
}

View File

@ -27,45 +27,45 @@ import org.apache.poi.util.GenericRecordUtil;
* Definition for the font alignment property.
*/
public class FontAlignmentProp extends TextProp {
public static final String NAME = "fontAlign";
public static final int BASELINE = 0;
public static final int TOP = 1;
public static final int CENTER = 2;
public static final int BOTTOM = 3;
public static final String NAME = "fontAlign";
public static final int BASELINE = 0;
public static final int TOP = 1;
public static final int CENTER = 2;
public static final int BOTTOM = 3;
public FontAlignmentProp() {
super(2, 0x10000, NAME);
}
public FontAlignmentProp() {
super(2, 0x10000, NAME);
}
public FontAlignmentProp(FontAlignmentProp other) {
super(other);
}
public FontAlignmentProp(FontAlignmentProp other) {
super(other);
}
public FontAlign getFontAlign() {
switch (getValue()) {
default:
return FontAlign.AUTO;
case BASELINE:
return FontAlign.BASELINE;
case TOP:
return FontAlign.TOP;
case CENTER:
return FontAlign.CENTER;
case BOTTOM:
return FontAlign.BOTTOM;
}
}
public FontAlign getFontAlign() {
switch (getValue()) {
default:
return FontAlign.AUTO;
case BASELINE:
return FontAlign.BASELINE;
case TOP:
return FontAlign.TOP;
case CENTER:
return FontAlign.CENTER;
case BOTTOM:
return FontAlign.BOTTOM;
}
}
@Override
public Map<String, Supplier<?>> getGenericProperties() {
return GenericRecordUtil.getGenericProperties(
"base", super::getGenericProperties,
"fontAlign", this::getFontAlign
);
}
@Override
public Map<String, Supplier<?>> getGenericProperties() {
return GenericRecordUtil.getGenericProperties(
"base", super::getGenericProperties,
"fontAlign", this::getFontAlign
);
}
@Override
public FontAlignmentProp copy() {
return new FontAlignmentProp(this);
}
@Override
public FontAlignmentProp copy() {
return new FontAlignmentProp(this);
}
}

View File

@ -34,13 +34,13 @@ public class IndentProp implements GenericRecord {
private int charactersCovered;
private short indentLevel;
/**
* Generate the definition of a given text indent
*/
public IndentProp(int charactersCovered, short indentLevel) {
this.charactersCovered = charactersCovered;
this.indentLevel = indentLevel;
}
/**
* Generate the definition of a given text indent
*/
public IndentProp(int charactersCovered, short indentLevel) {
this.charactersCovered = charactersCovered;
this.indentLevel = indentLevel;
}
/** Fetch the number of characters this styling applies to */
public int getCharactersCovered() { return charactersCovered; }

View File

@ -21,28 +21,28 @@ package org.apache.poi.hslf.model.textproperties;
* Definition for the common paragraph text property bitset.
*/
public final class ParagraphFlagsTextProp extends BitMaskTextProp {
public static final int BULLET_IDX = 0;
public static final int BULLET_HARDFONT_IDX = 1;
public static final int BULLET_HARDCOLOR_IDX = 2;
public static final int BULLET_HARDSIZE_IDX = 4;
public static final int BULLET_IDX = 0;
public static final int BULLET_HARDFONT_IDX = 1;
public static final int BULLET_HARDCOLOR_IDX = 2;
public static final int BULLET_HARDSIZE_IDX = 4;
public static final String NAME = "paragraph_flags";
public ParagraphFlagsTextProp() {
super(2, 0xF, NAME,
"bullet",
public ParagraphFlagsTextProp() {
super(2, 0xF, NAME,
"bullet",
"bullet.hardfont",
"bullet.hardcolor",
"bullet.hardcolor",
"bullet.hardsize"
);
}
);
}
public ParagraphFlagsTextProp(ParagraphFlagsTextProp other) {
super(other);
}
public ParagraphFlagsTextProp(ParagraphFlagsTextProp other) {
super(other);
}
@Override
public ParagraphFlagsTextProp copy() {
return new ParagraphFlagsTextProp(this);
}
@Override
public ParagraphFlagsTextProp copy() {
return new ParagraphFlagsTextProp(this);
}
}

View File

@ -27,82 +27,82 @@ import org.apache.poi.util.GenericRecordUtil;
* Definition for the text alignment property.
*/
public class TextAlignmentProp extends TextProp {
/**
* For horizontal text, left aligned.
* For vertical text, top aligned.
*/
/**
* For horizontal text, left aligned.
* For vertical text, top aligned.
*/
public static final int LEFT = 0;
/**
* For horizontal text, centered.
* For vertical text, middle aligned.
*/
public static final int CENTER = 1;
public static final int CENTER = 1;
/**
* For horizontal text, right aligned.
* For vertical text, bottom aligned.
*/
public static final int RIGHT = 2;
/**
* For horizontal text, right aligned.
* For vertical text, bottom aligned.
*/
public static final int RIGHT = 2;
/**
* For horizontal text, flush left and right.
* For vertical text, flush top and bottom.
*/
public static final int JUSTIFY = 3;
/**
* For horizontal text, flush left and right.
* For vertical text, flush top and bottom.
*/
public static final int JUSTIFY = 3;
/**
* Distribute space between characters.
*/
public static final int DISTRIBUTED = 4;
/**
* Distribute space between characters.
*/
public static final int DISTRIBUTED = 4;
/**
* Thai distribution justification.
*/
public static final int THAIDISTRIBUTED = 5;
/**
* Thai distribution justification.
*/
public static final int THAIDISTRIBUTED = 5;
/**
* Kashida justify low.
*/
public static final int JUSTIFYLOW = 6;
/**
* Kashida justify low.
*/
public static final int JUSTIFYLOW = 6;
public TextAlignmentProp() {
super(2, 0x800, "alignment");
}
public TextAlignmentProp() {
super(2, 0x800, "alignment");
}
public TextAlignmentProp(TextAlignmentProp other) {
super(other);
}
public TextAlignmentProp(TextAlignmentProp other) {
super(other);
}
public TextAlign getTextAlign() {
switch (getValue()) {
default:
case LEFT:
return TextAlign.LEFT;
case CENTER:
return TextAlign.CENTER;
case RIGHT:
return TextAlign.RIGHT;
case JUSTIFY:
return TextAlign.JUSTIFY;
case DISTRIBUTED:
return TextAlign.DIST;
case THAIDISTRIBUTED:
return TextAlign.THAI_DIST;
}
}
public TextAlign getTextAlign() {
switch (getValue()) {
default:
case LEFT:
return TextAlign.LEFT;
case CENTER:
return TextAlign.CENTER;
case RIGHT:
return TextAlign.RIGHT;
case JUSTIFY:
return TextAlign.JUSTIFY;
case DISTRIBUTED:
return TextAlign.DIST;
case THAIDISTRIBUTED:
return TextAlign.THAI_DIST;
}
}
@Override
public Map<String, Supplier<?>> getGenericProperties() {
return GenericRecordUtil.getGenericProperties(
"base", super::getGenericProperties,
"textAlign", this::getTextAlign
);
}
@Override
public Map<String, Supplier<?>> getGenericProperties() {
return GenericRecordUtil.getGenericProperties(
"base", super::getGenericProperties,
"textAlign", this::getTextAlign
);
}
@Override
public TextAlignmentProp copy() {
return new TextAlignmentProp(this);
}
@Override
public TextAlignmentProp copy() {
return new TextAlignmentProp(this);
}
}

View File

@ -38,96 +38,96 @@ import org.apache.poi.util.LittleEndian;
*/
public class TextPFException9 implements GenericRecord {
private static final AutoNumberingScheme DEFAULT_AUTONUMBER_SCHEME = AutoNumberingScheme.arabicPeriod;
private static final Short DEFAULT_START_NUMBER = 1;
private static final AutoNumberingScheme DEFAULT_AUTONUMBER_SCHEME = AutoNumberingScheme.arabicPeriod;
private static final Short DEFAULT_START_NUMBER = 1;
//private final byte mask1;
//private final byte mask2;
private final byte mask3;
private final byte mask4;
private final Short bulletBlipRef;
private final Short fBulletHasAutoNumber;
private final AutoNumberingScheme autoNumberScheme;
private final Short autoNumberStartNumber;
private final int recordLength;
public TextPFException9(final byte[] source, final int startIndex) { // NOSONAR
//this.mask1 = source[startIndex];
//this.mask2 = source[startIndex + 1];
this.mask3 = source[startIndex + 2];
this.mask4 = source[startIndex + 3];
int length = 4;
int index = startIndex + 4;
if (0 == (mask3 & (byte)0x80 )) {
this.bulletBlipRef = null;
} else {
this.bulletBlipRef = LittleEndian.getShort(source, index);
index +=2;
length = 6;
}
if (0 == (mask4 & 2)) {
this.fBulletHasAutoNumber = null;
} else {
this.fBulletHasAutoNumber = LittleEndian.getShort(source, index);
index +=2;
length +=2;
}
if (0 == (mask4 & 1)) {
this.autoNumberScheme = null;
this.autoNumberStartNumber = null;
} else {
this.autoNumberScheme = AutoNumberingScheme.forNativeID(LittleEndian.getShort(source, index));
index +=2;
this.autoNumberStartNumber = LittleEndian.getShort(source, index);
index +=2;
length +=4;
}
this.recordLength = length;
}
public Short getBulletBlipRef() {
return bulletBlipRef;
}
public Short getfBulletHasAutoNumber() {
return fBulletHasAutoNumber;
}
public AutoNumberingScheme getAutoNumberScheme() {
if (autoNumberScheme != null) {
return autoNumberScheme;
}
return hasBulletAutoNumber() ? DEFAULT_AUTONUMBER_SCHEME : null;
}
//private final byte mask1;
//private final byte mask2;
private final byte mask3;
private final byte mask4;
private final Short bulletBlipRef;
private final Short fBulletHasAutoNumber;
private final AutoNumberingScheme autoNumberScheme;
private final Short autoNumberStartNumber;
private final int recordLength;
public TextPFException9(final byte[] source, final int startIndex) { // NOSONAR
//this.mask1 = source[startIndex];
//this.mask2 = source[startIndex + 1];
this.mask3 = source[startIndex + 2];
this.mask4 = source[startIndex + 3];
int length = 4;
int index = startIndex + 4;
if (0 == (mask3 & (byte)0x80 )) {
this.bulletBlipRef = null;
} else {
this.bulletBlipRef = LittleEndian.getShort(source, index);
index +=2;
length = 6;
}
if (0 == (mask4 & 2)) {
this.fBulletHasAutoNumber = null;
} else {
this.fBulletHasAutoNumber = LittleEndian.getShort(source, index);
index +=2;
length +=2;
}
if (0 == (mask4 & 1)) {
this.autoNumberScheme = null;
this.autoNumberStartNumber = null;
} else {
this.autoNumberScheme = AutoNumberingScheme.forNativeID(LittleEndian.getShort(source, index));
index +=2;
this.autoNumberStartNumber = LittleEndian.getShort(source, index);
index +=2;
length +=4;
}
this.recordLength = length;
}
public Short getBulletBlipRef() {
return bulletBlipRef;
}
public Short getfBulletHasAutoNumber() {
return fBulletHasAutoNumber;
}
public AutoNumberingScheme getAutoNumberScheme() {
if (autoNumberScheme != null) {
return autoNumberScheme;
}
return hasBulletAutoNumber() ? DEFAULT_AUTONUMBER_SCHEME : null;
}
public Short getAutoNumberStartNumber() {
if (autoNumberStartNumber != null) {
return autoNumberStartNumber;
}
return hasBulletAutoNumber() ? DEFAULT_START_NUMBER : null;
}
public Short getAutoNumberStartNumber() {
if (autoNumberStartNumber != null) {
return autoNumberStartNumber;
}
return hasBulletAutoNumber() ? DEFAULT_START_NUMBER : null;
}
private boolean hasBulletAutoNumber() {
final Short one = 1;
return one.equals(fBulletHasAutoNumber);
}
private boolean hasBulletAutoNumber() {
final Short one = 1;
return one.equals(fBulletHasAutoNumber);
}
public int getRecordLength() {
return recordLength;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Record length: ").append(this.recordLength).append(" bytes\n");
sb.append("bulletBlipRef: ").append(this.bulletBlipRef).append("\n");
sb.append("fBulletHasAutoNumber: ").append(this.fBulletHasAutoNumber).append("\n");
sb.append("autoNumberScheme: ").append(this.autoNumberScheme).append("\n");
sb.append("autoNumberStartNumber: ").append(this.autoNumberStartNumber).append("\n");
return sb.toString();
}
public int getRecordLength() {
return recordLength;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Record length: ").append(this.recordLength).append(" bytes\n");
sb.append("bulletBlipRef: ").append(this.bulletBlipRef).append("\n");
sb.append("fBulletHasAutoNumber: ").append(this.fBulletHasAutoNumber).append("\n");
sb.append("autoNumberScheme: ").append(this.autoNumberScheme).append("\n");
sb.append("autoNumberStartNumber: ").append(this.autoNumberStartNumber).append("\n");
return sb.toString();
}
@Override
public Map<String, Supplier<?>> getGenericProperties() {
return GenericRecordUtil.getGenericProperties(
"bulletBlipRef", this::getBulletBlipRef,
"bulletHasAutoNumber", this::hasBulletAutoNumber,
"autoNumberScheme", this::getAutoNumberScheme,
"autoNumberStartNumber", this::getAutoNumberStartNumber
);
}
@Override
public Map<String, Supplier<?>> getGenericProperties() {
return GenericRecordUtil.getGenericProperties(
"bulletBlipRef", this::getBulletBlipRef,
"bulletHasAutoNumber", this::hasBulletAutoNumber,
"autoNumberScheme", this::getAutoNumberScheme,
"autoNumberStartNumber", this::getAutoNumberStartNumber
);
}
}

View File

@ -38,79 +38,79 @@ import org.apache.poi.util.GenericRecordUtil;
* TextProps is stored in the different record classes
*/
public class TextProp implements Duplicatable, GenericRecord {
private int sizeOfDataBlock; // Number of bytes the data part uses
private String propName;
private int dataValue;
private int maskInHeader;
private int sizeOfDataBlock; // Number of bytes the data part uses
private String propName;
private int dataValue;
private int maskInHeader;
/**
* Generate the definition of a given type of text property.
*/
public TextProp(int sizeOfDataBlock, int maskInHeader, String propName) {
this.sizeOfDataBlock = sizeOfDataBlock;
this.maskInHeader = maskInHeader;
this.propName = propName;
this.dataValue = 0;
}
/**
* Clones the property
*/
public TextProp(TextProp other) {
this.sizeOfDataBlock = other.sizeOfDataBlock;
this.maskInHeader = other.maskInHeader;
this.propName = other.propName;
this.dataValue = other.dataValue;
}
/**
* Name of the text property
*/
public String getName() { return propName; }
/**
* Size of the data section of the text property (2 or 4 bytes)
*/
public int getSize() { return sizeOfDataBlock; }
/**
* Mask in the paragraph or character "contains" header field
* that indicates that this text property is present.
*/
public int getMask() { return maskInHeader; }
/**
* Get the mask that's used at write time. Only differs from
* the result of getMask() for the mask based properties
*/
public int getWriteMask() { return getMask(); }
/**
* Fetch the value of the text property (meaning is specific to
* each different kind of text property)
*/
public int getValue() { return dataValue; }
/**
* Set the value of the text property.
*/
public void setValue(int val) { dataValue = val; }
/**
* Clone, eg when you want to actually make use of one of these.
*/
@Override
public TextProp copy(){
// subclasses need to override copy()
assert(TextProp.class.equals(this.getClass()));
return new TextProp(this);
}
@Override
public int hashCode() {
return Objects.hash(dataValue, maskInHeader, propName, sizeOfDataBlock);
/**
* Generate the definition of a given type of text property.
*/
public TextProp(int sizeOfDataBlock, int maskInHeader, String propName) {
this.sizeOfDataBlock = sizeOfDataBlock;
this.maskInHeader = maskInHeader;
this.propName = propName;
this.dataValue = 0;
}
@Override
/**
* Clones the property
*/
public TextProp(TextProp other) {
this.sizeOfDataBlock = other.sizeOfDataBlock;
this.maskInHeader = other.maskInHeader;
this.propName = other.propName;
this.dataValue = other.dataValue;
}
/**
* Name of the text property
*/
public String getName() { return propName; }
/**
* Size of the data section of the text property (2 or 4 bytes)
*/
public int getSize() { return sizeOfDataBlock; }
/**
* Mask in the paragraph or character "contains" header field
* that indicates that this text property is present.
*/
public int getMask() { return maskInHeader; }
/**
* Get the mask that's used at write time. Only differs from
* the result of getMask() for the mask based properties
*/
public int getWriteMask() { return getMask(); }
/**
* Fetch the value of the text property (meaning is specific to
* each different kind of text property)
*/
public int getValue() { return dataValue; }
/**
* Set the value of the text property.
*/
public void setValue(int val) { dataValue = val; }
/**
* Clone, eg when you want to actually make use of one of these.
*/
@Override
public TextProp copy(){
// subclasses need to override copy()
assert(TextProp.class.equals(this.getClass()));
return new TextProp(this);
}
@Override
public int hashCode() {
return Objects.hash(dataValue, maskInHeader, propName, sizeOfDataBlock);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
@ -152,13 +152,13 @@ public class TextProp implements Duplicatable, GenericRecord {
return String.format(Locale.ROOT, "%s = %d (%0#"+len+"X mask / %d bytes)", getName(), getValue(), getMask(), getSize());
}
@Override
public Map<String, Supplier<?>> getGenericProperties() {
return GenericRecordUtil.getGenericProperties(
"sizeOfDataBlock", this::getSize,
"propName", this::getName,
"dataValue", this::getValue,
"maskInHeader", this::getMask
);
}
@Override
public Map<String, Supplier<?>> getGenericProperties() {
return GenericRecordUtil.getGenericProperties(
"sizeOfDataBlock", this::getSize,
"propName", this::getName,
"dataValue", this::getValue,
"maskInHeader", this::getMask
);
}
}

View File

@ -100,7 +100,7 @@ public class TextPropCollection implements GenericRecord, Duplicatable {
// indentLevel is only valid for paragraph collection
// if it's set to -1, it must be omitted - see 2.9.36 TextMasterStyleLevel
private short indentLevel;
private final Map<String,TextProp> textProps = new HashMap<>();
private final Map<String,TextProp> textProps = new HashMap<>();
private int maskSpecial;
private final TextPropType textPropType;
@ -126,51 +126,51 @@ public class TextPropCollection implements GenericRecord, Duplicatable {
return maskSpecial;
}
/** Fetch the number of characters this styling applies to */
public int getCharactersCovered() {
return charactersCovered;
/** Fetch the number of characters this styling applies to */
public int getCharactersCovered() {
return charactersCovered;
}
/** Fetch the TextProps that define this styling in the record order */
public List<TextProp> getTextPropList() {
List<TextProp> orderedList = new ArrayList<>();
/** Fetch the TextProps that define this styling in the record order */
public List<TextProp> getTextPropList() {
List<TextProp> orderedList = new ArrayList<>();
for (TextProp potProp : getPotentialProperties()) {
TextProp textProp = textProps.get(potProp.getName());
if (textProp != null) {
orderedList.add(textProp);
}
}
return orderedList;
return orderedList;
}
/** Fetch the TextProp with this name, or null if it isn't present */
@SuppressWarnings("unchecked")
/** Fetch the TextProp with this name, or null if it isn't present */
@SuppressWarnings("unchecked")
public final <T extends TextProp> T findByName(String textPropName) {
return (T)textProps.get(textPropName);
}
return (T)textProps.get(textPropName);
}
@SuppressWarnings("unchecked")
public final <T extends TextProp> T removeByName(String name) {
return (T)textProps.remove(name);
}
public final <T extends TextProp> T removeByName(String name) {
return (T)textProps.remove(name);
}
public final TextPropType getTextPropType() {
return textPropType;
}
public final TextPropType getTextPropType() {
return textPropType;
}
private TextProp[] getPotentialProperties() {
return (textPropType == TextPropType.paragraph) ? paragraphTextPropTypes : characterTextPropTypes;
}
private TextProp[] getPotentialProperties() {
return (textPropType == TextPropType.paragraph) ? paragraphTextPropTypes : characterTextPropTypes;
}
/**
* Checks the paragraph or character properties for the given property name.
* Throws a HSLFException, if the name doesn't belong into this set of properties
*
* @param name the property name
* @return if found, the property template to copy from
*/
/**
* Checks the paragraph or character properties for the given property name.
* Throws a HSLFException, if the name doesn't belong into this set of properties
*
* @param name the property name
* @return if found, the property template to copy from
*/
@SuppressWarnings("unchecked")
private <T extends TextProp> T validatePropName(final String name) {
private <T extends TextProp> T validatePropName(final String name) {
for (TextProp tp : getPotentialProperties()) {
if (tp.getName().equals(name)) {
return (T)tp;
@ -180,7 +180,7 @@ public class TextPropCollection implements GenericRecord, Duplicatable {
"No TextProp with name " + name + " is defined to add from. " +
"Character and paragraphs have their own properties/names.";
throw new HSLFException(errStr);
}
}
/** Add the TextProp with this name to the list */
@SuppressWarnings("unchecked")
@ -195,37 +195,37 @@ public class TextPropCollection implements GenericRecord, Duplicatable {
return textProp;
}
/**
* Add the property at the correct position. Replaces an existing property with the same name.
*
* @param textProp the property to be added
*/
public final void addProp(TextProp textProp) {
if (textProp == null) {
throw new HSLFException("TextProp must not be null");
}
/**
* Add the property at the correct position. Replaces an existing property with the same name.
*
* @param textProp the property to be added
*/
public final void addProp(TextProp textProp) {
if (textProp == null) {
throw new HSLFException("TextProp must not be null");
}
String propName = textProp.getName();
validatePropName(propName);
String propName = textProp.getName();
validatePropName(propName);
textProps.put(propName, textProp);
}
textProps.put(propName, textProp);
}
/**
* For an existing set of text properties, build the list of
* properties coded for in a given run of properties.
* @return the number of bytes that were used encoding the properties list
*/
public int buildTextPropList(int containsField, byte[] data, int dataOffset) {
int bytesPassed = 0;
/**
* For an existing set of text properties, build the list of
* properties coded for in a given run of properties.
* @return the number of bytes that were used encoding the properties list
*/
public int buildTextPropList(int containsField, byte[] data, int dataOffset) {
int bytesPassed = 0;
// For each possible entry, see if we match the mask
// If we do, decode that, save it, and shuffle on
for(TextProp tp : getPotentialProperties()) {
// Check there's still data left to read
// For each possible entry, see if we match the mask
// If we do, decode that, save it, and shuffle on
for(TextProp tp : getPotentialProperties()) {
// Check there's still data left to read
// Check if this property is found in the mask
if((containsField & tp.getMask()) != 0) {
// Check if this property is found in the mask
if((containsField & tp.getMask()) != 0) {
if(dataOffset+bytesPassed >= data.length) {
// Out of data, can't be any more properties to go
// remember the mask and return
@ -233,82 +233,82 @@ public class TextPropCollection implements GenericRecord, Duplicatable {
return bytesPassed;
}
// Bingo, data contains this property
TextProp prop = tp.copy();
int val = 0;
if (prop instanceof HSLFTabStopPropCollection) {
// Bingo, data contains this property
TextProp prop = tp.copy();
int val = 0;
if (prop instanceof HSLFTabStopPropCollection) {
((HSLFTabStopPropCollection)prop).parseProperty(data, dataOffset+bytesPassed);
} else if (prop.getSize() == 2) {
val = LittleEndian.getShort(data,dataOffset+bytesPassed);
} else if(prop.getSize() == 4) {
val = LittleEndian.getInt(data,dataOffset+bytesPassed);
} else if (prop.getSize() == 0) {
val = LittleEndian.getShort(data,dataOffset+bytesPassed);
} else if(prop.getSize() == 4) {
val = LittleEndian.getInt(data,dataOffset+bytesPassed);
} else if (prop.getSize() == 0) {
//remember "special" bits.
maskSpecial |= tp.getMask();
continue;
}
if (prop instanceof BitMaskTextProp) {
((BitMaskTextProp)prop).setValueWithMask(val, containsField);
} else if (!(prop instanceof HSLFTabStopPropCollection)) {
prop.setValue(val);
}
bytesPassed += prop.getSize();
addProp(prop);
}
}
if (prop instanceof BitMaskTextProp) {
((BitMaskTextProp)prop).setValueWithMask(val, containsField);
} else if (!(prop instanceof HSLFTabStopPropCollection)) {
prop.setValue(val);
}
bytesPassed += prop.getSize();
addProp(prop);
}
}
// Return how many bytes were used
return bytesPassed;
}
// Return how many bytes were used
return bytesPassed;
}
/**
* Clones the given text properties
*/
@Override
public TextPropCollection copy() {
public TextPropCollection copy() {
return new TextPropCollection(this);
}
}
/**
* Update the size of the text that this set of properties
* applies to
*/
public void updateTextSize(int textSize) {
charactersCovered = textSize;
}
/**
* Update the size of the text that this set of properties
* applies to
*/
public void updateTextSize(int textSize) {
charactersCovered = textSize;
}
/**
/**
* Writes out to disk the header, and then all the properties
*/
public void writeOut(OutputStream o) throws IOException {
writeOut(o, false);
}
/**
* Writes out to disk the header, and then all the properties
*/
public void writeOut(OutputStream o, boolean isMasterStyle) throws IOException {
if (!isMasterStyle) {
// First goes the number of characters we affect
// MasterStyles don't have this field
/**
* Writes out to disk the header, and then all the properties
*/
public void writeOut(OutputStream o, boolean isMasterStyle) throws IOException {
if (!isMasterStyle) {
// First goes the number of characters we affect
// MasterStyles don't have this field
Record.writeLittleEndian(charactersCovered,o);
}
}
// Then we have the indentLevel field if it's a paragraph collection
if (textPropType == TextPropType.paragraph && indentLevel > -1) {
// Then we have the indentLevel field if it's a paragraph collection
if (textPropType == TextPropType.paragraph && indentLevel > -1) {
Record.writeLittleEndian(indentLevel, o);
}
}
// Then the mask field
int mask = maskSpecial;
for (TextProp textProp : textProps.values()) {
// Then the mask field
int mask = maskSpecial;
for (TextProp textProp : textProps.values()) {
mask |= textProp.getWriteMask();
}
Record.writeLittleEndian(mask,o);
// Then the contents of all the properties
for (TextProp textProp : getTextPropList()) {
// Then the contents of all the properties
for (TextProp textProp : getTextPropList()) {
int val = textProp.getValue();
if (textProp instanceof BitMaskTextProp && textProp.getWriteMask() == 0) {
// don't add empty properties, as they can't be recognized while reading
@ -320,8 +320,8 @@ public class TextPropCollection implements GenericRecord, Duplicatable {
} else if (textProp instanceof HSLFTabStopPropCollection) {
((HSLFTabStopPropCollection)textProp).writeProperty(o);
}
}
}
}
}
public short getIndentLevel(){
return indentLevel;
@ -335,7 +335,7 @@ public class TextPropCollection implements GenericRecord, Duplicatable {
}
public int hashCode() {
return Objects.hash(charactersCovered,maskSpecial,indentLevel,textProps);
return Objects.hash(charactersCovered,maskSpecial,indentLevel,textProps);
}
/**
* compares most properties apart of the covered characters length

View File

@ -29,64 +29,64 @@ import static org.apache.logging.log4j.util.Unbox.box;
* A container record that specifies information about animation information for a shape.
*/
public final class AnimationInfo extends RecordContainer {
private byte[] _header;
private byte[] _header;
// Links to our more interesting children
private AnimationInfoAtom animationAtom;
// Links to our more interesting children
private AnimationInfoAtom animationAtom;
/**
* Set things up, and find our more interesting children
*/
protected AnimationInfo(byte[] source, int start, int len) {
// Grab the header
_header = Arrays.copyOfRange(source, start, start+8);
/**
* Set things up, and find our more interesting children
*/
protected AnimationInfo(byte[] source, int start, int len) {
// Grab the header
_header = Arrays.copyOfRange(source, start, start+8);
// Find our children
_children = Record.findChildRecords(source,start+8,len-8);
findInterestingChildren();
}
// Find our children
_children = Record.findChildRecords(source,start+8,len-8);
findInterestingChildren();
}
/**
* Go through our child records, picking out the ones that are
* interesting, and saving those for use by the easy helper
* methods.
*/
private void findInterestingChildren() {
/**
* Go through our child records, picking out the ones that are
* interesting, and saving those for use by the easy helper
* methods.
*/
private void findInterestingChildren() {
// First child should be the ExMediaAtom
final Record child = _children[0];
if(child instanceof AnimationInfoAtom) {
animationAtom = (AnimationInfoAtom) child;
} else {
LOG.atError().log("First child record wasn't a AnimationInfoAtom, was of type {}", box(child.getRecordType()));
}
}
// First child should be the ExMediaAtom
final Record child = _children[0];
if(child instanceof AnimationInfoAtom) {
animationAtom = (AnimationInfoAtom) child;
} else {
LOG.atError().log("First child record wasn't a AnimationInfoAtom, was of type {}", box(child.getRecordType()));
}
}
/**
* Create a new AnimationInfo, with blank fields
*/
public AnimationInfo() {
/**
* Create a new AnimationInfo, with blank fields
*/
public AnimationInfo() {
// Setup our header block
_header = new byte[8];
_header[0] = 0x0f; // We are a container record
LittleEndian.putShort(_header, 2, (short)getRecordType());
_header = new byte[8];
_header[0] = 0x0f; // We are a container record
LittleEndian.putShort(_header, 2, (short)getRecordType());
_children = new org.apache.poi.hslf.record.Record[1];
_children[0] = animationAtom = new AnimationInfoAtom();
}
_children[0] = animationAtom = new AnimationInfoAtom();
}
/**
* We are of type 4103
*/
public long getRecordType() { return RecordTypes.AnimationInfo.typeID; }
/**
* We are of type 4103
*/
public long getRecordType() { return RecordTypes.AnimationInfo.typeID; }
/**
* Write the contents of the record back, so it can be written
* to disk
*/
public void writeOut(OutputStream out) throws IOException {
writeOut(_header[0],_header[1],getRecordType(),_children,out);
}
/**
* Write the contents of the record back, so it can be written
* to disk
*/
public void writeOut(OutputStream out) throws IOException {
writeOut(_header[0],_header[1],getRecordType(),_children,out);
}
/**
* Returns the AnimationInfo

View File

@ -35,32 +35,32 @@ import org.apache.poi.util.LittleEndian;
public final class BinaryTagDataBlob extends PositionDependentRecordContainer
{
private byte[] _header;
private long _type;
private byte[] _header;
private long _type;
/**
* Create a new holder for a boring record with children, but with
* position dependent characteristics
*/
protected BinaryTagDataBlob(byte[] source, int start, int len) {
// Just grab the header, not the whole contents
_header = Arrays.copyOfRange(source, start, start+8);
_type = LittleEndian.getUShort(_header,2);
/**
* Create a new holder for a boring record with children, but with
* position dependent characteristics
*/
protected BinaryTagDataBlob(byte[] source, int start, int len) {
// Just grab the header, not the whole contents
_header = Arrays.copyOfRange(source, start, start+8);
_type = LittleEndian.getUShort(_header,2);
// Find our children
_children = Record.findChildRecords(source,start+8,len-8);
}
// Find our children
_children = Record.findChildRecords(source,start+8,len-8);
}
/**
* Return the value we were given at creation
*/
public long getRecordType() { return _type; }
/**
* Return the value we were given at creation
*/
public long getRecordType() { return _type; }
/**
* Write the contents of the record back, so it can be written
* to disk
*/
public void writeOut(OutputStream out) throws IOException {
writeOut(_header[0],_header[1],_type,_children,out);
}
/**
* Write the contents of the record back, so it can be written
* to disk
*/
public void writeOut(OutputStream out) throws IOException {
writeOut(_header[0],_header[1],_type,_children,out);
}
}

View File

@ -36,88 +36,88 @@ import org.apache.poi.util.StringUtil;
public final class CString extends RecordAtom {
//arbitrarily selected; may need to increase
private static final int MAX_RECORD_LENGTH = 1_000_000;
//arbitrarily selected; may need to increase
private static final int MAX_RECORD_LENGTH = 1_000_000;
private byte[] _header;
private byte[] _header;
/** The bytes that make up the text */
private byte[] _text;
/** The bytes that make up the text */
private byte[] _text;
/** Grabs the text. Never <code>null</code> */
public String getText() {
return StringUtil.getFromUnicodeLE(_text);
}
/** Grabs the text. Never <code>null</code> */
public String getText() {
return StringUtil.getFromUnicodeLE(_text);
}
/** Updates the text in the Atom. */
public void setText(String text) {
// Convert to little endian unicode
_text = new byte[text.length()*2];
StringUtil.putUnicodeLE(text,_text,0);
/** Updates the text in the Atom. */
public void setText(String text) {
// Convert to little endian unicode
_text = new byte[text.length()*2];
StringUtil.putUnicodeLE(text,_text,0);
// Update the size (header bytes 5-8)
LittleEndian.putInt(_header,4,_text.length);
}
// Update the size (header bytes 5-8)
LittleEndian.putInt(_header,4,_text.length);
}
/**
* Grabs the count, from the first two bytes of the header.
* The meaning of the count is specific to the type of the parent record
*/
public int getOptions() {
return LittleEndian.getShort(_header);
}
/**
* Grabs the count, from the first two bytes of the header.
* The meaning of the count is specific to the type of the parent record
*/
public int getOptions() {
return LittleEndian.getShort(_header);
}
/**
* Sets the count
* The meaning of the count is specific to the type of the parent record
*/
public void setOptions(int count) {
LittleEndian.putShort(_header, 0, (short)count);
}
/**
* Sets the count
* The meaning of the count is specific to the type of the parent record
*/
public void setOptions(int count) {
LittleEndian.putShort(_header, 0, (short)count);
}
/* *************** record code follows ********************** */
/* *************** record code follows ********************** */
/**
* For the CStrubg Atom
*/
protected CString(byte[] source, int start, int len) {
// Sanity Checking
if(len < 8) { len = 8; }
/**
* For the CStrubg Atom
*/
protected CString(byte[] source, int start, int len) {
// Sanity Checking
if(len < 8) { len = 8; }
// Get the header
_header = Arrays.copyOfRange(source, start, start+8);
// Get the header
_header = Arrays.copyOfRange(source, start, start+8);
// Grab the text
_text = IOUtils.safelyClone(source,start+8, len-8, MAX_RECORD_LENGTH);
}
/**
* Create an empty CString
*/
public CString() {
// 0 length header
_header = new byte[] { 0, 0, 0xBA-256, 0x0f, 0, 0, 0, 0 };
// Empty text
_text = new byte[0];
}
// Grab the text
_text = IOUtils.safelyClone(source,start+8, len-8, MAX_RECORD_LENGTH);
}
/**
* Create an empty CString
*/
public CString() {
// 0 length header
_header = new byte[] { 0, 0, 0xBA-256, 0x0f, 0, 0, 0, 0 };
// Empty text
_text = new byte[0];
}
/**
* We are of type 4026
*/
public long getRecordType() {
return RecordTypes.CString.typeID;
}
/**
* We are of type 4026
*/
public long getRecordType() {
return RecordTypes.CString.typeID;
}
/**
* Write the contents of the record back, so it can be written
* to disk
*/
public void writeOut(OutputStream out) throws IOException {
// Header - size or type unchanged
out.write(_header);
/**
* Write the contents of the record back, so it can be written
* to disk
*/
public void writeOut(OutputStream out) throws IOException {
// Header - size or type unchanged
out.write(_header);
// Write out our text
out.write(_text);
}
// Write out our text
out.write(_text);
}
/**
* Gets a string representation of this object, primarily for debugging.
@ -127,8 +127,8 @@ public final class CString extends RecordAtom {
return getText();
}
@Override
public Map<String, Supplier<?>> getGenericProperties() {
return GenericRecordUtil.getGenericProperties("text", this::getText);
}
@Override
public Map<String, Supplier<?>> getGenericProperties() {
return GenericRecordUtil.getGenericProperties("text", this::getText);
}
}

View File

@ -36,200 +36,200 @@ import org.apache.poi.util.LittleEndian;
* defines the colours to be used
*/
public final class ColorSchemeAtom extends RecordAtom {
private static final long _type = 2032L;
private static final long _type = 2032L;
private final byte[] _header;
private int backgroundColourRGB;
private int textAndLinesColourRGB;
private int shadowsColourRGB;
private int titleTextColourRGB;
private int fillsColourRGB;
private int accentColourRGB;
private int accentAndHyperlinkColourRGB;
private int accentAndFollowingHyperlinkColourRGB;
private final byte[] _header;
private int backgroundColourRGB;
private int textAndLinesColourRGB;
private int shadowsColourRGB;
private int titleTextColourRGB;
private int fillsColourRGB;
private int accentColourRGB;
private int accentAndHyperlinkColourRGB;
private int accentAndFollowingHyperlinkColourRGB;
/** Fetch the RGB value for Background Colour */
public int getBackgroundColourRGB() { return backgroundColourRGB; }
/** Set the RGB value for Background Colour */
public void setBackgroundColourRGB(int rgb) { backgroundColourRGB = rgb; }
/** Fetch the RGB value for Background Colour */
public int getBackgroundColourRGB() { return backgroundColourRGB; }
/** Set the RGB value for Background Colour */
public void setBackgroundColourRGB(int rgb) { backgroundColourRGB = rgb; }
/** Fetch the RGB value for Text And Lines Colour */
public int getTextAndLinesColourRGB() { return textAndLinesColourRGB; }
/** Set the RGB value for Text And Lines Colour */
public void setTextAndLinesColourRGB(int rgb) { textAndLinesColourRGB = rgb; }
/** Fetch the RGB value for Text And Lines Colour */
public int getTextAndLinesColourRGB() { return textAndLinesColourRGB; }
/** Set the RGB value for Text And Lines Colour */
public void setTextAndLinesColourRGB(int rgb) { textAndLinesColourRGB = rgb; }
/** Fetch the RGB value for Shadows Colour */
public int getShadowsColourRGB() { return shadowsColourRGB; }
/** Set the RGB value for Shadows Colour */
public void setShadowsColourRGB(int rgb) { shadowsColourRGB = rgb; }
/** Fetch the RGB value for Shadows Colour */
public int getShadowsColourRGB() { return shadowsColourRGB; }
/** Set the RGB value for Shadows Colour */
public void setShadowsColourRGB(int rgb) { shadowsColourRGB = rgb; }
/** Fetch the RGB value for Title Text Colour */
public int getTitleTextColourRGB() { return titleTextColourRGB; }
/** Set the RGB value for Title Text Colour */
public void setTitleTextColourRGB(int rgb) { titleTextColourRGB = rgb; }
/** Fetch the RGB value for Title Text Colour */
public int getTitleTextColourRGB() { return titleTextColourRGB; }
/** Set the RGB value for Title Text Colour */
public void setTitleTextColourRGB(int rgb) { titleTextColourRGB = rgb; }
/** Fetch the RGB value for Fills Colour */
public int getFillsColourRGB() { return fillsColourRGB; }
/** Set the RGB value for Fills Colour */
public void setFillsColourRGB(int rgb) { fillsColourRGB = rgb; }
/** Fetch the RGB value for Fills Colour */
public int getFillsColourRGB() { return fillsColourRGB; }
/** Set the RGB value for Fills Colour */
public void setFillsColourRGB(int rgb) { fillsColourRGB = rgb; }
/** Fetch the RGB value for Accent Colour */
public int getAccentColourRGB() { return accentColourRGB; }
/** Set the RGB value for Accent Colour */
public void setAccentColourRGB(int rgb) { accentColourRGB = rgb; }
/** Fetch the RGB value for Accent Colour */
public int getAccentColourRGB() { return accentColourRGB; }
/** Set the RGB value for Accent Colour */
public void setAccentColourRGB(int rgb) { accentColourRGB = rgb; }
/** Fetch the RGB value for Accent And Hyperlink Colour */
public int getAccentAndHyperlinkColourRGB()
{ return accentAndHyperlinkColourRGB; }
/** Set the RGB value for Accent And Hyperlink Colour */
public void setAccentAndHyperlinkColourRGB(int rgb)
{ accentAndHyperlinkColourRGB = rgb; }
/** Fetch the RGB value for Accent And Hyperlink Colour */
public int getAccentAndHyperlinkColourRGB()
{ return accentAndHyperlinkColourRGB; }
/** Set the RGB value for Accent And Hyperlink Colour */
public void setAccentAndHyperlinkColourRGB(int rgb)
{ accentAndHyperlinkColourRGB = rgb; }
/** Fetch the RGB value for Accent And Following Hyperlink Colour */
public int getAccentAndFollowingHyperlinkColourRGB()
{ return accentAndFollowingHyperlinkColourRGB; }
/** Set the RGB value for Accent And Following Hyperlink Colour */
public void setAccentAndFollowingHyperlinkColourRGB(int rgb)
{ accentAndFollowingHyperlinkColourRGB = rgb; }
/** Fetch the RGB value for Accent And Following Hyperlink Colour */
public int getAccentAndFollowingHyperlinkColourRGB()
{ return accentAndFollowingHyperlinkColourRGB; }
/** Set the RGB value for Accent And Following Hyperlink Colour */
public void setAccentAndFollowingHyperlinkColourRGB(int rgb)
{ accentAndFollowingHyperlinkColourRGB = rgb; }
/* *************** record code follows ********************** */
/* *************** record code follows ********************** */
/**
* For the Colour Scheme (ColorSchem) Atom
*/
protected ColorSchemeAtom(byte[] source, int start, int len) {
// Sanity Checking - we're always 40 bytes long
if (len < 40) {
if(source.length - start < 40) {
throw new HSLFException("Not enough data to form a ColorSchemeAtom (always 40 bytes long) - found " + (source.length - start));
}
}
/**
* For the Colour Scheme (ColorSchem) Atom
*/
protected ColorSchemeAtom(byte[] source, int start, int len) {
// Sanity Checking - we're always 40 bytes long
if (len < 40) {
if(source.length - start < 40) {
throw new HSLFException("Not enough data to form a ColorSchemeAtom (always 40 bytes long) - found " + (source.length - start));
}
}
// Get the header
_header = Arrays.copyOfRange(source, start, start+8);
// Get the header
_header = Arrays.copyOfRange(source, start, start+8);
// Grab the rgb values
backgroundColourRGB = LittleEndian.getInt(source, start+8);
textAndLinesColourRGB = LittleEndian.getInt(source,start+8+4);
shadowsColourRGB = LittleEndian.getInt(source,start+8+8);
titleTextColourRGB = LittleEndian.getInt(source,start+8+12);
fillsColourRGB = LittleEndian.getInt(source,start+8+16);
accentColourRGB = LittleEndian.getInt(source,start+8+20);
accentAndHyperlinkColourRGB = LittleEndian.getInt(source,start+8+24);
accentAndFollowingHyperlinkColourRGB = LittleEndian.getInt(source,start+8+28);
}
// Grab the rgb values
backgroundColourRGB = LittleEndian.getInt(source, start+8);
textAndLinesColourRGB = LittleEndian.getInt(source,start+8+4);
shadowsColourRGB = LittleEndian.getInt(source,start+8+8);
titleTextColourRGB = LittleEndian.getInt(source,start+8+12);
fillsColourRGB = LittleEndian.getInt(source,start+8+16);
accentColourRGB = LittleEndian.getInt(source,start+8+20);
accentAndHyperlinkColourRGB = LittleEndian.getInt(source,start+8+24);
accentAndFollowingHyperlinkColourRGB = LittleEndian.getInt(source,start+8+28);
}
/**
* Create a new ColorSchemeAtom, to go with a new Slide
*/
public ColorSchemeAtom(){
_header = new byte[8];
LittleEndian.putUShort(_header, 0, 16);
LittleEndian.putUShort(_header, 2, (int)_type);
LittleEndian.putInt(_header, 4, 32);
/**
* Create a new ColorSchemeAtom, to go with a new Slide
*/
public ColorSchemeAtom(){
_header = new byte[8];
LittleEndian.putUShort(_header, 0, 16);
LittleEndian.putUShort(_header, 2, (int)_type);
LittleEndian.putInt(_header, 4, 32);
// Setup the default rgb values
backgroundColourRGB = 16777215;
textAndLinesColourRGB = 0;
shadowsColourRGB = 8421504;
titleTextColourRGB = 0;
fillsColourRGB = 10079232;
accentColourRGB = 13382451;
accentAndHyperlinkColourRGB = 16764108;
accentAndFollowingHyperlinkColourRGB = 11711154;
}
// Setup the default rgb values
backgroundColourRGB = 16777215;
textAndLinesColourRGB = 0;
shadowsColourRGB = 8421504;
titleTextColourRGB = 0;
fillsColourRGB = 10079232;
accentColourRGB = 13382451;
accentAndHyperlinkColourRGB = 16764108;
accentAndFollowingHyperlinkColourRGB = 11711154;
}
/**
* We are of type 3999
*/
@Override
/**
* We are of type 3999
*/
@Override
public long getRecordType() { return _type; }
/**
* Convert from an integer RGB value to individual R, G, B 0-255 values
*/
public static byte[] splitRGB(int rgb) {
byte[] ret = new byte[3];
/**
* Convert from an integer RGB value to individual R, G, B 0-255 values
*/
public static byte[] splitRGB(int rgb) {
byte[] ret = new byte[3];
// Serialise to bytes, then grab the right ones out
UnsynchronizedByteArrayOutputStream baos = new UnsynchronizedByteArrayOutputStream();
try {
writeLittleEndian(rgb,baos);
} catch(IOException ie) {
// Should never happen
throw new HSLFException(ie);
}
byte[] b = baos.toByteArray();
System.arraycopy(b,0,ret,0,3);
// Serialise to bytes, then grab the right ones out
UnsynchronizedByteArrayOutputStream baos = new UnsynchronizedByteArrayOutputStream();
try {
writeLittleEndian(rgb,baos);
} catch(IOException ie) {
// Should never happen
throw new HSLFException(ie);
}
byte[] b = baos.toByteArray();
System.arraycopy(b,0,ret,0,3);
return ret;
}
return ret;
}
/**
* Convert from split R, G, B values to an integer RGB value
*/
public static int joinRGB(byte r, byte g, byte b) {
return joinRGB(new byte[] { r,g,b });
}
/**
* Convert from split R, G, B values to an integer RGB value
*/
public static int joinRGB(byte[] rgb) {
if(rgb.length != 3) {
throw new HSLFException("joinRGB accepts a byte array of 3 values, but got one of " + rgb.length + " values!");
}
byte[] with_zero = new byte[4];
System.arraycopy(rgb,0,with_zero,0,3);
with_zero[3] = 0;
/**
* Convert from split R, G, B values to an integer RGB value
*/
public static int joinRGB(byte r, byte g, byte b) {
return joinRGB(new byte[] { r,g,b });
}
/**
* Convert from split R, G, B values to an integer RGB value
*/
public static int joinRGB(byte[] rgb) {
if(rgb.length != 3) {
throw new HSLFException("joinRGB accepts a byte array of 3 values, but got one of " + rgb.length + " values!");
}
byte[] with_zero = new byte[4];
System.arraycopy(rgb,0,with_zero,0,3);
with_zero[3] = 0;
return LittleEndian.getInt(with_zero,0);
}
}
/**
* Write the contents of the record back, so it can be written
* to disk
*/
@Override
/**
* Write the contents of the record back, so it can be written
* to disk
*/
@Override
public void writeOut(OutputStream out) throws IOException {
// Header - size or type unchanged
out.write(_header);
// Header - size or type unchanged
out.write(_header);
// Write out the rgb values
writeLittleEndian(backgroundColourRGB,out);
writeLittleEndian(textAndLinesColourRGB,out);
writeLittleEndian(shadowsColourRGB,out);
writeLittleEndian(titleTextColourRGB,out);
writeLittleEndian(fillsColourRGB,out);
writeLittleEndian(accentColourRGB,out);
writeLittleEndian(accentAndHyperlinkColourRGB,out);
writeLittleEndian(accentAndFollowingHyperlinkColourRGB,out);
}
// Write out the rgb values
writeLittleEndian(backgroundColourRGB,out);
writeLittleEndian(textAndLinesColourRGB,out);
writeLittleEndian(shadowsColourRGB,out);
writeLittleEndian(titleTextColourRGB,out);
writeLittleEndian(fillsColourRGB,out);
writeLittleEndian(accentColourRGB,out);
writeLittleEndian(accentAndHyperlinkColourRGB,out);
writeLittleEndian(accentAndFollowingHyperlinkColourRGB,out);
}
/**
* Returns color by its index
*
* @param idx 0-based color index
* @return color by its index
*/
public int getColor(int idx){
int[] clr = {backgroundColourRGB, textAndLinesColourRGB, shadowsColourRGB, titleTextColourRGB,
fillsColourRGB, accentColourRGB, accentAndHyperlinkColourRGB, accentAndFollowingHyperlinkColourRGB};
return clr[idx];
}
/**
* Returns color by its index
*
* @param idx 0-based color index
* @return color by its index
*/
public int getColor(int idx){
int[] clr = {backgroundColourRGB, textAndLinesColourRGB, shadowsColourRGB, titleTextColourRGB,
fillsColourRGB, accentColourRGB, accentAndHyperlinkColourRGB, accentAndFollowingHyperlinkColourRGB};
return clr[idx];
}
@Override
public Map<String, Supplier<?>> getGenericProperties() {
final Map<String, Supplier<?>> m = new LinkedHashMap<>();
m.put("backgroundColourRGB", this::getBackgroundColourRGB);
m.put("textAndLinesColourRGB", this::getTextAndLinesColourRGB);
m.put("shadowsColourRGB", this::getShadowsColourRGB);
m.put("titleTextColourRGB", this::getTitleTextColourRGB);
m.put("fillsColourRGB", this::getFillsColourRGB);
m.put("accentColourRGB", this::getAccentColourRGB);
m.put("accentAndHyperlinkColourRGB", this::getAccentAndHyperlinkColourRGB);
m.put("accentAndFollowingHyperlinkColourRGB", this::getAccentAndFollowingHyperlinkColourRGB);
return Collections.unmodifiableMap(m);
}
@Override
public Map<String, Supplier<?>> getGenericProperties() {
final Map<String, Supplier<?>> m = new LinkedHashMap<>();
m.put("backgroundColourRGB", this::getBackgroundColourRGB);
m.put("textAndLinesColourRGB", this::getTextAndLinesColourRGB);
m.put("shadowsColourRGB", this::getShadowsColourRGB);
m.put("titleTextColourRGB", this::getTitleTextColourRGB);
m.put("fillsColourRGB", this::getFillsColourRGB);
m.put("accentColourRGB", this::getAccentColourRGB);
m.put("accentAndHyperlinkColourRGB", this::getAccentAndHyperlinkColourRGB);
m.put("accentAndFollowingHyperlinkColourRGB", this::getAccentAndFollowingHyperlinkColourRGB);
return Collections.unmodifiableMap(m);
}
}

View File

@ -30,10 +30,10 @@ import static org.apache.logging.log4j.util.Unbox.box;
* PPT 2000/XP/etc. (PPT 97 uses plain Escher Text Boxes for comments)
*/
public final class Comment2000 extends RecordContainer {
private byte[] _header;
private static final long _type = RecordTypes.Comment2000.typeID;
private byte[] _header;
private static final long _type = RecordTypes.Comment2000.typeID;
// Links to our more interesting children
// Links to our more interesting children
/**
* An optional string that specifies the name of the author of the presentation comment.
@ -53,68 +53,68 @@ public final class Comment2000 extends RecordContainer {
*/
private Comment2000Atom commentAtom;
/**
* Returns the Comment2000Atom of this Comment
*/
public Comment2000Atom getComment2000Atom() { return commentAtom; }
/**
* Returns the Comment2000Atom of this Comment
*/
public Comment2000Atom getComment2000Atom() { return commentAtom; }
/**
* Get the Author of this comment
*/
public String getAuthor() {
return authorRecord == null ? null : authorRecord.getText();
}
/**
* Set the Author of this comment
*/
public void setAuthor(String author) {
authorRecord.setText(author);
}
/**
* Get the Author of this comment
*/
public String getAuthor() {
return authorRecord == null ? null : authorRecord.getText();
}
/**
* Set the Author of this comment
*/
public void setAuthor(String author) {
authorRecord.setText(author);
}
/**
* Get the Author's Initials of this comment
*/
public String getAuthorInitials() {
return authorInitialsRecord == null ? null : authorInitialsRecord.getText();
}
/**
* Set the Author's Initials of this comment
*/
public void setAuthorInitials(String initials) {
authorInitialsRecord.setText(initials);
}
/**
* Get the Author's Initials of this comment
*/
public String getAuthorInitials() {
return authorInitialsRecord == null ? null : authorInitialsRecord.getText();
}
/**
* Set the Author's Initials of this comment
*/
public void setAuthorInitials(String initials) {
authorInitialsRecord.setText(initials);
}
/**
* Get the text of this comment
*/
public String getText() {
return commentRecord == null ? null : commentRecord.getText();
}
/**
* Set the text of this comment
*/
public void setText(String text) {
commentRecord.setText(text);
}
/**
* Get the text of this comment
*/
public String getText() {
return commentRecord == null ? null : commentRecord.getText();
}
/**
* Set the text of this comment
*/
public void setText(String text) {
commentRecord.setText(text);
}
/**
* Set things up, and find our more interesting children
*/
protected Comment2000(byte[] source, int start, int len) {
// Grab the header
_header = Arrays.copyOfRange(source, start, start+8);
/**
* Set things up, and find our more interesting children
*/
protected Comment2000(byte[] source, int start, int len) {
// Grab the header
_header = Arrays.copyOfRange(source, start, start+8);
// Find our children
_children = org.apache.poi.hslf.record.Record.findChildRecords(source,start+8,len-8);
findInterestingChildren();
}
// Find our children
_children = org.apache.poi.hslf.record.Record.findChildRecords(source,start+8,len-8);
findInterestingChildren();
}
/**
* Go through our child records, picking out the ones that are
* interesting, and saving those for use by the easy helper
* methods.
*/
private void findInterestingChildren() {
/**
* Go through our child records, picking out the ones that are
* interesting, and saving those for use by the easy helper
* methods.
*/
private void findInterestingChildren() {
for(org.apache.poi.hslf.record.Record r : _children){
if (r instanceof CString){
@ -129,47 +129,47 @@ public final class Comment2000 extends RecordContainer {
} else if (r instanceof Comment2000Atom){
commentAtom = (Comment2000Atom)r;
} else {
LOG.atWarn().log("Unexpected record with type={} in Comment2000: {}", box(r.getRecordType()),r.getClass().getName());
LOG.atWarn().log("Unexpected record with type={} in Comment2000: {}", box(r.getRecordType()),r.getClass().getName());
}
}
}
}
/**
* Create a new Comment2000, with blank fields
*/
public Comment2000() {
_header = new byte[8];
_children = new org.apache.poi.hslf.record.Record[4];
/**
* Create a new Comment2000, with blank fields
*/
public Comment2000() {
_header = new byte[8];
_children = new org.apache.poi.hslf.record.Record[4];
// Setup our header block
_header[0] = 0x0f; // We are a container record
LittleEndian.putShort(_header, 2, (short)_type);
// Setup our header block
_header[0] = 0x0f; // We are a container record
LittleEndian.putShort(_header, 2, (short)_type);
// Setup our child records
CString csa = new CString();
CString csb = new CString();
CString csc = new CString();
csa.setOptions(0x00);
csb.setOptions(0x10);
csc.setOptions(0x20);
_children[0] = csa;
_children[1] = csb;
_children[2] = csc;
_children[3] = new Comment2000Atom();
findInterestingChildren();
}
// Setup our child records
CString csa = new CString();
CString csb = new CString();
CString csc = new CString();
csa.setOptions(0x00);
csb.setOptions(0x10);
csc.setOptions(0x20);
_children[0] = csa;
_children[1] = csb;
_children[2] = csc;
_children[3] = new Comment2000Atom();
findInterestingChildren();
}
/**
* We are of type 1200
*/
public long getRecordType() { return _type; }
/**
* We are of type 1200
*/
public long getRecordType() { return _type; }
/**
* Write the contents of the record back, so it can be written
* to disk
*/
public void writeOut(OutputStream out) throws IOException {
writeOut(_header[0],_header[1],_type,_children,out);
}
/**
* Write the contents of the record back, so it can be written
* to disk
*/
public void writeOut(OutputStream out) throws IOException {
writeOut(_header[0],_header[1],_type,_children,out);
}
}

View File

@ -98,7 +98,7 @@ public final class Comment2000Atom extends RecordAtom
* @return the comment date.
*/
public Date getDate() {
return SystemTimeUtils.getDate(_data,4);
return SystemTimeUtils.getDate(_data,4);
}
/**
@ -106,7 +106,7 @@ public final class Comment2000Atom extends RecordAtom
* @param date the comment date.
*/
public void setDate(Date date) {
SystemTimeUtils.storeDate(date, _data, 4);
SystemTimeUtils.storeDate(date, _data, 4);
}
/**

View File

@ -46,232 +46,232 @@ import org.apache.poi.util.StringUtil;
*/
public class CurrentUserAtom
{
private static final Logger LOG = LogManager.getLogger(CurrentUserAtom.class);
//arbitrarily selected; may need to increase
private static final int MAX_RECORD_LENGTH = 1_000_000;
private static final Logger LOG = LogManager.getLogger(CurrentUserAtom.class);
//arbitrarily selected; may need to increase
private static final int MAX_RECORD_LENGTH = 1_000_000;
/** Standard Atom header */
private static final byte[] atomHeader = new byte[] { 0, 0, -10, 15 };
/** The PowerPoint magic number for a non-encrypted file */
private static final byte[] headerToken = new byte[] { 95, -64, -111, -29 };
/** The PowerPoint magic number for an encrypted file */
private static final byte[] encHeaderToken = new byte[] { -33, -60, -47, -13 };
// The Powerpoint 97 version, major and minor numbers
// byte[] ppt97FileVer = new byte[] { 8, 00, -13, 03, 03, 00 };
/** Standard Atom header */
private static final byte[] atomHeader = new byte[] { 0, 0, -10, 15 };
/** The PowerPoint magic number for a non-encrypted file */
private static final byte[] headerToken = new byte[] { 95, -64, -111, -29 };
/** The PowerPoint magic number for an encrypted file */
private static final byte[] encHeaderToken = new byte[] { -33, -60, -47, -13 };
// The Powerpoint 97 version, major and minor numbers
// byte[] ppt97FileVer = new byte[] { 8, 00, -13, 03, 03, 00 };
/** The version, major and minor numbers */
private int docFinalVersion;
private byte docMajorNo;
private byte docMinorNo;
/** The version, major and minor numbers */
private int docFinalVersion;
private byte docMajorNo;
private byte docMinorNo;
/** The Offset into the file for the current edit */
/** The Offset into the file for the current edit */
private long currentEditOffset;
/** The Username of the last person to edit the file */
private String lastEditUser;
/** The document release version. Almost always 8 */
private long releaseVersion;
/** The Username of the last person to edit the file */
private String lastEditUser;
/** The document release version. Almost always 8 */
private long releaseVersion;
/** Only correct after reading in or writing out */
private byte[] _contents;
/** Only correct after reading in or writing out */
private byte[] _contents;
/** Flag for encryption state of the whole file */
private boolean isEncrypted;
/** Flag for encryption state of the whole file */
private boolean isEncrypted;
/* ********************* getter/setter follows *********************** */
/* ********************* getter/setter follows *********************** */
public int getDocFinalVersion() { return docFinalVersion; }
public byte getDocMajorNo() { return docMajorNo; }
public byte getDocMinorNo() { return docMinorNo; }
public int getDocFinalVersion() { return docFinalVersion; }
public byte getDocMajorNo() { return docMajorNo; }
public byte getDocMinorNo() { return docMinorNo; }
public long getReleaseVersion() { return releaseVersion; }
public void setReleaseVersion(long rv) { releaseVersion = rv; }
public long getReleaseVersion() { return releaseVersion; }
public void setReleaseVersion(long rv) { releaseVersion = rv; }
/** Points to the UserEditAtom */
public long getCurrentEditOffset() { return currentEditOffset; }
public void setCurrentEditOffset(long id ) { currentEditOffset = id; }
/** Points to the UserEditAtom */
public long getCurrentEditOffset() { return currentEditOffset; }
public void setCurrentEditOffset(long id ) { currentEditOffset = id; }
public String getLastEditUsername() { return lastEditUser; }
public void setLastEditUsername(String u) { lastEditUser = u; }
public String getLastEditUsername() { return lastEditUser; }
public void setLastEditUsername(String u) { lastEditUser = u; }
public boolean isEncrypted() { return isEncrypted; }
public void setEncrypted(boolean isEncrypted) { this.isEncrypted = isEncrypted; }
public boolean isEncrypted() { return isEncrypted; }
public void setEncrypted(boolean isEncrypted) { this.isEncrypted = isEncrypted; }
/* ********************* real code follows *************************** */
/* ********************* real code follows *************************** */
/**
* Create a new Current User Atom
*/
public CurrentUserAtom() {
_contents = new byte[0];
/**
* Create a new Current User Atom
*/
public CurrentUserAtom() {
_contents = new byte[0];
// Initialise to empty
docFinalVersion = 0x03f4;
docMajorNo = 3;
docMinorNo = 0;
releaseVersion = 8;
currentEditOffset = 0;
lastEditUser = "Apache POI";
isEncrypted = false;
}
// Initialise to empty
docFinalVersion = 0x03f4;
docMajorNo = 3;
docMinorNo = 0;
releaseVersion = 8;
currentEditOffset = 0;
lastEditUser = "Apache POI";
isEncrypted = false;
}
/**
* Find the Current User in the filesystem, and create from that
*/
public CurrentUserAtom(DirectoryNode dir) throws IOException {
// Decide how big it is
DocumentEntry docProps =
(DocumentEntry)dir.getEntry("Current User");
/**
* Find the Current User in the filesystem, and create from that
*/
public CurrentUserAtom(DirectoryNode dir) throws IOException {
// Decide how big it is
DocumentEntry docProps =
(DocumentEntry)dir.getEntry("Current User");
// If it's clearly junk, bail out
if(docProps.getSize() > 131072) {
throw new CorruptPowerPointFileException("The Current User stream is implausably long. It's normally 28-200 bytes long, but was " + docProps.getSize() + " bytes");
}
// If it's clearly junk, bail out
if(docProps.getSize() > 131072) {
throw new CorruptPowerPointFileException("The Current User stream is implausably long. It's normally 28-200 bytes long, but was " + docProps.getSize() + " bytes");
}
// Grab the contents
try (InputStream in = dir.createDocumentInputStream("Current User")) {
_contents = IOUtils.toByteArray(in, docProps.getSize(), MAX_RECORD_LENGTH);
}
// Grab the contents
try (InputStream in = dir.createDocumentInputStream("Current User")) {
_contents = IOUtils.toByteArray(in, docProps.getSize(), MAX_RECORD_LENGTH);
}
// 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(PP95_DOCUMENT);
// PPT95 has 4 byte size, then data
if (!isPP95 && _contents.length >= 4) {
int size = LittleEndian.getInt(_contents);
isPP95 = (size + 4 == _contents.length);
}
// 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(PP95_DOCUMENT);
// PPT95 has 4 byte size, then data
if (!isPP95 && _contents.length >= 4) {
int size = LittleEndian.getInt(_contents);
isPP95 = (size + 4 == _contents.length);
}
if (isPP95) {
throw new OldPowerPointFormatException("Based on the Current User stream, you seem to have supplied a PowerPoint95 file, which isn't supported");
} else {
throw new CorruptPowerPointFileException("The Current User stream must be at least 28 bytes long, but was only " + _contents.length);
}
}
if (isPP95) {
throw new OldPowerPointFormatException("Based on the Current User stream, you seem to have supplied a PowerPoint95 file, which isn't supported");
} else {
throw new CorruptPowerPointFileException("The Current User stream must be at least 28 bytes long, but was only " + _contents.length);
}
}
// Set everything up
init();
}
// Set everything up
init();
}
/**
* Actually do the creation from a block of bytes
*/
private void init() {
// First up is the size, in 4 bytes, which is fixed
// Then is the header
/**
* Actually do the creation from a block of bytes
*/
private void init() {
// First up is the size, in 4 bytes, which is fixed
// Then is the header
isEncrypted = (LittleEndian.getInt(encHeaderToken) == LittleEndian.getInt(_contents,12));
isEncrypted = (LittleEndian.getInt(encHeaderToken) == LittleEndian.getInt(_contents,12));
// Grab the edit offset
currentEditOffset = LittleEndian.getUInt(_contents,16);
// Grab the edit offset
currentEditOffset = LittleEndian.getUInt(_contents,16);
// Grab the versions
docFinalVersion = LittleEndian.getUShort(_contents,22);
docMajorNo = _contents[24];
docMinorNo = _contents[25];
// Grab the versions
docFinalVersion = LittleEndian.getUShort(_contents,22);
docMajorNo = _contents[24];
docMinorNo = _contents[25];
// Get the username length
long usernameLen = LittleEndian.getUShort(_contents,20);
if(usernameLen > 512) {
// Handle the case of it being garbage
LOG.atWarn().log("Invalid username length {} found, treating as if there was no username set", box(usernameLen));
usernameLen = 0;
}
// Get the username length
long usernameLen = LittleEndian.getUShort(_contents,20);
if(usernameLen > 512) {
// Handle the case of it being garbage
LOG.atWarn().log("Invalid username length {} found, treating as if there was no username set", box(usernameLen));
usernameLen = 0;
}
// Now we know the length of the username,
// use this to grab the revision
if(_contents.length >= 28+(int)usernameLen + 4) {
releaseVersion = LittleEndian.getUInt(_contents,28+(int)usernameLen);
} else {
// No revision given, as not enough data. Odd
releaseVersion = 0;
}
// Now we know the length of the username,
// use this to grab the revision
if(_contents.length >= 28+(int)usernameLen + 4) {
releaseVersion = LittleEndian.getUInt(_contents,28+(int)usernameLen);
} else {
// No revision given, as not enough data. Odd
releaseVersion = 0;
}
// Grab the unicode username, if stored
int start = 28+(int)usernameLen+4;
// Grab the unicode username, if stored
int start = 28+(int)usernameLen+4;
if(_contents.length >= start+2*usernameLen) {
lastEditUser = StringUtil.getFromUnicodeLE(_contents, start, (int)usernameLen);
} else {
// Fake from the 8 bit version
lastEditUser = StringUtil.getFromCompressedUnicode(_contents, 28, (int)usernameLen);
}
}
if(_contents.length >= start+2*usernameLen) {
lastEditUser = StringUtil.getFromUnicodeLE(_contents, start, (int)usernameLen);
} else {
// Fake from the 8 bit version
lastEditUser = StringUtil.getFromCompressedUnicode(_contents, 28, (int)usernameLen);
}
}
/**
* Writes ourselves back out
*/
public void writeOut(OutputStream out) throws IOException {
// Decide on the size
// 8 = atom header
// 20 = up to name
// 4 = revision
// 3 * len = ascii + unicode
int size = 8 + 20 + 4 + (3 * lastEditUser.length());
_contents = IOUtils.safelyAllocate(size, MAX_RECORD_LENGTH);
/**
* Writes ourselves back out
*/
public void writeOut(OutputStream out) throws IOException {
// Decide on the size
// 8 = atom header
// 20 = up to name
// 4 = revision
// 3 * len = ascii + unicode
int size = 8 + 20 + 4 + (3 * lastEditUser.length());
_contents = IOUtils.safelyAllocate(size, MAX_RECORD_LENGTH);
// First we have a 8 byte atom header
System.arraycopy(atomHeader,0,_contents,0,4);
// Size is 20+user len + revision len(4)
int atomSize = 20+4+lastEditUser.length();
LittleEndian.putInt(_contents,4,atomSize);
// First we have a 8 byte atom header
System.arraycopy(atomHeader,0,_contents,0,4);
// Size is 20+user len + revision len(4)
int atomSize = 20+4+lastEditUser.length();
LittleEndian.putInt(_contents,4,atomSize);
// Now we have the size of the details, which is 20
LittleEndian.putInt(_contents,8,20);
// Now we have the size of the details, which is 20
LittleEndian.putInt(_contents,8,20);
// Now the ppt un-encrypted header token (4 bytes)
System.arraycopy((isEncrypted ? encHeaderToken : headerToken),0,_contents,12,4);
// Now the ppt un-encrypted header token (4 bytes)
System.arraycopy((isEncrypted ? encHeaderToken : headerToken),0,_contents,12,4);
// Now the current edit offset
LittleEndian.putInt(_contents,16,(int)currentEditOffset);
// Now the current edit offset
LittleEndian.putInt(_contents,16,(int)currentEditOffset);
// The username gets stored twice, once as US
// ascii, and again as unicode laster on
byte[] asciiUN = IOUtils.safelyAllocate(lastEditUser.length(), MAX_RECORD_LENGTH);
StringUtil.putCompressedUnicode(lastEditUser,asciiUN,0);
// The username gets stored twice, once as US
// ascii, and again as unicode laster on
byte[] asciiUN = IOUtils.safelyAllocate(lastEditUser.length(), MAX_RECORD_LENGTH);
StringUtil.putCompressedUnicode(lastEditUser,asciiUN,0);
// Now we're able to do the length of the last edited user
LittleEndian.putShort(_contents,20,(short)asciiUN.length);
// Now we're able to do the length of the last edited user
LittleEndian.putShort(_contents,20,(short)asciiUN.length);
// Now the file versions, 2+1+1
LittleEndian.putShort(_contents,22,(short)docFinalVersion);
_contents[24] = docMajorNo;
_contents[25] = docMinorNo;
// Now the file versions, 2+1+1
LittleEndian.putShort(_contents,22,(short)docFinalVersion);
_contents[24] = docMajorNo;
_contents[25] = docMinorNo;
// 2 bytes blank
_contents[26] = 0;
_contents[27] = 0;
// 2 bytes blank
_contents[26] = 0;
_contents[27] = 0;
// At this point we have the username as us ascii
System.arraycopy(asciiUN,0,_contents,28,asciiUN.length);
// At this point we have the username as us ascii
System.arraycopy(asciiUN,0,_contents,28,asciiUN.length);
// 4 byte release version
LittleEndian.putInt(_contents,28+asciiUN.length,(int)releaseVersion);
// 4 byte release version
LittleEndian.putInt(_contents,28+asciiUN.length,(int)releaseVersion);
// username in unicode
byte [] ucUN = IOUtils.safelyAllocate(lastEditUser.length() * 2L, MAX_RECORD_LENGTH);
StringUtil.putUnicodeLE(lastEditUser,ucUN,0);
System.arraycopy(ucUN,0,_contents,28+asciiUN.length+4,ucUN.length);
// username in unicode
byte [] ucUN = IOUtils.safelyAllocate(lastEditUser.length() * 2L, MAX_RECORD_LENGTH);
StringUtil.putUnicodeLE(lastEditUser,ucUN,0);
System.arraycopy(ucUN,0,_contents,28+asciiUN.length+4,ucUN.length);
// Write out
out.write(_contents);
}
// Write out
out.write(_contents);
}
/**
* Writes ourselves back out to a filesystem
*/
public void writeToFS(POIFSFileSystem fs) throws IOException {
// Grab contents
try (UnsynchronizedByteArrayOutputStream baos = new UnsynchronizedByteArrayOutputStream()) {
writeOut(baos);
try (InputStream is = baos.toInputStream()) {
// Write out
fs.createOrUpdateDocument(is, "Current User");
}
}
}
/**
* Writes ourselves back out to a filesystem
*/
public void writeToFS(POIFSFileSystem fs) throws IOException {
// Grab contents
try (UnsynchronizedByteArrayOutputStream baos = new UnsynchronizedByteArrayOutputStream()) {
writeOut(baos);
try (InputStream is = baos.toInputStream()) {
// Write out
fs.createOrUpdateDocument(is, "Current User");
}
}
}
}

View File

@ -29,62 +29,62 @@ import org.apache.poi.util.LittleEndian;
* A container record that specifies information about the document and document display settings.
*/
public final class DocInfoListContainer extends RecordContainer {
private byte[] _header;
private static final long _type = RecordTypes.List.typeID;
private byte[] _header;
private static final long _type = RecordTypes.List.typeID;
// Links to our more interesting children
// Links to our more interesting children
/**
* Set things up, and find our more interesting children
*/
protected DocInfoListContainer(byte[] source, int start, int len) {
// Grab the header
_header = Arrays.copyOfRange(source,start,start+8);
/**
* Set things up, and find our more interesting children
*/
protected DocInfoListContainer(byte[] source, int start, int len) {
// Grab the header
_header = Arrays.copyOfRange(source,start,start+8);
// Find our children
_children = Record.findChildRecords(source,start+8,len-8);
findInterestingChildren();
}
// Find our children
_children = Record.findChildRecords(source,start+8,len-8);
findInterestingChildren();
}
/**
* Go through our child records, picking out the ones that are
* interesting, and saving those for use by the easy helper
* methods.
*/
private void findInterestingChildren() {
/**
* Go through our child records, picking out the ones that are
* interesting, and saving those for use by the easy helper
* methods.
*/
private void findInterestingChildren() {
}
}
/**
* Create a new DocInfoListContainer, with blank fields - not yet supported
*/
private DocInfoListContainer() {
_header = new byte[8];
_children = new org.apache.poi.hslf.record.Record[0];
/**
* Create a new DocInfoListContainer, with blank fields - not yet supported
*/
private DocInfoListContainer() {
_header = new byte[8];
_children = new org.apache.poi.hslf.record.Record[0];
// Setup our header block
_header[0] = 0x0f; // We are a container record
LittleEndian.putShort(_header, 2, (short)_type);
// Setup our header block
_header[0] = 0x0f; // We are a container record
LittleEndian.putShort(_header, 2, (short)_type);
// Setup our child records
findInterestingChildren();
}
// Setup our child records
findInterestingChildren();
}
/**
* We are of type 0x7D0
*/
public long getRecordType() { return _type; }
/**
* We are of type 0x7D0
*/
public long getRecordType() { return _type; }
/**
* Write the contents of the record back, so it can be written
* to disk
*/
public void writeOut(OutputStream out) throws IOException {
writeOut(_header[0],_header[1],_type,_children,out);
}
/**
* Write the contents of the record back, so it can be written
* to disk
*/
public void writeOut(OutputStream out) throws IOException {
writeOut(_header[0],_header[1],_type,_children,out);
}
@Override
public Map<String, Supplier<?>> getGenericProperties() {
return null;
}
@Override
public Map<String, Supplier<?>> getGenericProperties() {
return null;
}
}

View File

@ -31,177 +31,177 @@ import static org.apache.logging.log4j.util.Unbox.box;
public final class Document extends PositionDependentRecordContainer
{
private byte[] _header;
private static long _type = 1000;
private byte[] _header;
private static long _type = 1000;
// Links to our more interesting children
private DocumentAtom documentAtom;
private Environment environment;
private PPDrawingGroup ppDrawing;
private SlideListWithText[] slwts;
private ExObjList exObjList; // Can be null
/**
* Returns the DocumentAtom of this Document
*/
public DocumentAtom getDocumentAtom() { return documentAtom; }
/**
* Returns the Environment of this Notes, which lots of
* settings for the document in it
*/
public Environment getEnvironment() { return environment; }
/**
* Returns the PPDrawingGroup, which holds an Escher Structure
* that contains information on pictures in the slides.
*/
public PPDrawingGroup getPPDrawingGroup() { return ppDrawing; }
/**
* Returns the ExObjList, which holds the references to
* external objects used in the slides. This may be null, if
* there are no external references.
*
* @param create if true, create an ExObjList if it doesn't exist
*/
public ExObjList getExObjList(boolean create) {
if (exObjList == null && create) {
exObjList = new ExObjList();
addChildAfter(exObjList, getDocumentAtom());
}
return exObjList;
}
/**
* Returns all the SlideListWithTexts that are defined for
* this Document. They hold the text, and some of the text
* properties, which are referred to by the slides.
* This will normally return an array of size 2 or 3
*/
public SlideListWithText[] getSlideListWithTexts() { return slwts; }
// Links to our more interesting children
private DocumentAtom documentAtom;
private Environment environment;
private PPDrawingGroup ppDrawing;
private SlideListWithText[] slwts;
private ExObjList exObjList; // Can be null
/**
* Returns the SlideListWithText that deals with the
* Master Slides
*/
public SlideListWithText getMasterSlideListWithText() {
for (SlideListWithText slwt : slwts) {
if (slwt.getInstance() == SlideListWithText.MASTER) {
return slwt;
}
}
* Returns the DocumentAtom of this Document
*/
public DocumentAtom getDocumentAtom() { return documentAtom; }
/**
* Returns the Environment of this Notes, which lots of
* settings for the document in it
*/
public Environment getEnvironment() { return environment; }
/**
* Returns the PPDrawingGroup, which holds an Escher Structure
* that contains information on pictures in the slides.
*/
public PPDrawingGroup getPPDrawingGroup() { return ppDrawing; }
/**
* Returns the ExObjList, which holds the references to
* external objects used in the slides. This may be null, if
* there are no external references.
*
* @param create if true, create an ExObjList if it doesn't exist
*/
public ExObjList getExObjList(boolean create) {
if (exObjList == null && create) {
exObjList = new ExObjList();
addChildAfter(exObjList, getDocumentAtom());
}
return exObjList;
}
/**
* Returns all the SlideListWithTexts that are defined for
* this Document. They hold the text, and some of the text
* properties, which are referred to by the slides.
* This will normally return an array of size 2 or 3
*/
public SlideListWithText[] getSlideListWithTexts() { return slwts; }
/**
* Returns the SlideListWithText that deals with the
* Master Slides
*/
public SlideListWithText getMasterSlideListWithText() {
for (SlideListWithText slwt : slwts) {
if (slwt.getInstance() == SlideListWithText.MASTER) {
return slwt;
}
}
return null;
}
/**
* Returns the SlideListWithText that deals with the
* Slides, or null if there isn't one
*/
public SlideListWithText getSlideSlideListWithText() {
for (SlideListWithText slwt : slwts) {
if (slwt.getInstance() == SlideListWithText.SLIDES) {
return slwt;
}
}
return null;
/**
* Returns the SlideListWithText that deals with the
* Slides, or null if there isn't one
*/
public SlideListWithText getSlideSlideListWithText() {
for (SlideListWithText slwt : slwts) {
if (slwt.getInstance() == SlideListWithText.SLIDES) {
return slwt;
}
}
return null;
}
/**
* Returns the SlideListWithText that deals with the
* notes, or null if there isn't one
*/
public SlideListWithText getNotesSlideListWithText() {
for (SlideListWithText slwt : slwts) {
if (slwt.getInstance() == SlideListWithText.NOTES) {
return slwt;
}
}
return null;
/**
* Returns the SlideListWithText that deals with the
* notes, or null if there isn't one
*/
public SlideListWithText getNotesSlideListWithText() {
for (SlideListWithText slwt : slwts) {
if (slwt.getInstance() == SlideListWithText.NOTES) {
return slwt;
}
}
return null;
}
/**
* Set things up, and find our more interesting children
*/
/* package */ Document(byte[] source, int start, int len) {
// Grab the header
_header = Arrays.copyOfRange(source, start, start+8);
/**
* Set things up, and find our more interesting children
*/
/* package */ Document(byte[] source, int start, int len) {
// Grab the header
_header = Arrays.copyOfRange(source, start, start+8);
// Find our children
_children = Record.findChildRecords(source,start+8,len-8);
// Find our children
_children = Record.findChildRecords(source,start+8,len-8);
// Our first one should be a document atom
if(! (_children[0] instanceof DocumentAtom)) {
throw new IllegalStateException("The first child of a Document must be a DocumentAtom");
}
documentAtom = (DocumentAtom)_children[0];
// Our first one should be a document atom
if(! (_children[0] instanceof DocumentAtom)) {
throw new IllegalStateException("The first child of a Document must be a DocumentAtom");
}
documentAtom = (DocumentAtom)_children[0];
// Find how many SlideListWithTexts we have
// Also, grab the Environment and PPDrawing records
// on our way past
int slwtcount = 0;
for(int i=1; i<_children.length; i++) {
if(_children[i] instanceof SlideListWithText) {
slwtcount++;
}
if(_children[i] instanceof Environment) {
environment = (Environment)_children[i];
}
if(_children[i] instanceof PPDrawingGroup) {
ppDrawing = (PPDrawingGroup)_children[i];
}
if(_children[i] instanceof ExObjList) {
exObjList = (ExObjList)_children[i];
}
}
// Find how many SlideListWithTexts we have
// Also, grab the Environment and PPDrawing records
// on our way past
int slwtcount = 0;
for(int i=1; i<_children.length; i++) {
if(_children[i] instanceof SlideListWithText) {
slwtcount++;
}
if(_children[i] instanceof Environment) {
environment = (Environment)_children[i];
}
if(_children[i] instanceof PPDrawingGroup) {
ppDrawing = (PPDrawingGroup)_children[i];
}
if(_children[i] instanceof ExObjList) {
exObjList = (ExObjList)_children[i];
}
}
// You should only every have 1, 2 or 3 SLWTs
// (normally it's 2, or 3 if you have notes)
// Complain if it's not
if(slwtcount == 0) {
LOG.atWarn().log("No SlideListWithText's found - there should normally be at least one!");
}
if(slwtcount > 3) {
LOG.atWarn().log("Found {} SlideListWithTexts - normally there should only be three!", box(slwtcount));
}
// You should only every have 1, 2 or 3 SLWTs
// (normally it's 2, or 3 if you have notes)
// Complain if it's not
if(slwtcount == 0) {
LOG.atWarn().log("No SlideListWithText's found - there should normally be at least one!");
}
if(slwtcount > 3) {
LOG.atWarn().log("Found {} SlideListWithTexts - normally there should only be three!", box(slwtcount));
}
// Now grab all the SLWTs
slwts = new SlideListWithText[slwtcount];
slwtcount = 0;
for(int i=1; i<_children.length; i++) {
if(_children[i] instanceof SlideListWithText) {
slwts[slwtcount] = (SlideListWithText)_children[i];
slwtcount++;
}
}
}
// Now grab all the SLWTs
slwts = new SlideListWithText[slwtcount];
slwtcount = 0;
for(int i=1; i<_children.length; i++) {
if(_children[i] instanceof SlideListWithText) {
slwts[slwtcount] = (SlideListWithText)_children[i];
slwtcount++;
}
}
}
/**
* Adds a new SlideListWithText record, at the appropriate
* point in the child records.
*/
public void addSlideListWithText(SlideListWithText slwt) {
// The new SlideListWithText should go in
// just before the EndDocumentRecord
Record endDoc = _children[_children.length - 1];
if(endDoc.getRecordType() == RecordTypes.RoundTripCustomTableStyles12.typeID) {
// last record can optionally be a RoundTripCustomTableStyles12Atom
endDoc = _children[_children.length - 2];
}
if(endDoc.getRecordType() != RecordTypes.EndDocument.typeID) {
throw new IllegalStateException("The last child record of a Document should be EndDocument, but it was " + endDoc);
}
/**
* Adds a new SlideListWithText record, at the appropriate
* point in the child records.
*/
public void addSlideListWithText(SlideListWithText slwt) {
// The new SlideListWithText should go in
// just before the EndDocumentRecord
Record endDoc = _children[_children.length - 1];
if(endDoc.getRecordType() == RecordTypes.RoundTripCustomTableStyles12.typeID) {
// last record can optionally be a RoundTripCustomTableStyles12Atom
endDoc = _children[_children.length - 2];
}
if(endDoc.getRecordType() != RecordTypes.EndDocument.typeID) {
throw new IllegalStateException("The last child record of a Document should be EndDocument, but it was " + endDoc);
}
// Add in the record
addChildBefore(slwt, endDoc);
// Add in the record
addChildBefore(slwt, endDoc);
// Updated our cached list of SlideListWithText records
int newSize = slwts.length + 1;
SlideListWithText[] nl = new SlideListWithText[newSize];
System.arraycopy(slwts, 0, nl, 0, slwts.length);
nl[nl.length-1] = slwt;
slwts = nl;
}
// Updated our cached list of SlideListWithText records
int newSize = slwts.length + 1;
SlideListWithText[] nl = new SlideListWithText[newSize];
System.arraycopy(slwts, 0, nl, 0, slwts.length);
nl[nl.length-1] = slwt;
slwts = nl;
}
public void removeSlideListWithText(SlideListWithText slwt) {
ArrayList<SlideListWithText> lst = new ArrayList<>();
@ -214,16 +214,16 @@ public final class Document extends PositionDependentRecordContainer
slwts = lst.toArray(new SlideListWithText[0]);
}
/**
* We are of type 1000
*/
public long getRecordType() { return _type; }
/**
* We are of type 1000
*/
public long getRecordType() { return _type; }
/**
* Write the contents of the record back, so it can be written
* to disk
*/
public void writeOut(OutputStream out) throws IOException {
writeOut(_header[0],_header[1],_type,_children,out);
}
/**
* Write the contents of the record back, so it can be written
* to disk
*/
public void writeOut(OutputStream out) throws IOException {
writeOut(_header[0],_header[1],_type,_children,out);
}
}

View File

@ -36,224 +36,224 @@ import org.apache.poi.util.Removal;
@SuppressWarnings({"WeakerAccess", "unused"})
public final class DocumentAtom extends RecordAtom {
/**
* Holds the different Slide Size values
*/
public enum SlideSize {
/** Slide size ratio is consistent with a computer screen. */
ON_SCREEN,
/** Slide size ratio is consistent with letter paper. */
LETTER_SIZED_PAPER,
/** Slide size ratio is consistent with A4 paper. */
A4_SIZED_PAPER,
/** Slide size ratio is consistent with 35mm photo slides. */
ON_35MM,
/** Slide size ratio is consistent with overhead projector slides. */
OVERHEAD,
/** Slide size ratio is consistent with a banner. */
BANNER,
/**
* Slide size ratio that is not consistent with any of the other specified slide sizes in
* this enumeration.
*/
CUSTOM
}
/**
* Holds the different Slide Size values
*/
public enum SlideSize {
/** Slide size ratio is consistent with a computer screen. */
ON_SCREEN,
/** Slide size ratio is consistent with letter paper. */
LETTER_SIZED_PAPER,
/** Slide size ratio is consistent with A4 paper. */
A4_SIZED_PAPER,
/** Slide size ratio is consistent with 35mm photo slides. */
ON_35MM,
/** Slide size ratio is consistent with overhead projector slides. */
OVERHEAD,
/** Slide size ratio is consistent with a banner. */
BANNER,
/**
* Slide size ratio that is not consistent with any of the other specified slide sizes in
* this enumeration.
*/
CUSTOM
}
//arbitrarily selected; may need to increase
private static final int MAX_RECORD_LENGTH = 1_000_000;
//arbitrarily selected; may need to increase
private static final int MAX_RECORD_LENGTH = 1_000_000;
private final byte[] _header = new byte[8];
private static final long _type = RecordTypes.DocumentAtom.typeID;
private final byte[] _header = new byte[8];
private static final long _type = RecordTypes.DocumentAtom.typeID;
private long slideSizeX; // PointAtom, assume 1st 4 bytes = X
private long slideSizeY; // PointAtom, assume 2nd 4 bytes = Y
private long notesSizeX; // PointAtom, assume 1st 4 bytes = X
private long notesSizeY; // PointAtom, assume 2nd 4 bytes = Y
private long serverZoomFrom; // RatioAtom, assume 1st 4 bytes = from
private long serverZoomTo; // RatioAtom, assume 2nd 4 bytes = to
private long slideSizeX; // PointAtom, assume 1st 4 bytes = X
private long slideSizeY; // PointAtom, assume 2nd 4 bytes = Y
private long notesSizeX; // PointAtom, assume 1st 4 bytes = X
private long notesSizeY; // PointAtom, assume 2nd 4 bytes = Y
private long serverZoomFrom; // RatioAtom, assume 1st 4 bytes = from
private long serverZoomTo; // RatioAtom, assume 2nd 4 bytes = to
private final long notesMasterPersist; // ref to NotesMaster, 0 if none
private final long handoutMasterPersist; // ref to HandoutMaster, 0 if none
private final long notesMasterPersist; // ref to NotesMaster, 0 if none
private final long handoutMasterPersist; // ref to HandoutMaster, 0 if none
private final int firstSlideNum;
private int slideSizeType; // see DocumentAtom.SlideSize
private final int firstSlideNum;
private int slideSizeType; // see DocumentAtom.SlideSize
private byte saveWithFonts;
private final byte omitTitlePlace;
private final byte rightToLeft;
private final byte showComments;
private byte saveWithFonts;
private final byte omitTitlePlace;
private final byte rightToLeft;
private final byte showComments;
private final byte[] reserved;
private final byte[] reserved;
public long getSlideSizeX() { return slideSizeX; }
public long getSlideSizeY() { return slideSizeY; }
public long getNotesSizeX() { return notesSizeX; }
public long getNotesSizeY() { return notesSizeY; }
public void setSlideSizeX(long x) { slideSizeX = x; }
public void setSlideSizeY(long y) { slideSizeY = y; }
public void setNotesSizeX(long x) { notesSizeX = x; }
public void setNotesSizeY(long y) { notesSizeY = y; }
public long getSlideSizeX() { return slideSizeX; }
public long getSlideSizeY() { return slideSizeY; }
public long getNotesSizeX() { return notesSizeX; }
public long getNotesSizeY() { return notesSizeY; }
public void setSlideSizeX(long x) { slideSizeX = x; }
public void setSlideSizeY(long y) { slideSizeY = y; }
public void setNotesSizeX(long x) { notesSizeX = x; }
public void setNotesSizeY(long y) { notesSizeY = y; }
public long getServerZoomFrom() { return serverZoomFrom; }
public long getServerZoomTo() { return serverZoomTo; }
public void setServerZoomFrom(long zoom) { serverZoomFrom = zoom; }
public void setServerZoomTo(long zoom) { serverZoomTo = zoom; }
public long getServerZoomFrom() { return serverZoomFrom; }
public long getServerZoomTo() { return serverZoomTo; }
public void setServerZoomFrom(long zoom) { serverZoomFrom = zoom; }
public void setServerZoomTo(long zoom) { serverZoomTo = zoom; }
/** Returns a reference to the NotesMaster, or 0 if none */
public long getNotesMasterPersist() { return notesMasterPersist; }
/** Returns a reference to the HandoutMaster, or 0 if none */
public long getHandoutMasterPersist() { return handoutMasterPersist; }
/** Returns a reference to the NotesMaster, or 0 if none */
public long getNotesMasterPersist() { return notesMasterPersist; }
/** Returns a reference to the HandoutMaster, or 0 if none */
public long getHandoutMasterPersist() { return handoutMasterPersist; }
public int getFirstSlideNum() { return firstSlideNum; }
public int getFirstSlideNum() { return firstSlideNum; }
/**
* The Size of the Document's slides, @see DocumentAtom.SlideSize for values
* @deprecated to be replaced by enum
*/
@Deprecated
@Removal(version = "5.0.0")
public int getSlideSizeType() { return slideSizeType; }
/**
* The Size of the Document's slides, @see DocumentAtom.SlideSize for values
* @deprecated to be replaced by enum
*/
@Deprecated
@Removal(version = "5.0.0")
public int getSlideSizeType() { return slideSizeType; }
public SlideSize getSlideSizeTypeEnum() {
return SlideSize.values()[slideSizeType];
}
public SlideSize getSlideSizeTypeEnum() {
return SlideSize.values()[slideSizeType];
}
public void setSlideSize(SlideSize size) {
slideSizeType = size.ordinal();
}
public void setSlideSize(SlideSize size) {
slideSizeType = size.ordinal();
}
/** Was the document saved with True Type fonts embeded? */
public boolean getSaveWithFonts() {
return saveWithFonts != 0;
}
/** Was the document saved with True Type fonts embeded? */
public boolean getSaveWithFonts() {
return saveWithFonts != 0;
}
/** Set the font embedding state */
public void setSaveWithFonts(boolean saveWithFonts) {
this.saveWithFonts = (byte)(saveWithFonts ? 1 : 0);
}
/** Set the font embedding state */
public void setSaveWithFonts(boolean saveWithFonts) {
this.saveWithFonts = (byte)(saveWithFonts ? 1 : 0);
}
/** Have the placeholders on the title slide been omitted? */
public boolean getOmitTitlePlace() {
return omitTitlePlace != 0;
}
/** Have the placeholders on the title slide been omitted? */
public boolean getOmitTitlePlace() {
return omitTitlePlace != 0;
}
/** Is this a Bi-Directional PPT Doc? */
public boolean getRightToLeft() {
return rightToLeft != 0;
}
/** Is this a Bi-Directional PPT Doc? */
public boolean getRightToLeft() {
return rightToLeft != 0;
}
/** Are comment shapes visible? */
public boolean getShowComments() {
return showComments != 0;
}
/** Are comment shapes visible? */
public boolean getShowComments() {
return showComments != 0;
}
/* *************** record code follows ********************** */
/* *************** record code follows ********************** */
/**
* For the Document Atom
*/
/* package */ DocumentAtom(byte[] source, int start, int len) {
final int maxLen = Math.max(len, 48);
LittleEndianByteArrayInputStream leis =
new LittleEndianByteArrayInputStream(source, start, maxLen);
/**
* For the Document Atom
*/
/* package */ DocumentAtom(byte[] source, int start, int len) {
final int maxLen = Math.max(len, 48);
LittleEndianByteArrayInputStream leis =
new LittleEndianByteArrayInputStream(source, start, maxLen);
// Get the header
leis.readFully(_header);
// Get the header
leis.readFully(_header);
// Get the sizes and zoom ratios
slideSizeX = leis.readInt();
slideSizeY = leis.readInt();
notesSizeX = leis.readInt();
notesSizeY = leis.readInt();
serverZoomFrom = leis.readInt();
serverZoomTo = leis.readInt();
// Get the sizes and zoom ratios
slideSizeX = leis.readInt();
slideSizeY = leis.readInt();
notesSizeX = leis.readInt();
notesSizeY = leis.readInt();
serverZoomFrom = leis.readInt();
serverZoomTo = leis.readInt();
// Get the master persists
notesMasterPersist = leis.readInt();
handoutMasterPersist = leis.readInt();
// Get the master persists
notesMasterPersist = leis.readInt();
handoutMasterPersist = leis.readInt();
// Get the ID of the first slide
firstSlideNum = leis.readShort();
// Get the ID of the first slide
firstSlideNum = leis.readShort();
// Get the slide size type
slideSizeType = leis.readShort();
// Get the slide size type
slideSizeType = leis.readShort();
// Get the booleans as bytes
saveWithFonts = leis.readByte();
omitTitlePlace = leis.readByte();
rightToLeft = leis.readByte();
showComments = leis.readByte();
// Get the booleans as bytes
saveWithFonts = leis.readByte();
omitTitlePlace = leis.readByte();
rightToLeft = leis.readByte();
showComments = leis.readByte();
// If there's any other bits of data, keep them about
reserved = IOUtils.safelyAllocate(maxLen-48L, MAX_RECORD_LENGTH);
leis.readFully(reserved);
}
// If there's any other bits of data, keep them about
reserved = IOUtils.safelyAllocate(maxLen-48L, MAX_RECORD_LENGTH);
leis.readFully(reserved);
}
/**
* We are of type 1001
*/
@Override
public long getRecordType() { return _type; }
/**
* We are of type 1001
*/
@Override
public long getRecordType() { return _type; }
/**
* Write the contents of the record back, so it can be written
* to disk
*/
@Override
public void writeOut(OutputStream out) throws IOException {
// Header
out.write(_header);
/**
* Write the contents of the record back, so it can be written
* to disk
*/
@Override
public void writeOut(OutputStream out) throws IOException {
// Header
out.write(_header);
// The sizes and zoom ratios
writeLittleEndian((int)slideSizeX,out);
writeLittleEndian((int)slideSizeY,out);
writeLittleEndian((int)notesSizeX,out);
writeLittleEndian((int)notesSizeY,out);
writeLittleEndian((int)serverZoomFrom,out);
writeLittleEndian((int)serverZoomTo,out);
// The sizes and zoom ratios
writeLittleEndian((int)slideSizeX,out);
writeLittleEndian((int)slideSizeY,out);
writeLittleEndian((int)notesSizeX,out);
writeLittleEndian((int)notesSizeY,out);
writeLittleEndian((int)serverZoomFrom,out);
writeLittleEndian((int)serverZoomTo,out);
// The master persists
writeLittleEndian((int)notesMasterPersist,out);
writeLittleEndian((int)handoutMasterPersist,out);
// The master persists
writeLittleEndian((int)notesMasterPersist,out);
writeLittleEndian((int)handoutMasterPersist,out);
// The ID of the first slide
writeLittleEndian((short)firstSlideNum,out);
// The ID of the first slide
writeLittleEndian((short)firstSlideNum,out);
// The slide size type
writeLittleEndian((short)slideSizeType,out);
// The slide size type
writeLittleEndian((short)slideSizeType,out);
// The booleans as bytes
out.write(saveWithFonts);
out.write(omitTitlePlace);
out.write(rightToLeft);
out.write(showComments);
// The booleans as bytes
out.write(saveWithFonts);
out.write(omitTitlePlace);
out.write(rightToLeft);
out.write(showComments);
// Reserved data
out.write(reserved);
}
// Reserved data
out.write(reserved);
}
@Override
public Map<String, Supplier<?>> getGenericProperties() {
final Map<String, Supplier<?>> m = new LinkedHashMap<>();
m.put("slideSizeX", this::getSlideSizeX);
m.put("slideSizeY", this::getSlideSizeY);
m.put("notesSizeX", this::getNotesSizeX);
m.put("notesSizeY", this::getNotesSizeY);
m.put("serverZoomFrom", this::getServerZoomFrom);
m.put("serverZoomTo", this::getServerZoomTo);
m.put("notesMasterPersist", this::getNotesMasterPersist);
m.put("handoutMasterPersist", this::getHandoutMasterPersist);
m.put("firstSlideNum", this::getFirstSlideNum);
m.put("slideSize", this::getSlideSizeTypeEnum);
m.put("saveWithFonts", this::getSaveWithFonts);
m.put("omitTitlePlace", this::getOmitTitlePlace);
m.put("rightToLeft", this::getRightToLeft);
m.put("showComments", this::getShowComments);
return Collections.unmodifiableMap(m);
}
@Override
public Map<String, Supplier<?>> getGenericProperties() {
final Map<String, Supplier<?>> m = new LinkedHashMap<>();
m.put("slideSizeX", this::getSlideSizeX);
m.put("slideSizeY", this::getSlideSizeY);
m.put("notesSizeX", this::getNotesSizeX);
m.put("notesSizeY", this::getNotesSizeY);
m.put("serverZoomFrom", this::getServerZoomFrom);
m.put("serverZoomTo", this::getServerZoomTo);
m.put("notesMasterPersist", this::getNotesMasterPersist);
m.put("handoutMasterPersist", this::getHandoutMasterPersist);
m.put("firstSlideNum", this::getFirstSlideNum);
m.put("slideSize", this::getSlideSizeTypeEnum);
m.put("saveWithFonts", this::getSaveWithFonts);
m.put("omitTitlePlace", this::getOmitTitlePlace);
m.put("rightToLeft", this::getRightToLeft);
m.put("showComments", this::getShowComments);
return Collections.unmodifiableMap(m);
}
}

View File

@ -42,101 +42,101 @@ import org.apache.poi.util.LittleEndianInputStream;
*/
public final class DocumentEncryptionAtom extends PositionDependentRecordAtom {
private static final long _type = RecordTypes.DocumentEncryptionAtom.typeID;
private final byte[] _header;
private EncryptionInfo ei;
private final byte[] _header;
private EncryptionInfo ei;
/**
* For the Document Encryption Atom
*/
protected DocumentEncryptionAtom(byte[] source, int start, int len) {
// Get the header
_header = Arrays.copyOfRange(source,start,start+8);
/**
* For the Document Encryption Atom
*/
protected DocumentEncryptionAtom(byte[] source, int start, int len) {
// Get the header
_header = Arrays.copyOfRange(source,start,start+8);
ByteArrayInputStream bis = new ByteArrayInputStream(source, start+8, len-8);
try (LittleEndianInputStream leis = new LittleEndianInputStream(bis)) {
ei = new EncryptionInfo(leis, EncryptionMode.cryptoAPI);
} catch (IOException e) {
throw new EncryptedDocumentException(e);
}
}
ByteArrayInputStream bis = new ByteArrayInputStream(source, start+8, len-8);
try (LittleEndianInputStream leis = new LittleEndianInputStream(bis)) {
ei = new EncryptionInfo(leis, EncryptionMode.cryptoAPI);
} catch (IOException e) {
throw new EncryptedDocumentException(e);
}
}
public DocumentEncryptionAtom() {
_header = new byte[8];
LittleEndian.putShort(_header, 0, (short)0x000F);
LittleEndian.putShort(_header, 2, (short)_type);
// record length not yet known ...
public DocumentEncryptionAtom() {
_header = new byte[8];
LittleEndian.putShort(_header, 0, (short)0x000F);
LittleEndian.putShort(_header, 2, (short)_type);
// record length not yet known ...
ei = new EncryptionInfo(EncryptionMode.cryptoAPI);
}
ei = new EncryptionInfo(EncryptionMode.cryptoAPI);
}
/**
* Initializes the encryption settings
*
* @param keyBits see {@link CipherAlgorithm#rc4} for allowed values, use -1 for default size
*/
public void initializeEncryptionInfo(int keyBits) {
ei = new EncryptionInfo(EncryptionMode.cryptoAPI, CipherAlgorithm.rc4, HashAlgorithm.sha1, keyBits, -1, null);
}
/**
* Initializes the encryption settings
*
* @param keyBits see {@link CipherAlgorithm#rc4} for allowed values, use -1 for default size
*/
public void initializeEncryptionInfo(int keyBits) {
ei = new EncryptionInfo(EncryptionMode.cryptoAPI, CipherAlgorithm.rc4, HashAlgorithm.sha1, keyBits, -1, null);
}
/**
* Return the length of the encryption key, in bits
*/
public int getKeyLength() {
return ei.getHeader().getKeySize();
}
/**
* Return the length of the encryption key, in bits
*/
public int getKeyLength() {
return ei.getHeader().getKeySize();
}
/**
* Return the name of the encryption provider used
*/
public String getEncryptionProviderName() {
return ei.getHeader().getCspName();
}
/**
* Return the name of the encryption provider used
*/
public String getEncryptionProviderName() {
return ei.getHeader().getCspName();
}
/**
* @return the {@link EncryptionInfo} object for details about encryption settings
*/
public EncryptionInfo getEncryptionInfo() {
return ei;
}
/**
* @return the {@link EncryptionInfo} object for details about encryption settings
*/
public EncryptionInfo getEncryptionInfo() {
return ei;
}
/**
* We are of type 12052
*/
public long getRecordType() { return _type; }
/**
* We are of type 12052
*/
public long getRecordType() { return _type; }
/**
* Write the contents of the record back, so it can be written
* to disk
*/
public void writeOut(OutputStream out) throws IOException {
/**
* Write the contents of the record back, so it can be written
* to disk
*/
public void writeOut(OutputStream out) throws IOException {
// Data
// Data
byte[] data = new byte[1024];
LittleEndianByteArrayOutputStream bos = new LittleEndianByteArrayOutputStream(data, 0);
bos.writeShort(ei.getVersionMajor());
bos.writeShort(ei.getVersionMinor());
bos.writeInt(ei.getEncryptionFlags());
LittleEndianByteArrayOutputStream bos = new LittleEndianByteArrayOutputStream(data, 0);
bos.writeShort(ei.getVersionMajor());
bos.writeShort(ei.getVersionMinor());
bos.writeInt(ei.getEncryptionFlags());
((CryptoAPIEncryptionHeader)ei.getHeader()).write(bos);
((CryptoAPIEncryptionVerifier)ei.getVerifier()).write(bos);
((CryptoAPIEncryptionHeader)ei.getHeader()).write(bos);
((CryptoAPIEncryptionVerifier)ei.getVerifier()).write(bos);
// Header
LittleEndian.putInt(_header, 4, bos.getWriteIndex());
LittleEndian.putInt(_header, 4, bos.getWriteIndex());
out.write(_header);
out.write(data, 0, bos.getWriteIndex());
bos.close();
}
out.write(data, 0, bos.getWriteIndex());
bos.close();
}
@Override
public void updateOtherRecordReferences(Map<Integer,Integer> oldToNewReferencesLookup) {
// nothing to update
}
@Override
public Map<String, Supplier<?>> getGenericProperties() {
return GenericRecordUtil.getGenericProperties(
"encryptionInfo", this::getEncryptionInfo
);
}
@Override
public Map<String, Supplier<?>> getGenericProperties() {
return GenericRecordUtil.getGenericProperties(
"encryptionInfo", this::getEncryptionInfo
);
}
}

View File

@ -35,32 +35,32 @@ import org.apache.poi.util.LittleEndian;
public final class DummyPositionSensitiveRecordWithChildren extends PositionDependentRecordContainer
{
private byte[] _header;
private long _type;
private byte[] _header;
private long _type;
/**
* Create a new holder for a boring record with children, but with
* position dependent characteristics
*/
protected DummyPositionSensitiveRecordWithChildren(byte[] source, int start, int len) {
// Just grab the header, not the whole contents
_header = Arrays.copyOfRange(source,start,start+8);
_type = LittleEndian.getUShort(_header,2);
/**
* Create a new holder for a boring record with children, but with
* position dependent characteristics
*/
protected DummyPositionSensitiveRecordWithChildren(byte[] source, int start, int len) {
// Just grab the header, not the whole contents
_header = Arrays.copyOfRange(source,start,start+8);
_type = LittleEndian.getUShort(_header,2);
// Find our children
_children = Record.findChildRecords(source,start+8,len-8);
}
// Find our children
_children = Record.findChildRecords(source,start+8,len-8);
}
/**
* Return the value we were given at creation
*/
public long getRecordType() { return _type; }
/**
* Return the value we were given at creation
*/
public long getRecordType() { return _type; }
/**
* Write the contents of the record back, so it can be written
* to disk
*/
public void writeOut(OutputStream out) throws IOException {
writeOut(_header[0],_header[1],_type,_children,out);
}
/**
* Write the contents of the record back, so it can be written
* to disk
*/
public void writeOut(OutputStream out) throws IOException {
writeOut(_header[0],_header[1],_type,_children,out);
}
}

View File

@ -31,31 +31,31 @@ import org.apache.poi.util.LittleEndian;
public final class DummyRecordWithChildren extends RecordContainer
{
private byte[] _header;
private long _type;
private byte[] _header;
private long _type;
/**
* Create a new holder for a boring record with children
*/
protected DummyRecordWithChildren(byte[] source, int start, int len) {
// Just grab the header, not the whole contents
_header = Arrays.copyOfRange(source, start, start+8);
_type = LittleEndian.getUShort(_header,2);
/**
* Create a new holder for a boring record with children
*/
protected DummyRecordWithChildren(byte[] source, int start, int len) {
// Just grab the header, not the whole contents
_header = Arrays.copyOfRange(source, start, start+8);
_type = LittleEndian.getUShort(_header,2);
// Find our children
_children = Record.findChildRecords(source,start+8,len-8);
}
// Find our children
_children = Record.findChildRecords(source,start+8,len-8);
}
/**
* Return the value we were given at creation
*/
public long getRecordType() { return _type; }
/**
* Return the value we were given at creation
*/
public long getRecordType() { return _type; }
/**
* Write the contents of the record back, so it can be written
* to disk
*/
public void writeOut(OutputStream out) throws IOException {
writeOut(_header[0],_header[1],_type,_children,out);
}
/**
* Write the contents of the record back, so it can be written
* to disk
*/
public void writeOut(OutputStream out) throws IOException {
writeOut(_header[0],_header[1],_type,_children,out);
}
}

View File

@ -27,58 +27,58 @@ import java.util.Arrays;
public final class Environment extends PositionDependentRecordContainer
{
private byte[] _header;
private static long _type = 1010;
private byte[] _header;
private static long _type = 1010;
// Links to our more interesting children
private FontCollection fontCollection;
// Links to our more interesting children
private FontCollection fontCollection;
//master style for text with type=TextHeaderAtom.OTHER_TYPE
private TxMasterStyleAtom txmaster;
/**
* Returns the FontCollection of this Environment
*/
public FontCollection getFontCollection() { return fontCollection; }
/**
* Returns the FontCollection of this Environment
*/
public FontCollection getFontCollection() { return fontCollection; }
/**
* Set things up, and find our more interesting children
*/
protected Environment(byte[] source, int start, int len) {
// Grab the header
_header = Arrays.copyOfRange(source, start, start+8);
/**
* Set things up, and find our more interesting children
*/
protected Environment(byte[] source, int start, int len) {
// Grab the header
_header = Arrays.copyOfRange(source, start, start+8);
// Find our children
_children = Record.findChildRecords(source,start+8,len-8);
// Find our children
_children = Record.findChildRecords(source,start+8,len-8);
// Find our FontCollection record
for(int i=0; i<_children.length; i++) {
if(_children[i] instanceof FontCollection) {
fontCollection = (FontCollection)_children[i];
} else if (_children[i] instanceof TxMasterStyleAtom){
// Find our FontCollection record
for(int i=0; i<_children.length; i++) {
if(_children[i] instanceof FontCollection) {
fontCollection = (FontCollection)_children[i];
} else if (_children[i] instanceof TxMasterStyleAtom){
txmaster = (TxMasterStyleAtom)_children[i];
}
}
}
if(fontCollection == null) {
throw new IllegalStateException("Environment didn't contain a FontCollection record!");
}
}
if(fontCollection == null) {
throw new IllegalStateException("Environment didn't contain a FontCollection record!");
}
}
public TxMasterStyleAtom getTxMasterStyleAtom(){
return txmaster;
}
/**
* We are of type 1010
*/
public long getRecordType() { return _type; }
/**
* We are of type 1010
*/
public long getRecordType() { return _type; }
/**
* Write the contents of the record back, so it can be written
* to disk
*/
public void writeOut(OutputStream out) throws IOException {
writeOut(_header[0],_header[1],_type,_children,out);
}
/**
* Write the contents of the record back, so it can be written
* to disk
*/
public void writeOut(OutputStream out) throws IOException {
writeOut(_header[0],_header[1],_type,_children,out);
}
}

View File

@ -34,99 +34,99 @@ import org.apache.poi.util.GenericRecordUtil;
* parent PPDrawing) will do the actual write out
*/
public final class EscherTextboxWrapper extends RecordContainer {
private final EscherTextboxRecord _escherRecord;
private long _type;
private int shapeId;
private StyleTextPropAtom styleTextPropAtom;
private StyleTextProp9Atom styleTextProp9Atom;
private final EscherTextboxRecord _escherRecord;
private long _type;
private int shapeId;
private StyleTextPropAtom styleTextPropAtom;
private StyleTextProp9Atom styleTextProp9Atom;
/**
* Returns the underlying DDF Escher Record
*/
public EscherTextboxRecord getEscherRecord() { return _escherRecord; }
/**
* Returns the underlying DDF Escher Record
*/
public EscherTextboxRecord getEscherRecord() { return _escherRecord; }
/**
* Creates the wrapper for the given DDF Escher Record and children
*/
public EscherTextboxWrapper(EscherTextboxRecord textbox) {
_escherRecord = textbox;
_type = _escherRecord.getRecordId();
/**
* Creates the wrapper for the given DDF Escher Record and children
*/
public EscherTextboxWrapper(EscherTextboxRecord textbox) {
_escherRecord = textbox;
_type = _escherRecord.getRecordId();
// Find the child records in the escher data
byte[] data = _escherRecord.getData();
_children = Record.findChildRecords(data,0,data.length);
for (org.apache.poi.hslf.record.Record r : this._children) {
if (r instanceof StyleTextPropAtom) { this.styleTextPropAtom = (StyleTextPropAtom) r; }
}
}
// Find the child records in the escher data
byte[] data = _escherRecord.getData();
_children = Record.findChildRecords(data,0,data.length);
for (org.apache.poi.hslf.record.Record r : this._children) {
if (r instanceof StyleTextPropAtom) { this.styleTextPropAtom = (StyleTextPropAtom) r; }
}
}
/**
* Creates a new, empty wrapper for DDF Escher Records and their children
*/
public EscherTextboxWrapper() {
_escherRecord = new EscherTextboxRecord();
_escherRecord.setRecordId(EscherTextboxRecord.RECORD_ID);
_escherRecord.setOptions((short)15);
/**
* Creates a new, empty wrapper for DDF Escher Records and their children
*/
public EscherTextboxWrapper() {
_escherRecord = new EscherTextboxRecord();
_escherRecord.setRecordId(EscherTextboxRecord.RECORD_ID);
_escherRecord.setOptions((short)15);
_children = new org.apache.poi.hslf.record.Record[0];
}
_children = new org.apache.poi.hslf.record.Record[0];
}
/**
* Return the type of the escher record (normally in the 0xFnnn range)
*/
@Override
public long getRecordType() { return _type; }
/**
* Return the type of the escher record (normally in the 0xFnnn range)
*/
@Override
public long getRecordType() { return _type; }
/**
* Stores the data for the child records back into the Escher layer.
* Doesn't actually do the writing out, that's left to the Escher
* layer to do. Must be called before writeOut/serialize is called
* on the underlying Escher object!
*/
@Override
public void writeOut(OutputStream out) throws IOException {
// Write out our children, and stuff them into the Escher layer
/**
* Stores the data for the child records back into the Escher layer.
* Doesn't actually do the writing out, that's left to the Escher
* layer to do. Must be called before writeOut/serialize is called
* on the underlying Escher object!
*/
@Override
public void writeOut(OutputStream out) throws IOException {
// Write out our children, and stuff them into the Escher layer
// Grab the children's data
UnsynchronizedByteArrayOutputStream baos = new UnsynchronizedByteArrayOutputStream();
for (org.apache.poi.hslf.record.Record r : _children) r.writeOut(baos);
byte[] data = baos.toByteArray();
// Grab the children's data
UnsynchronizedByteArrayOutputStream baos = new UnsynchronizedByteArrayOutputStream();
for (org.apache.poi.hslf.record.Record r : _children) r.writeOut(baos);
byte[] data = baos.toByteArray();
// Save in the escher layer
_escherRecord.setData(data);
}
// Save in the escher layer
_escherRecord.setData(data);
}
/**
* @return Shape ID
*/
public int getShapeId(){
return shapeId;
}
/**
* @return Shape ID
*/
public int getShapeId(){
return shapeId;
}
/**
* @param id Shape ID
*/
public void setShapeId(int id){
shapeId = id;
}
/**
* @param id Shape ID
*/
public void setShapeId(int id){
shapeId = id;
}
public StyleTextPropAtom getStyleTextPropAtom() {
return styleTextPropAtom;
}
public StyleTextPropAtom getStyleTextPropAtom() {
return styleTextPropAtom;
}
public void setStyleTextProp9Atom(final StyleTextProp9Atom nineAtom) {
this.styleTextProp9Atom = nineAtom;
}
public StyleTextProp9Atom getStyleTextProp9Atom() {
return this.styleTextProp9Atom;
}
public void setStyleTextProp9Atom(final StyleTextProp9Atom nineAtom) {
this.styleTextProp9Atom = nineAtom;
}
public StyleTextProp9Atom getStyleTextProp9Atom() {
return this.styleTextProp9Atom;
}
@Override
public Map<String, Supplier<?>> getGenericProperties() {
return GenericRecordUtil.getGenericProperties(
"shapeId", this::getShapeId,
"escherRecord", this::getEscherRecord
);
}
@Override
public Map<String, Supplier<?>> getGenericProperties() {
return GenericRecordUtil.getGenericProperties(
"shapeId", this::getShapeId,
"escherRecord", this::getEscherRecord
);
}
}

View File

@ -100,7 +100,7 @@ public class ExEmbedAtom extends RecordAtom {
// Must be at least 8 bytes long
if(_data.length < 8) {
throw new IllegalArgumentException("The length of the data for a ExEmbedAtom must be at least 4 bytes, but was only " + _data.length);
throw new IllegalArgumentException("The length of the data for a ExEmbedAtom must be at least 4 bytes, but was only " + _data.length);
}
}
@ -125,7 +125,7 @@ public class ExEmbedAtom extends RecordAtom {
}
public void setCantLockServerB(boolean cantBeLocked) {
_data[4] = (byte)(cantBeLocked ? 1 : 0);
_data[4] = (byte)(cantBeLocked ? 1 : 0);
}
/**

View File

@ -28,28 +28,28 @@ import static org.apache.logging.log4j.util.Unbox.box;
* This class represents the data of a link in the document.
*/
public class ExHyperlink extends RecordContainer {
private static final long _type = RecordTypes.ExHyperlink.typeID;
private static final long _type = RecordTypes.ExHyperlink.typeID;
private byte[] _header;
private byte[] _header;
// Links to our more interesting children
private ExHyperlinkAtom linkAtom;
private CString linkDetailsA;
private CString linkDetailsB;
// Links to our more interesting children
private ExHyperlinkAtom linkAtom;
private CString linkDetailsA;
private CString linkDetailsB;
/**
* Returns the ExHyperlinkAtom of this link
*/
public ExHyperlinkAtom getExHyperlinkAtom() { return linkAtom; }
/**
* Returns the ExHyperlinkAtom of this link
*/
public ExHyperlinkAtom getExHyperlinkAtom() { return linkAtom; }
/**
* Returns the URL of the link.
/**
* Returns the URL of the link.
*
* @return the URL of the link
*/
public String getLinkURL() {
return linkDetailsB == null ? null : linkDetailsB.getText();
}
*/
public String getLinkURL() {
return linkDetailsB == null ? null : linkDetailsB.getText();
}
/**
* Returns the hyperlink's user-readable name
@ -60,15 +60,15 @@ public class ExHyperlink extends RecordContainer {
return linkDetailsA == null ? null : linkDetailsA.getText();
}
/**
* Sets the URL of the link
* TODO: Figure out if we should always set both
*/
public void setLinkURL(String url) {
if(linkDetailsB != null) {
linkDetailsB.setText(url);
}
}
/**
* Sets the URL of the link
* TODO: Figure out if we should always set both
*/
public void setLinkURL(String url) {
if(linkDetailsB != null) {
linkDetailsB.setText(url);
}
}
public void setLinkOptions(int options) {
if(linkDetailsB != null) {
@ -82,49 +82,49 @@ public class ExHyperlink extends RecordContainer {
}
}
/**
* Get the link details (field A)
*/
public String _getDetailsA() {
return linkDetailsA == null ? null : linkDetailsA.getText();
}
/**
* Get the link details (field B)
*/
public String _getDetailsB() {
return linkDetailsB == null ? null : linkDetailsB.getText();
}
/**
* Get the link details (field A)
*/
public String _getDetailsA() {
return linkDetailsA == null ? null : linkDetailsA.getText();
}
/**
* Get the link details (field B)
*/
public String _getDetailsB() {
return linkDetailsB == null ? null : linkDetailsB.getText();
}
/**
* Set things up, and find our more interesting children
*/
protected ExHyperlink(byte[] source, int start, int len) {
// Grab the header
_header = Arrays.copyOfRange(source, start, start+8);
/**
* Set things up, and find our more interesting children
*/
protected ExHyperlink(byte[] source, int start, int len) {
// Grab the header
_header = Arrays.copyOfRange(source, start, start+8);
// Find our children
_children = Record.findChildRecords(source,start+8,len-8);
findInterestingChildren();
}
// Find our children
_children = Record.findChildRecords(source,start+8,len-8);
findInterestingChildren();
}
/**
* Go through our child records, picking out the ones that are
* interesting, and saving those for use by the easy helper
* methods.
*/
private void findInterestingChildren() {
/**
* Go through our child records, picking out the ones that are
* interesting, and saving those for use by the easy helper
* methods.
*/
private void findInterestingChildren() {
// First child should be the ExHyperlinkAtom
Record child = _children[0];
if(child instanceof ExHyperlinkAtom) {
linkAtom = (ExHyperlinkAtom) child;
} else {
LOG.atError().log("First child record wasn't a ExHyperlinkAtom, was of type {}", box(child.getRecordType()));
}
// First child should be the ExHyperlinkAtom
Record child = _children[0];
if(child instanceof ExHyperlinkAtom) {
linkAtom = (ExHyperlinkAtom) child;
} else {
LOG.atError().log("First child record wasn't a ExHyperlinkAtom, was of type {}", box(child.getRecordType()));
}
for (int i = 1; i < _children.length; i++) {
child = _children[i];
if (child instanceof CString){
child = _children[i];
if (child instanceof CString){
if ( linkDetailsA == null) linkDetailsA = (CString) child;
else linkDetailsB = (CString) child;
} else {
@ -132,40 +132,40 @@ public class ExHyperlink extends RecordContainer {
}
}
}
}
/**
* Create a new ExHyperlink, with blank fields
*/
public ExHyperlink() {
_header = new byte[8];
_children = new org.apache.poi.hslf.record.Record[3];
/**
* Create a new ExHyperlink, with blank fields
*/
public ExHyperlink() {
_header = new byte[8];
_children = new org.apache.poi.hslf.record.Record[3];
// Setup our header block
_header[0] = 0x0f; // We are a container record
LittleEndian.putShort(_header, 2, (short)_type);
// Setup our header block
_header[0] = 0x0f; // We are a container record
LittleEndian.putShort(_header, 2, (short)_type);
// Setup our child records
CString csa = new CString();
CString csb = new CString();
csa.setOptions(0x00);
csb.setOptions(0x10);
_children[0] = new ExHyperlinkAtom();
_children[1] = csa;
_children[2] = csb;
findInterestingChildren();
}
// Setup our child records
CString csa = new CString();
CString csb = new CString();
csa.setOptions(0x00);
csb.setOptions(0x10);
_children[0] = new ExHyperlinkAtom();
_children[1] = csa;
_children[2] = csb;
findInterestingChildren();
}
/**
* We are of type 4055
*/
public long getRecordType() { return _type; }
/**
* We are of type 4055
*/
public long getRecordType() { return _type; }
/**
* Write the contents of the record back, so it can be written
* to disk
*/
public void writeOut(OutputStream out) throws IOException {
writeOut(_header[0],_header[1],_type,_children,out);
}
/**
* Write the contents of the record back, so it can be written
* to disk
*/
public void writeOut(OutputStream out) throws IOException {
writeOut(_header[0],_header[1],_type,_children,out);
}
}

View File

@ -76,7 +76,7 @@ public final class ExHyperlinkAtom extends RecordAtom {
// Must be at least 4 bytes long
if(_data.length < 4) {
throw new IllegalArgumentException("The length of the data for a ExHyperlinkAtom must be at least 4 bytes, but was only " + _data.length);
throw new IllegalArgumentException("The length of the data for a ExHyperlinkAtom must be at least 4 bytes, but was only " + _data.length);
}
}

View File

@ -27,85 +27,85 @@ import org.apache.poi.util.LittleEndian;
* This class holds the links to exernal objects referenced from the document.
*/
public class ExObjList extends RecordContainer {
private byte[] _header;
private static final long _type = RecordTypes.ExObjList.typeID;
private byte[] _header;
private static final long _type = RecordTypes.ExObjList.typeID;
// Links to our more interesting children
private ExObjListAtom exObjListAtom;
// Links to our more interesting children
private ExObjListAtom exObjListAtom;
/**
* Returns the ExObjListAtom of this list
*/
public ExObjListAtom getExObjListAtom() { return exObjListAtom; }
/**
* Returns the ExObjListAtom of this list
*/
public ExObjListAtom getExObjListAtom() { return exObjListAtom; }
/**
* Returns all the ExHyperlinks
*/
public ExHyperlink[] getExHyperlinks() {
ArrayList<ExHyperlink> links = new ArrayList<>();
for(int i=0; i<_children.length; i++) {
if(_children[i] instanceof ExHyperlink) {
links.add( (ExHyperlink)_children[i] );
}
}
/**
* Returns all the ExHyperlinks
*/
public ExHyperlink[] getExHyperlinks() {
ArrayList<ExHyperlink> links = new ArrayList<>();
for(int i=0; i<_children.length; i++) {
if(_children[i] instanceof ExHyperlink) {
links.add( (ExHyperlink)_children[i] );
}
}
return links.toArray(new ExHyperlink[0]);
}
return links.toArray(new ExHyperlink[0]);
}
/**
* Set things up, and find our more interesting children
*/
protected ExObjList(byte[] source, int start, int len) {
// Grab the header
_header = Arrays.copyOfRange(source, start, start+8);
/**
* Set things up, and find our more interesting children
*/
protected ExObjList(byte[] source, int start, int len) {
// Grab the header
_header = Arrays.copyOfRange(source, start, start+8);
// Find our children
_children = Record.findChildRecords(source,start+8,len-8);
findInterestingChildren();
}
// Find our children
_children = Record.findChildRecords(source,start+8,len-8);
findInterestingChildren();
}
/**
* Go through our child records, picking out the ones that are
* interesting, and saving those for use by the easy helper
* methods.
*/
private void findInterestingChildren() {
// First child should be the atom
if(_children[0] instanceof ExObjListAtom) {
exObjListAtom = (ExObjListAtom)_children[0];
} else {
throw new IllegalStateException("First child record wasn't a ExObjListAtom, was of type " + _children[0].getRecordType());
}
}
/**
* Go through our child records, picking out the ones that are
* interesting, and saving those for use by the easy helper
* methods.
*/
private void findInterestingChildren() {
// First child should be the atom
if(_children[0] instanceof ExObjListAtom) {
exObjListAtom = (ExObjListAtom)_children[0];
} else {
throw new IllegalStateException("First child record wasn't a ExObjListAtom, was of type " + _children[0].getRecordType());
}
}
/**
* Create a new ExObjList, with blank fields
*/
public ExObjList() {
_header = new byte[8];
_children = new org.apache.poi.hslf.record.Record[1];
/**
* Create a new ExObjList, with blank fields
*/
public ExObjList() {
_header = new byte[8];
_children = new org.apache.poi.hslf.record.Record[1];
// Setup our header block
_header[0] = 0x0f; // We are a container record
LittleEndian.putShort(_header, 2, (short)_type);
// Setup our header block
_header[0] = 0x0f; // We are a container record
LittleEndian.putShort(_header, 2, (short)_type);
// Setup our child records
_children[0] = new ExObjListAtom();
findInterestingChildren();
}
// Setup our child records
_children[0] = new ExObjListAtom();
findInterestingChildren();
}
/**
* We are of type 1033
*/
public long getRecordType() { return _type; }
/**
* We are of type 1033
*/
public long getRecordType() { return _type; }
/**
* Write the contents of the record back, so it can be written
* to disk
*/
public void writeOut(OutputStream out) throws IOException {
writeOut(_header[0],_header[1],_type,_children,out);
}
/**
* Write the contents of the record back, so it can be written
* to disk
*/
public void writeOut(OutputStream out) throws IOException {
writeOut(_header[0],_header[1],_type,_children,out);
}
/**
* Lookup a hyperlink by its unique id

View File

@ -78,7 +78,7 @@ public class ExObjListAtom extends RecordAtom
// Must be at least 4 bytes long
if(_data.length < 4) {
throw new IllegalArgumentException("The length of the data for a ExObjListAtom must be at least 4 bytes, but was only " + _data.length);
throw new IllegalArgumentException("The length of the data for a ExObjListAtom must be at least 4 bytes, but was only " + _data.length);
}
}

View File

@ -34,14 +34,14 @@ import org.apache.poi.util.LittleEndian;
*/
public final class ExObjRefAtom extends RecordAtom {
private byte[] _header;
private byte[] _header;
/**
* A 4-byte unsigned integer that specifies a reference to an external object.
* It MUST be equal to the value of the exObjId field of an ExMediaAtom record
* or the value of the exObjId field of an ExOleObjAtom record.
*/
private int exObjIdRef;
/**
* A 4-byte unsigned integer that specifies a reference to an external object.
* It MUST be equal to the value of the exObjId field of an ExMediaAtom record
* or the value of the exObjId field of an ExOleObjAtom record.
*/
private int exObjIdRef;
/**
* Create a new instance of <code>ExObjRefAtom</code>
@ -61,16 +61,16 @@ public final class ExObjRefAtom extends RecordAtom {
* @param start the start offset into the byte array.
* @param len the length of the slice in the byte array.
*/
protected ExObjRefAtom(byte[] source, int start, int len) {
protected ExObjRefAtom(byte[] source, int start, int len) {
_header = Arrays.copyOfRange(source, start, start+8);
exObjIdRef = (int)LittleEndian.getUInt(source, start+8);
}
}
/**
* @return type of this record {@link RecordTypes#ExObjRefAtom}.
*/
public long getRecordType() {
return RecordTypes.ExObjRefAtom.typeID;
public long getRecordType() {
return RecordTypes.ExObjRefAtom.typeID;
}
public int getExObjIdRef(){
@ -82,17 +82,17 @@ public final class ExObjRefAtom extends RecordAtom {
}
/**
* Write the contents of the record back, so it can be written
* to disk
*/
public void writeOut(OutputStream out) throws IOException {
out.write(_header);
* Write the contents of the record back, so it can be written
* to disk
*/
public void writeOut(OutputStream out) throws IOException {
out.write(_header);
byte[] recdata = new byte[4];
LittleEndian.putUInt(recdata, 0, exObjIdRef);
out.write(recdata);
}
}
@Override
public Map<String, Supplier<?>> getGenericProperties() {

View File

@ -195,7 +195,7 @@ public class ExOleObjAtom extends RecordAtom {
// Must be at least 24 bytes long
if(_data.length < 24) {
throw new IllegalArgumentException("The length of the data for a ExOleObjAtom must be at least 24 bytes, but was only " + _data.length);
throw new IllegalArgumentException("The length of the data for a ExOleObjAtom must be at least 24 bytes, but was only " + _data.length);
}
}

View File

@ -29,73 +29,73 @@ import static org.apache.logging.log4j.util.Unbox.box;
* A container record that specifies information about external video data.
*/
public final class ExVideoContainer extends RecordContainer {
private byte[] _header;
private byte[] _header;
// Links to our more interesting children
private ExMediaAtom mediaAtom;
// Links to our more interesting children
private ExMediaAtom mediaAtom;
//the UNC or local path to a video file.
private CString pathAtom;
/**
* Set things up, and find our more interesting children
*/
protected ExVideoContainer(byte[] source, int start, int len) {
// Grab the header
_header = Arrays.copyOfRange(source, start, start+8);
/**
* Set things up, and find our more interesting children
*/
protected ExVideoContainer(byte[] source, int start, int len) {
// Grab the header
_header = Arrays.copyOfRange(source, start, start+8);
// Find our children
_children = Record.findChildRecords(source,start+8,len-8);
findInterestingChildren();
}
// Find our children
_children = Record.findChildRecords(source,start+8,len-8);
findInterestingChildren();
}
/**
* Go through our child records, picking out the ones that are
* interesting, and saving those for use by the easy helper
* methods.
*/
private void findInterestingChildren() {
/**
* Go through our child records, picking out the ones that are
* interesting, and saving those for use by the easy helper
* methods.
*/
private void findInterestingChildren() {
// First child should be the ExMediaAtom
Record child = _children[0];
if(child instanceof ExMediaAtom) {
mediaAtom = (ExMediaAtom) child;
} else {
LOG.atError().log("First child record wasn't a ExMediaAtom, was of type {}", box(child.getRecordType()));
}
child = _children[1];
if(child instanceof CString) {
// First child should be the ExMediaAtom
Record child = _children[0];
if(child instanceof ExMediaAtom) {
mediaAtom = (ExMediaAtom) child;
} else {
LOG.atError().log("First child record wasn't a ExMediaAtom, was of type {}", box(child.getRecordType()));
}
child = _children[1];
if(child instanceof CString) {
pathAtom = (CString) child;
} else {
LOG.atError().log("Second child record wasn't a CString, was of type {}", box(child.getRecordType()));
}
}
}
/**
* Create a new ExVideoContainer, with blank fields
*/
public ExVideoContainer() {
/**
* Create a new ExVideoContainer, with blank fields
*/
public ExVideoContainer() {
// Setup our header block
_header = new byte[8];
_header[0] = 0x0f; // We are a container record
LittleEndian.putShort(_header, 2, (short)getRecordType());
_header = new byte[8];
_header[0] = 0x0f; // We are a container record
LittleEndian.putShort(_header, 2, (short)getRecordType());
_children = new org.apache.poi.hslf.record.Record[2];
_children[0] = mediaAtom = new ExMediaAtom();
_children[1] = pathAtom = new CString();
}
_children[0] = mediaAtom = new ExMediaAtom();
_children[1] = pathAtom = new CString();
}
/**
* We are of type 4103
*/
public long getRecordType() { return RecordTypes.ExVideoContainer.typeID; }
/**
* We are of type 4103
*/
public long getRecordType() { return RecordTypes.ExVideoContainer.typeID; }
/**
* Write the contents of the record back, so it can be written
* to disk
*/
public void writeOut(OutputStream out) throws IOException {
writeOut(_header[0],_header[1],getRecordType(),_children,out);
}
/**
* Write the contents of the record back, so it can be written
* to disk
*/
public void writeOut(OutputStream out) throws IOException {
writeOut(_header[0],_header[1],getRecordType(),_children,out);
}
/**
* Returns the ExMediaAtom of this link

View File

@ -46,42 +46,42 @@ public final class FontCollection extends RecordContainer {
private final Map<Integer,HSLFFontInfo> fonts = new LinkedHashMap<>();
private byte[] _header;
/* package */ FontCollection(byte[] source, int start, int len) {
/* package */ FontCollection(byte[] source, int start, int len) {
_header = Arrays.copyOfRange(source, start, start+8);
_children = Record.findChildRecords(source,start+8,len-8);
_children = Record.findChildRecords(source,start+8,len-8);
for (org.apache.poi.hslf.record.Record r : _children){
if(r instanceof FontEntityAtom) {
for (org.apache.poi.hslf.record.Record r : _children){
if(r instanceof FontEntityAtom) {
HSLFFontInfo fi = new HSLFFontInfo((FontEntityAtom) r);
fonts.put(fi.getIndex(), fi);
} else if (r instanceof FontEmbeddedData) {
FontEmbeddedData fed = (FontEmbeddedData)r;
FontHeader fontHeader = fed.getFontHeader();
HSLFFontInfo fi = addFont(fontHeader);
fi.addFacet(fed);
} else {
LOG.atWarn().log("FontCollection child wasn't a FontEntityAtom, was {}", r.getClass().getSimpleName());
}
}
}
FontHeader fontHeader = fed.getFontHeader();
HSLFFontInfo fi = addFont(fontHeader);
fi.addFacet(fed);
} else {
LOG.atWarn().log("FontCollection child wasn't a FontEntityAtom, was {}", r.getClass().getSimpleName());
}
}
}
/**
* Return the type, which is 2005
*/
@Override
/**
* Return the type, which is 2005
*/
@Override
public long getRecordType() {
return RecordTypes.FontCollection.typeID;
}
/**
* Write the contents of the record back, so it can be written
* to disk
*/
@Override
/**
* Write the contents of the record back, so it can be written
* to disk
*/
@Override
public void writeOut(OutputStream out) throws IOException {
writeOut(_header[0],_header[1],getRecordType(),_children,out);
}
writeOut(_header[0],_header[1],getRecordType(),_children,out);
}
/**
* Add font with the given FontInfo configuration to the font collection.

View File

@ -62,21 +62,21 @@ public final class FontEntityAtom extends RecordAtom {
*/
private final byte[] _header;
/**
/**
* record data
*/
private byte[] _recdata;
private byte[] _recdata;
/**
* Build an instance of <code>FontEntityAtom</code> from on-disk data
*/
/* package */ FontEntityAtom(byte[] source, int start, int len) {
// Get the header
/* package */ FontEntityAtom(byte[] source, int start, int len) {
// Get the header
_header = Arrays.copyOfRange(source, start, start+8);
// Grab the record data
_recdata = IOUtils.safelyClone(source, start+8, len-8, MAX_RECORD_LENGTH);
}
// Grab the record data
_recdata = IOUtils.safelyClone(source, start+8, len-8, MAX_RECORD_LENGTH);
}
/**
* Create a new instance of <code>FontEntityAtom</code>
@ -97,19 +97,19 @@ public final class FontEntityAtom extends RecordAtom {
/**
* A null-terminated string that specifies the typeface name of the font.
* The length of this string must not exceed 32 characters
* including the null terminator.
* including the null terminator.
* @return font name
*/
public String getFontName(){
final int maxLen = Math.min(_recdata.length,64)/2;
return StringUtil.getFromUnicodeLE0Terminated(_recdata, 0, maxLen);
final int maxLen = Math.min(_recdata.length,64)/2;
return StringUtil.getFromUnicodeLE0Terminated(_recdata, 0, maxLen);
}
/**
* Set the name of the font.
* The length of this string must not exceed 32 characters
* including the null terminator.
* Will be converted to null-terminated if not already
* including the null terminator.
* Will be converted to null-terminated if not already
* @param name of the font
*/
public void setFontName(String name) {
@ -220,13 +220,13 @@ public final class FontEntityAtom extends RecordAtom {
}
/**
* Write the contents of the record back, so it can be written to disk
*/
@Override
* Write the contents of the record back, so it can be written to disk
*/
@Override
public void writeOut(OutputStream out) throws IOException {
out.write(_header);
out.write(_recdata);
}
out.write(_header);
out.write(_recdata);
}
@Override
public Map<String, Supplier<?>> getGenericProperties() {

View File

@ -131,21 +131,21 @@ public final class HeadersFootersAtom extends RecordAtom {
*/
private final byte[] _header;
/**
/**
* record data
*/
private final byte[] _recdata;
private final byte[] _recdata;
/**
* Build an instance of {@code HeadersFootersAtom} from on-disk data
*/
protected HeadersFootersAtom(byte[] source, int start, int len) {
// Get the header
protected HeadersFootersAtom(byte[] source, int start, int len) {
// Get the header
_header = Arrays.copyOfRange(source, start, start+8);
// Grab the record data
_recdata = IOUtils.safelyClone(source, start+8, len-8, MAX_RECORD_LENGTH);
}
// Grab the record data
_recdata = IOUtils.safelyClone(source, start+8, len-8, MAX_RECORD_LENGTH);
}
/**
* Create a new instance of {@code HeadersFootersAtom}
@ -164,13 +164,13 @@ public final class HeadersFootersAtom extends RecordAtom {
}
/**
* Write the contents of the record back, so it can be written to disk
*/
@Override
* Write the contents of the record back, so it can be written to disk
*/
@Override
public void writeOut(OutputStream out) throws IOException {
out.write(_header);
out.write(_recdata);
}
out.write(_header);
out.write(_recdata);
}
/**
* A signed integer that specifies the format ID to be used to style the datetime.

View File

@ -28,70 +28,70 @@ import org.apache.poi.util.LittleEndian;
* in it.
*/
public class InteractiveInfo extends RecordContainer {
private byte[] _header;
private static final long _type = RecordTypes.InteractiveInfo.typeID;
private byte[] _header;
private static final long _type = RecordTypes.InteractiveInfo.typeID;
// Links to our more interesting children
private InteractiveInfoAtom infoAtom;
// Links to our more interesting children
private InteractiveInfoAtom infoAtom;
/**
* Returns the InteractiveInfoAtom of this InteractiveInfo
*/
public InteractiveInfoAtom getInteractiveInfoAtom() { return infoAtom; }
/**
* Returns the InteractiveInfoAtom of this InteractiveInfo
*/
public InteractiveInfoAtom getInteractiveInfoAtom() { return infoAtom; }
/**
* Set things up, and find our more interesting children
*/
protected InteractiveInfo(byte[] source, int start, int len) {
// Grab the header
_header = Arrays.copyOfRange(source, start, start+8);
/**
* Set things up, and find our more interesting children
*/
protected InteractiveInfo(byte[] source, int start, int len) {
// Grab the header
_header = Arrays.copyOfRange(source, start, start+8);
// Find our children
_children = Record.findChildRecords(source,start+8,len-8);
findInterestingChildren();
}
// Find our children
_children = Record.findChildRecords(source,start+8,len-8);
findInterestingChildren();
}
/**
* Go through our child records, picking out the ones that are
* interesting, and saving those for use by the easy helper
* methods.
*/
private void findInterestingChildren() {
// First child should be the InteractiveInfoAtom
if (_children == null || _children.length == 0 || !(_children[0] instanceof InteractiveInfoAtom)) {
LOG.atWarn().log("First child record wasn't a InteractiveInfoAtom - leaving this atom in an invalid state...");
return;
}
/**
* Go through our child records, picking out the ones that are
* interesting, and saving those for use by the easy helper
* methods.
*/
private void findInterestingChildren() {
// First child should be the InteractiveInfoAtom
if (_children == null || _children.length == 0 || !(_children[0] instanceof InteractiveInfoAtom)) {
LOG.atWarn().log("First child record wasn't a InteractiveInfoAtom - leaving this atom in an invalid state...");
return;
}
infoAtom = (InteractiveInfoAtom)_children[0];
}
infoAtom = (InteractiveInfoAtom)_children[0];
}
/**
* Create a new InteractiveInfo, with blank fields
*/
public InteractiveInfo() {
_header = new byte[8];
_children = new org.apache.poi.hslf.record.Record[1];
/**
* Create a new InteractiveInfo, with blank fields
*/
public InteractiveInfo() {
_header = new byte[8];
_children = new org.apache.poi.hslf.record.Record[1];
// Setup our header block
_header[0] = 0x0f; // We are a container record
LittleEndian.putShort(_header, 2, (short)_type);
// Setup our header block
_header[0] = 0x0f; // We are a container record
LittleEndian.putShort(_header, 2, (short)_type);
// Setup our child records
infoAtom = new InteractiveInfoAtom();
_children[0] = infoAtom;
}
// Setup our child records
infoAtom = new InteractiveInfoAtom();
_children[0] = infoAtom;
}
/**
* We are of type 4802
*/
public long getRecordType() { return _type; }
/**
* We are of type 4802
*/
public long getRecordType() { return _type; }
/**
* Write the contents of the record back, so it can be written
* to disk
*/
public void writeOut(OutputStream out) throws IOException {
writeOut(_header[0],_header[1],_type,_children,out);
}
/**
* Write the contents of the record back, so it can be written
* to disk
*/
public void writeOut(OutputStream out) throws IOException {
writeOut(_header[0],_header[1],_type,_children,out);
}
}

View File

@ -158,7 +158,7 @@ public class InteractiveInfoAtom extends RecordAtom {
// Must be at least 16 bytes long
if(_data.length < 16) {
throw new IllegalArgumentException("The length of the data for a InteractiveInfoAtom must be at least 16 bytes, but was only " + _data.length);
throw new IllegalArgumentException("The length of the data for a InteractiveInfoAtom must be at least 16 bytes, but was only " + _data.length);
}
// First 4 bytes - no idea, normally 0
@ -197,7 +197,7 @@ public class InteractiveInfoAtom extends RecordAtom {
* @param val a reference to a sound in the sound collection
*/
public void setSoundRef(int val) {
LittleEndian.putInt(_data, 0, val);
LittleEndian.putInt(_data, 0, val);
}
/**
@ -221,7 +221,7 @@ public class InteractiveInfoAtom extends RecordAtom {
* @param val hyperlink action.
*/
public void setAction(byte val) {
_data[8] = val;
_data[8] = val;
}
/**
@ -235,7 +235,7 @@ public class InteractiveInfoAtom extends RecordAtom {
* Only valid when action == OLEAction. OLE verb to use, 0 = first verb, 1 = second verb, etc.
*/
public void setOleVerb(byte val) {
_data[9] = val;
_data[9] = val;
}
/**
@ -259,7 +259,7 @@ public class InteractiveInfoAtom extends RecordAtom {
* @param val jump
*/
public void setJump(byte val) {
_data[10] = val;
_data[10] = val;
}
/**
@ -285,7 +285,7 @@ public class InteractiveInfoAtom extends RecordAtom {
* </ul>
*/
public void setFlags(byte val) {
_data[11] = val;
_data[11] = val;
}
/**
@ -303,7 +303,7 @@ public class InteractiveInfoAtom extends RecordAtom {
* @param val hyperlink type
*/
public void setHyperlinkType(byte val) {
_data[12] = val;
_data[12] = val;
}
/**

View File

@ -26,78 +26,78 @@ import java.util.Arrays;
* Master slide
*/
public final class MainMaster extends SheetContainer {
private byte[] _header;
private static long _type = 1016;
private byte[] _header;
private static long _type = 1016;
// Links to our more interesting children
private SlideAtom slideAtom;
private PPDrawing ppDrawing;
private TxMasterStyleAtom[] txmasters;
private ColorSchemeAtom[] clrscheme;
private ColorSchemeAtom _colorScheme;
// Links to our more interesting children
private SlideAtom slideAtom;
private PPDrawing ppDrawing;
private TxMasterStyleAtom[] txmasters;
private ColorSchemeAtom[] clrscheme;
private ColorSchemeAtom _colorScheme;
/**
* Returns the SlideAtom of this Slide
*/
public SlideAtom getSlideAtom() { return slideAtom; }
/**
* Returns the SlideAtom of this Slide
*/
public SlideAtom getSlideAtom() { return slideAtom; }
/**
* Returns the PPDrawing of this Slide, which has all the
* interesting data in it
*/
public PPDrawing getPPDrawing() { return ppDrawing; }
/**
* Returns the PPDrawing of this Slide, which has all the
* interesting data in it
*/
public PPDrawing getPPDrawing() { return ppDrawing; }
public TxMasterStyleAtom[] getTxMasterStyleAtoms() { return txmasters; }
public TxMasterStyleAtom[] getTxMasterStyleAtoms() { return txmasters; }
public ColorSchemeAtom[] getColorSchemeAtoms() { return clrscheme; }
public ColorSchemeAtom[] getColorSchemeAtoms() { return clrscheme; }
/**
* Set things up, and find our more interesting children
*/
protected MainMaster(byte[] source, int start, int len) {
// Grab the header
_header = Arrays.copyOfRange(source, start, start+8);
/**
* Set things up, and find our more interesting children
*/
protected MainMaster(byte[] source, int start, int len) {
// Grab the header
_header = Arrays.copyOfRange(source, start, start+8);
// Find our children
_children = Record.findChildRecords(source,start+8,len-8);
// Find our children
_children = Record.findChildRecords(source,start+8,len-8);
ArrayList<TxMasterStyleAtom> tx = new ArrayList<>();
ArrayList<ColorSchemeAtom> clr = new ArrayList<>();
// Find the interesting ones in there
for (Record child : _children) {
if (child instanceof SlideAtom) {
slideAtom = (SlideAtom) child;
} else if (child instanceof PPDrawing) {
ppDrawing = (PPDrawing) child;
} else if (child instanceof TxMasterStyleAtom) {
tx.add((TxMasterStyleAtom) child);
} else if (child instanceof ColorSchemeAtom) {
clr.add((ColorSchemeAtom) child);
}
ArrayList<TxMasterStyleAtom> tx = new ArrayList<>();
ArrayList<ColorSchemeAtom> clr = new ArrayList<>();
// Find the interesting ones in there
for (Record child : _children) {
if (child instanceof SlideAtom) {
slideAtom = (SlideAtom) child;
} else if (child instanceof PPDrawing) {
ppDrawing = (PPDrawing) child;
} else if (child instanceof TxMasterStyleAtom) {
tx.add((TxMasterStyleAtom) child);
} else if (child instanceof ColorSchemeAtom) {
clr.add((ColorSchemeAtom) child);
}
if (ppDrawing != null && child instanceof ColorSchemeAtom) {
_colorScheme = (ColorSchemeAtom) child;
}
if (ppDrawing != null && child instanceof ColorSchemeAtom) {
_colorScheme = (ColorSchemeAtom) child;
}
}
txmasters = tx.toArray(new TxMasterStyleAtom[0]);
clrscheme = clr.toArray(new ColorSchemeAtom[0]);
}
}
txmasters = tx.toArray(new TxMasterStyleAtom[0]);
clrscheme = clr.toArray(new ColorSchemeAtom[0]);
}
/**
* We are of type 1016
*/
public long getRecordType() { return _type; }
/**
* We are of type 1016
*/
public long getRecordType() { return _type; }
/**
* Write the contents of the record back, so it can be written
* to disk
*/
public void writeOut(OutputStream out) throws IOException {
writeOut(_header[0],_header[1],_type,_children,out);
}
/**
* Write the contents of the record back, so it can be written
* to disk
*/
public void writeOut(OutputStream out) throws IOException {
writeOut(_header[0],_header[1],_type,_children,out);
}
public ColorSchemeAtom getColorScheme(){
return _colorScheme;
}
public ColorSchemeAtom getColorScheme(){
return _colorScheme;
}
}

View File

@ -28,62 +28,62 @@ import java.util.Arrays;
public final class Notes extends SheetContainer
{
private byte[] _header;
private static long _type = 1008l;
private byte[] _header;
private static long _type = 1008l;
// Links to our more interesting children
private NotesAtom notesAtom;
private PPDrawing ppDrawing;
// Links to our more interesting children
private NotesAtom notesAtom;
private PPDrawing ppDrawing;
private ColorSchemeAtom _colorScheme;
/**
* Returns the NotesAtom of this Notes
*/
public NotesAtom getNotesAtom() { return notesAtom; }
/**
* Returns the PPDrawing of this Notes, which has all the
* interesting data in it
*/
public PPDrawing getPPDrawing() { return ppDrawing; }
/**
* Returns the NotesAtom of this Notes
*/
public NotesAtom getNotesAtom() { return notesAtom; }
/**
* Returns the PPDrawing of this Notes, which has all the
* interesting data in it
*/
public PPDrawing getPPDrawing() { return ppDrawing; }
/**
* Set things up, and find our more interesting children
*/
protected Notes(byte[] source, int start, int len) {
// Grab the header
_header = Arrays.copyOfRange(source, start, start+8);
/**
* Set things up, and find our more interesting children
*/
protected Notes(byte[] source, int start, int len) {
// Grab the header
_header = Arrays.copyOfRange(source, start, start+8);
// Find our children
_children = Record.findChildRecords(source,start+8,len-8);
// Find our children
_children = Record.findChildRecords(source,start+8,len-8);
// Find the interesting ones in there
for (Record child : _children) {
if (child instanceof NotesAtom) {
notesAtom = (NotesAtom) child;
}
if (child instanceof PPDrawing) {
ppDrawing = (PPDrawing) child;
}
if (ppDrawing != null && child instanceof ColorSchemeAtom) {
_colorScheme = (ColorSchemeAtom) child;
}
}
}
// Find the interesting ones in there
for (Record child : _children) {
if (child instanceof NotesAtom) {
notesAtom = (NotesAtom) child;
}
if (child instanceof PPDrawing) {
ppDrawing = (PPDrawing) child;
}
if (ppDrawing != null && child instanceof ColorSchemeAtom) {
_colorScheme = (ColorSchemeAtom) child;
}
}
}
/**
* We are of type 1008
*/
public long getRecordType() { return _type; }
/**
* We are of type 1008
*/
public long getRecordType() { return _type; }
/**
* Write the contents of the record back, so it can be written
* to disk
*/
public void writeOut(OutputStream out) throws IOException {
writeOut(_header[0],_header[1],_type,_children,out);
}
/**
* Write the contents of the record back, so it can be written
* to disk
*/
public void writeOut(OutputStream out) throws IOException {
writeOut(_header[0],_header[1],_type,_children,out);
}
public ColorSchemeAtom getColorScheme(){
return _colorScheme;

View File

@ -35,89 +35,89 @@ import org.apache.poi.util.LittleEndian;
public final class NotesAtom extends RecordAtom
{
//arbitrarily selected; may need to increase
private static final int MAX_RECORD_LENGTH = 1_000_000;
//arbitrarily selected; may need to increase
private static final int MAX_RECORD_LENGTH = 1_000_000;
private byte[] _header;
private static long _type = 1009l;
private byte[] _header;
private static long _type = 1009l;
private int slideID;
private boolean followMasterObjects;
private boolean followMasterScheme;
private boolean followMasterBackground;
private byte[] reserved;
private int slideID;
private boolean followMasterObjects;
private boolean followMasterScheme;
private boolean followMasterBackground;
private byte[] reserved;
public int getSlideID() { return slideID; }
public void setSlideID(int id) { slideID = id; }
public int getSlideID() { return slideID; }
public void setSlideID(int id) { slideID = id; }
public boolean getFollowMasterObjects() { return followMasterObjects; }
public boolean getFollowMasterScheme() { return followMasterScheme; }
public boolean getFollowMasterBackground() { return followMasterBackground; }
public void setFollowMasterObjects(boolean flag) { followMasterObjects = flag; }
public void setFollowMasterScheme(boolean flag) { followMasterScheme = flag; }
public void setFollowMasterBackground(boolean flag) { followMasterBackground = flag; }
public boolean getFollowMasterObjects() { return followMasterObjects; }
public boolean getFollowMasterScheme() { return followMasterScheme; }
public boolean getFollowMasterBackground() { return followMasterBackground; }
public void setFollowMasterObjects(boolean flag) { followMasterObjects = flag; }
public void setFollowMasterScheme(boolean flag) { followMasterScheme = flag; }
public void setFollowMasterBackground(boolean flag) { followMasterBackground = flag; }
/* *************** record code follows ********************** */
/* *************** record code follows ********************** */
/**
* For the Notes Atom
*/
protected NotesAtom(byte[] source, int start, int len) {
// Sanity Checking
if(len < 8) { len = 8; }
/**
* For the Notes Atom
*/
protected NotesAtom(byte[] source, int start, int len) {
// Sanity Checking
if(len < 8) { len = 8; }
// Get the header
_header = Arrays.copyOfRange(source, start, start+8);
// Get the header
_header = Arrays.copyOfRange(source, start, start+8);
// Get the slide ID
slideID = LittleEndian.getInt(source,start+8);
// Get the slide ID
slideID = LittleEndian.getInt(source,start+8);
// Grok the flags, stored as bits
int flags = LittleEndian.getUShort(source,start+12);
followMasterBackground = (flags & 4) == 4;
followMasterScheme = (flags & 2) == 2;
followMasterObjects = (flags & 1) == 1;
// Grok the flags, stored as bits
int flags = LittleEndian.getUShort(source,start+12);
followMasterBackground = (flags & 4) == 4;
followMasterScheme = (flags & 2) == 2;
followMasterObjects = (flags & 1) == 1;
// There might be 2 more bytes, which are a reserved field
reserved = IOUtils.safelyClone(source, start+14, len-14, MAX_RECORD_LENGTH);
}
// There might be 2 more bytes, which are a reserved field
reserved = IOUtils.safelyClone(source, start+14, len-14, MAX_RECORD_LENGTH);
}
/**
* We are of type 1009
*/
public long getRecordType() { return _type; }
/**
* We are of type 1009
*/
public long getRecordType() { return _type; }
/**
* Write the contents of the record back, so it can be written
* to disk
*/
public void writeOut(OutputStream out) throws IOException {
// Header
out.write(_header);
/**
* Write the contents of the record back, so it can be written
* to disk
*/
public void writeOut(OutputStream out) throws IOException {
// Header
out.write(_header);
// Slide ID
writeLittleEndian(slideID,out);
// Slide ID
writeLittleEndian(slideID,out);
// Flags
short flags = 0;
if(followMasterObjects) { flags += 1; }
if(followMasterScheme) { flags += 2; }
if(followMasterBackground) { flags += 4; }
writeLittleEndian(flags,out);
// Flags
short flags = 0;
if(followMasterObjects) { flags += 1; }
if(followMasterScheme) { flags += 2; }
if(followMasterBackground) { flags += 4; }
writeLittleEndian(flags,out);
// Reserved fields
out.write(reserved);
}
// Reserved fields
out.write(reserved);
}
@Override
public Map<String, Supplier<?>> getGenericProperties() {
return GenericRecordUtil.getGenericProperties(
"slideId", this::getSlideID,
"followMasterObjects", this::getFollowMasterObjects,
"followMasterScheme", this::getFollowMasterScheme,
"followMasterBackground", this::getFollowMasterBackground
);
}
@Override
public Map<String, Supplier<?>> getGenericProperties() {
return GenericRecordUtil.getGenericProperties(
"slideId", this::getSlideID,
"followMasterObjects", this::getFollowMasterObjects,
"followMasterScheme", this::getFollowMasterScheme,
"followMasterBackground", this::getFollowMasterBackground
);
}
}

View File

@ -52,7 +52,7 @@ public final class OEPlaceholderAtom extends RecordAtom{
*/
public static final int PLACEHOLDER_QUARTSIZE = 2;
private byte[] _header;
private byte[] _header;
private int placementId;
private int placeholderId;
@ -77,7 +77,7 @@ public final class OEPlaceholderAtom extends RecordAtom{
/**
* Build an instance of {@code OEPlaceholderAtom} from on-disk data
*/
protected OEPlaceholderAtom(byte[] source, int start, int len) {
protected OEPlaceholderAtom(byte[] source, int start, int len) {
_header = Arrays.copyOfRange(source, start, start+8);
int offset = start+8;
@ -85,12 +85,12 @@ public final class OEPlaceholderAtom extends RecordAtom{
placeholderId = LittleEndian.getUByte(source, offset); offset++;
placeholderSize = LittleEndian.getUByte(source, offset); offset++;
unusedShort = LittleEndian.getShort(source, offset);
}
}
/**
* @return type of this record {@link RecordTypes#OEPlaceholderAtom}.
*/
@Override
@Override
public long getRecordType() { return RecordTypes.OEPlaceholderAtom.typeID; }
/**
@ -163,12 +163,12 @@ public final class OEPlaceholderAtom extends RecordAtom{
placeholderSize = size;
}
/**
* Write the contents of the record back, so it can be written to disk
*/
@Override
/**
* Write the contents of the record back, so it can be written to disk
*/
@Override
public void writeOut(OutputStream out) throws IOException {
out.write(_header);
out.write(_header);
byte[] recdata = new byte[8];
LittleEndian.putInt(recdata, 0, placementId);
@ -177,7 +177,7 @@ public final class OEPlaceholderAtom extends RecordAtom{
LittleEndian.putShort(recdata, 6, unusedShort);
out.write(recdata);
}
}
@Override
public Map<String, Supplier<?>> getGenericProperties() {

View File

@ -64,34 +64,34 @@ import org.apache.poi.util.LittleEndian;
// would require a wrapping class
public final class PPDrawing extends RecordAtom implements Iterable<EscherRecord> {
private final byte[] _header;
private long _type;
private final byte[] _header;
private long _type;
private EscherTextboxWrapper[] textboxWrappers;
private EscherTextboxWrapper[] textboxWrappers;
private final EscherContainerRecord dgContainer = new EscherContainerRecord();
private final EscherContainerRecord dgContainer = new EscherContainerRecord();
//cached EscherDgRecord
private EscherDgRecord dg;
//cached EscherDgRecord
private EscherDgRecord dg;
/**
* Get access to the underlying Escher Records
*/
@SuppressWarnings("WeakerAccess")
public List<EscherRecord> getEscherRecords() { return Collections.singletonList(dgContainer); }
/**
* Get access to the underlying Escher Records
*/
@SuppressWarnings("WeakerAccess")
public List<EscherRecord> getEscherRecords() { return Collections.singletonList(dgContainer); }
@Override
public Iterator<EscherRecord> iterator() {
return getEscherRecords().iterator();
}
@Override
public Iterator<EscherRecord> iterator() {
return getEscherRecords().iterator();
}
/**
* Get access to the atoms inside Textboxes
*/
public EscherTextboxWrapper[] getTextboxWrappers() { return textboxWrappers; }
/**
* Get access to the atoms inside Textboxes
*/
public EscherTextboxWrapper[] getTextboxWrappers() { return textboxWrappers; }
/* ******************** record stuff follows ********************** */
/* ******************** record stuff follows ********************** */
/**
* Creates a new, empty, PPDrawing (typically for use with a new Slide
@ -107,223 +107,223 @@ public final class PPDrawing extends RecordAtom implements Iterable<EscherRecord
create();
}
/**
* Sets everything up, groks the escher etc
*/
PPDrawing(byte[] source, int start, int len) {
// Get the header
_header = Arrays.copyOfRange(source, start, start+8);
/**
* Sets everything up, groks the escher etc
*/
PPDrawing(byte[] source, int start, int len) {
// Get the header
_header = Arrays.copyOfRange(source, start, start+8);
// Get the type
_type = LittleEndian.getUShort(_header,2);
// Get the type
_type = LittleEndian.getUShort(_header,2);
// Build up a tree of Escher records contained within
final DefaultEscherRecordFactory erf = new HSLFEscherRecordFactory();
dgContainer.fillFields(source, start + 8, erf);
assert dgContainer.getRecordId() == EscherRecordTypes.DG_CONTAINER.typeID;
dg = dgContainer.getChildById(EscherRecordTypes.DG.typeID);
// Build up a tree of Escher records contained within
final DefaultEscherRecordFactory erf = new HSLFEscherRecordFactory();
dgContainer.fillFields(source, start + 8, erf);
assert dgContainer.getRecordId() == EscherRecordTypes.DG_CONTAINER.typeID;
dg = dgContainer.getChildById(EscherRecordTypes.DG.typeID);
textboxWrappers = Stream.of(dgContainer).
flatMap(findEscherContainer(EscherRecordTypes.SPGR_CONTAINER)).
flatMap(findEscherContainer(EscherRecordTypes.SP_CONTAINER)).
flatMap(PPDrawing::getTextboxHelper).
toArray(EscherTextboxWrapper[]::new);
}
private static Stream<EscherTextboxWrapper> getTextboxHelper(EscherContainerRecord spContainer) {
Optional<EscherTextboxRecord> oTB = firstEscherRecord(spContainer, EscherRecordTypes.CLIENT_TEXTBOX);
if (!oTB.isPresent()) {
return Stream.empty();
}
EscherTextboxWrapper tbw = new EscherTextboxWrapper(oTB.get());
findInSpContainer(spContainer).ifPresent(tbw::setStyleTextProp9Atom);
Optional<EscherSpRecord> oSP = firstEscherRecord(spContainer, EscherRecordTypes.SP);
oSP.map(EscherSpRecord::getShapeId).ifPresent(tbw::setShapeId);
return Stream.of(tbw);
}
private static Optional<StyleTextProp9Atom> findInSpContainer(final EscherContainerRecord spContainer) {
Optional<HSLFEscherClientDataRecord> oCD = firstEscherRecord(spContainer, EscherRecordTypes.CLIENT_DATA);
return oCD.map(HSLFEscherClientDataRecord::getHSLFChildRecords).map(List::stream).orElseGet(Stream::empty).
filter(sameHSLF(RecordTypes.ProgTags)).
flatMap(r -> Stream.of(r.getChildRecords())).
filter(sameHSLF(RecordTypes.ProgBinaryTag)).
flatMap(PPDrawing::findInProgBinaryTag).
findFirst();
}
private static Stream<StyleTextProp9Atom> findInProgBinaryTag(org.apache.poi.hslf.record.Record r) {
Record[] ch = r.getChildRecords();
if (ch != null &&
ch.length == 2 &&
ch[0] instanceof CString &&
ch[1] instanceof BinaryTagDataBlob &&
"___PPT9".equals(((CString)ch[0]).getText())
) {
BinaryTagDataBlob blob = (BinaryTagDataBlob) ch[1];
StyleTextProp9Atom prop9 = (StyleTextProp9Atom) blob.findFirstOfType(RecordTypes.StyleTextProp9Atom.typeID);
if (prop9 != null) {
return Stream.of(prop9);
}
}
return Stream.empty();
}
/**
* We are type 1036
*/
public long getRecordType() { return _type; }
/**
* We're pretending to be an atom, so return null
*/
public org.apache.poi.hslf.record.Record[] getChildRecords() { return null; }
/**
* Write the contents of the record back, so it can be written
* to disk
* Walks the escher layer to get the contents
*/
public void writeOut(OutputStream out) throws IOException {
// Ensure the escher layer reflects the text changes
for (EscherTextboxWrapper w : textboxWrappers) {
w.writeOut(null);
}
// Find the new size of the escher children;
int newSize = 0;
newSize += dgContainer.getRecordSize();
// Update the size (header bytes 5-8)
LittleEndian.putInt(_header,4,newSize);
// Write out our header
out.write(_header);
// Now grab the children's data
byte[] b = new byte[newSize];
int done = 0;
dgContainer.serialize(done, b);
// Finally, write out the children
out.write(b);
}
/**
* Create the Escher records associated with a new PPDrawing
*/
private void create(){
dgContainer.setRecordId( EscherContainerRecord.DG_CONTAINER );
dgContainer.setOptions((short)15);
dg = new EscherDgRecord();
dg.setOptions((short)16);
dg.setNumShapes(1);
dgContainer.addChildRecord(dg);
EscherContainerRecord spgrContainer = new EscherContainerRecord();
spgrContainer.setOptions((short)15);
spgrContainer.setRecordId(EscherContainerRecord.SPGR_CONTAINER);
EscherContainerRecord spContainer = new EscherContainerRecord();
spContainer.setOptions((short)15);
spContainer.setRecordId(EscherContainerRecord.SP_CONTAINER);
EscherSpgrRecord spgr = new EscherSpgrRecord();
spgr.setOptions((short)1);
spContainer.addChildRecord(spgr);
EscherSpRecord sp = new EscherSpRecord();
sp.setOptions((short)((ShapeType.NOT_PRIMITIVE.nativeId << 4) + 2));
sp.setFlags(EscherSpRecord.FLAG_PATRIARCH | EscherSpRecord.FLAG_GROUP);
spContainer.addChildRecord(sp);
spgrContainer.addChildRecord(spContainer);
dgContainer.addChildRecord(spgrContainer);
spContainer = new EscherContainerRecord();
spContainer.setOptions((short)15);
spContainer.setRecordId(EscherContainerRecord.SP_CONTAINER);
sp = new EscherSpRecord();
sp.setOptions((short)((ShapeType.RECT.nativeId << 4) + 2));
sp.setFlags(EscherSpRecord.FLAG_BACKGROUND | EscherSpRecord.FLAG_HASSHAPETYPE);
spContainer.addChildRecord(sp);
EscherOptRecord opt = new EscherOptRecord();
opt.setRecordId(EscherOptRecord.RECORD_ID);
opt.addEscherProperty(new EscherRGBProperty(EscherPropertyTypes.FILL__FILLCOLOR, 134217728));
opt.addEscherProperty(new EscherRGBProperty(EscherPropertyTypes.FILL__FILLBACKCOLOR, 134217733));
opt.addEscherProperty(new EscherSimpleProperty(EscherPropertyTypes.FILL__RECTRIGHT, 10064750));
opt.addEscherProperty(new EscherSimpleProperty(EscherPropertyTypes.FILL__RECTBOTTOM, 7778750));
opt.addEscherProperty(new EscherBoolProperty(EscherPropertyTypes.FILL__NOFILLHITTEST, 1179666));
opt.addEscherProperty(new EscherBoolProperty(EscherPropertyTypes.LINESTYLE__NOLINEDRAWDASH, 524288));
opt.addEscherProperty(new EscherSimpleProperty(EscherPropertyTypes.SHAPE__BLACKANDWHITESETTINGS, 9));
opt.addEscherProperty(new EscherSimpleProperty(EscherPropertyTypes.SHAPE__BACKGROUNDSHAPE, 65537));
spContainer.addChildRecord(opt);
dgContainer.addChildRecord(spContainer);
}
/**
* Add a new EscherTextboxWrapper to this <code>PPDrawing</code>.
*/
public void addTextboxWrapper(EscherTextboxWrapper txtbox){
EscherTextboxWrapper[] tw = new EscherTextboxWrapper[textboxWrappers.length + 1];
System.arraycopy(textboxWrappers, 0, tw, 0, textboxWrappers.length);
tw[textboxWrappers.length] = txtbox;
textboxWrappers = tw;
}
/**
* @return the container record for drawings
* @since POI 3.14-Beta2
*/
public EscherContainerRecord getDgContainer() {
return dgContainer;
}
/**
* Return EscherDgRecord which keeps track of the number of shapes and shapeId in this drawing group
*
* @return EscherDgRecord
*/
public EscherDgRecord getEscherDgRecord(){
return dg;
}
public StyleTextProp9Atom[] getNumberedListInfo() {
return Stream.of(dgContainer).
flatMap(findEscherContainer(EscherRecordTypes.SPGR_CONTAINER)).
flatMap(findEscherContainer(EscherRecordTypes.SP_CONTAINER)).
map(PPDrawing::findInSpContainer).
filter(Optional::isPresent).
map(Optional::get).
toArray(StyleTextProp9Atom[]::new);
textboxWrappers = Stream.of(dgContainer).
flatMap(findEscherContainer(EscherRecordTypes.SPGR_CONTAINER)).
flatMap(findEscherContainer(EscherRecordTypes.SP_CONTAINER)).
flatMap(PPDrawing::getTextboxHelper).
toArray(EscherTextboxWrapper[]::new);
}
@Override
public Map<String, Supplier<?>> getGenericProperties() {
return GenericRecordUtil.getGenericProperties("escherRecords", this::getEscherRecords);
}
private static Stream<EscherTextboxWrapper> getTextboxHelper(EscherContainerRecord spContainer) {
Optional<EscherTextboxRecord> oTB = firstEscherRecord(spContainer, EscherRecordTypes.CLIENT_TEXTBOX);
if (!oTB.isPresent()) {
return Stream.empty();
}
private static Predicate<org.apache.poi.hslf.record.Record> sameHSLF(RecordTypes type) {
return (p) -> p.getRecordType() == type.typeID;
}
EscherTextboxWrapper tbw = new EscherTextboxWrapper(oTB.get());
findInSpContainer(spContainer).ifPresent(tbw::setStyleTextProp9Atom);
private static Predicate<EscherRecord> sameEscher(EscherRecordTypes type) {
return (p) -> p.getRecordId() == type.typeID;
}
Optional<EscherSpRecord> oSP = firstEscherRecord(spContainer, EscherRecordTypes.SP);
oSP.map(EscherSpRecord::getShapeId).ifPresent(tbw::setShapeId);
@SuppressWarnings("unchecked")
private static <T extends EscherRecord> Optional<T> firstEscherRecord(Iterable<EscherRecord> container, EscherRecordTypes type) {
return StreamSupport.stream(container.spliterator(), false).filter(sameEscher(type)).map(o -> (T)o).findFirst();
}
return Stream.of(tbw);
}
private static Function<EscherContainerRecord,Stream<EscherContainerRecord>> findEscherContainer(EscherRecordTypes type) {
return (r) -> r.getChildContainers().stream().filter(sameEscher(type));
}
private static Optional<StyleTextProp9Atom> findInSpContainer(final EscherContainerRecord spContainer) {
Optional<HSLFEscherClientDataRecord> oCD = firstEscherRecord(spContainer, EscherRecordTypes.CLIENT_DATA);
return oCD.map(HSLFEscherClientDataRecord::getHSLFChildRecords).map(List::stream).orElseGet(Stream::empty).
filter(sameHSLF(RecordTypes.ProgTags)).
flatMap(r -> Stream.of(r.getChildRecords())).
filter(sameHSLF(RecordTypes.ProgBinaryTag)).
flatMap(PPDrawing::findInProgBinaryTag).
findFirst();
}
private static Stream<StyleTextProp9Atom> findInProgBinaryTag(org.apache.poi.hslf.record.Record r) {
Record[] ch = r.getChildRecords();
if (ch != null &&
ch.length == 2 &&
ch[0] instanceof CString &&
ch[1] instanceof BinaryTagDataBlob &&
"___PPT9".equals(((CString)ch[0]).getText())
) {
BinaryTagDataBlob blob = (BinaryTagDataBlob) ch[1];
StyleTextProp9Atom prop9 = (StyleTextProp9Atom) blob.findFirstOfType(RecordTypes.StyleTextProp9Atom.typeID);
if (prop9 != null) {
return Stream.of(prop9);
}
}
return Stream.empty();
}
/**
* We are type 1036
*/
public long getRecordType() { return _type; }
/**
* We're pretending to be an atom, so return null
*/
public org.apache.poi.hslf.record.Record[] getChildRecords() { return null; }
/**
* Write the contents of the record back, so it can be written
* to disk
* Walks the escher layer to get the contents
*/
public void writeOut(OutputStream out) throws IOException {
// Ensure the escher layer reflects the text changes
for (EscherTextboxWrapper w : textboxWrappers) {
w.writeOut(null);
}
// Find the new size of the escher children;
int newSize = 0;
newSize += dgContainer.getRecordSize();
// Update the size (header bytes 5-8)
LittleEndian.putInt(_header,4,newSize);
// Write out our header
out.write(_header);
// Now grab the children's data
byte[] b = new byte[newSize];
int done = 0;
dgContainer.serialize(done, b);
// Finally, write out the children
out.write(b);
}
/**
* Create the Escher records associated with a new PPDrawing
*/
private void create(){
dgContainer.setRecordId( EscherContainerRecord.DG_CONTAINER );
dgContainer.setOptions((short)15);
dg = new EscherDgRecord();
dg.setOptions((short)16);
dg.setNumShapes(1);
dgContainer.addChildRecord(dg);
EscherContainerRecord spgrContainer = new EscherContainerRecord();
spgrContainer.setOptions((short)15);
spgrContainer.setRecordId(EscherContainerRecord.SPGR_CONTAINER);
EscherContainerRecord spContainer = new EscherContainerRecord();
spContainer.setOptions((short)15);
spContainer.setRecordId(EscherContainerRecord.SP_CONTAINER);
EscherSpgrRecord spgr = new EscherSpgrRecord();
spgr.setOptions((short)1);
spContainer.addChildRecord(spgr);
EscherSpRecord sp = new EscherSpRecord();
sp.setOptions((short)((ShapeType.NOT_PRIMITIVE.nativeId << 4) + 2));
sp.setFlags(EscherSpRecord.FLAG_PATRIARCH | EscherSpRecord.FLAG_GROUP);
spContainer.addChildRecord(sp);
spgrContainer.addChildRecord(spContainer);
dgContainer.addChildRecord(spgrContainer);
spContainer = new EscherContainerRecord();
spContainer.setOptions((short)15);
spContainer.setRecordId(EscherContainerRecord.SP_CONTAINER);
sp = new EscherSpRecord();
sp.setOptions((short)((ShapeType.RECT.nativeId << 4) + 2));
sp.setFlags(EscherSpRecord.FLAG_BACKGROUND | EscherSpRecord.FLAG_HASSHAPETYPE);
spContainer.addChildRecord(sp);
EscherOptRecord opt = new EscherOptRecord();
opt.setRecordId(EscherOptRecord.RECORD_ID);
opt.addEscherProperty(new EscherRGBProperty(EscherPropertyTypes.FILL__FILLCOLOR, 134217728));
opt.addEscherProperty(new EscherRGBProperty(EscherPropertyTypes.FILL__FILLBACKCOLOR, 134217733));
opt.addEscherProperty(new EscherSimpleProperty(EscherPropertyTypes.FILL__RECTRIGHT, 10064750));
opt.addEscherProperty(new EscherSimpleProperty(EscherPropertyTypes.FILL__RECTBOTTOM, 7778750));
opt.addEscherProperty(new EscherBoolProperty(EscherPropertyTypes.FILL__NOFILLHITTEST, 1179666));
opt.addEscherProperty(new EscherBoolProperty(EscherPropertyTypes.LINESTYLE__NOLINEDRAWDASH, 524288));
opt.addEscherProperty(new EscherSimpleProperty(EscherPropertyTypes.SHAPE__BLACKANDWHITESETTINGS, 9));
opt.addEscherProperty(new EscherSimpleProperty(EscherPropertyTypes.SHAPE__BACKGROUNDSHAPE, 65537));
spContainer.addChildRecord(opt);
dgContainer.addChildRecord(spContainer);
}
/**
* Add a new EscherTextboxWrapper to this <code>PPDrawing</code>.
*/
public void addTextboxWrapper(EscherTextboxWrapper txtbox){
EscherTextboxWrapper[] tw = new EscherTextboxWrapper[textboxWrappers.length + 1];
System.arraycopy(textboxWrappers, 0, tw, 0, textboxWrappers.length);
tw[textboxWrappers.length] = txtbox;
textboxWrappers = tw;
}
/**
* @return the container record for drawings
* @since POI 3.14-Beta2
*/
public EscherContainerRecord getDgContainer() {
return dgContainer;
}
/**
* Return EscherDgRecord which keeps track of the number of shapes and shapeId in this drawing group
*
* @return EscherDgRecord
*/
public EscherDgRecord getEscherDgRecord(){
return dg;
}
public StyleTextProp9Atom[] getNumberedListInfo() {
return Stream.of(dgContainer).
flatMap(findEscherContainer(EscherRecordTypes.SPGR_CONTAINER)).
flatMap(findEscherContainer(EscherRecordTypes.SP_CONTAINER)).
map(PPDrawing::findInSpContainer).
filter(Optional::isPresent).
map(Optional::get).
toArray(StyleTextProp9Atom[]::new);
}
@Override
public Map<String, Supplier<?>> getGenericProperties() {
return GenericRecordUtil.getGenericProperties("escherRecords", this::getEscherRecords);
}
private static Predicate<org.apache.poi.hslf.record.Record> sameHSLF(RecordTypes type) {
return (p) -> p.getRecordType() == type.typeID;
}
private static Predicate<EscherRecord> sameEscher(EscherRecordTypes type) {
return (p) -> p.getRecordId() == type.typeID;
}
@SuppressWarnings("unchecked")
private static <T extends EscherRecord> Optional<T> firstEscherRecord(Iterable<EscherRecord> container, EscherRecordTypes type) {
return StreamSupport.stream(container.spliterator(), false).filter(sameEscher(type)).map(o -> (T)o).findFirst();
}
private static Function<EscherContainerRecord,Stream<EscherContainerRecord>> findEscherContainer(EscherRecordTypes type) {
return (r) -> r.getChildContainers().stream().filter(sameEscher(type));
}
}

View File

@ -22,6 +22,6 @@ package org.apache.poi.hslf.record;
* parent is, and how it wants to be told which record is its parent.
*/
public interface ParentAwareRecord {
public org.apache.poi.hslf.record.RecordContainer getParentRecord();
public void setParentRecord(RecordContainer parentRecord);
public org.apache.poi.hslf.record.RecordContainer getParentRecord();
public void setParentRecord(RecordContainer parentRecord);
}

View File

@ -48,22 +48,22 @@ import org.apache.poi.util.LittleEndianConsts;
public final class PersistPtrHolder extends PositionDependentRecordAtom
{
//arbitrarily selected; may need to increase
private static final int MAX_RECORD_LENGTH = 100_000;
//arbitrarily selected; may need to increase
private static final int MAX_RECORD_LENGTH = 100_000;
private final byte[] _header;
private byte[] _ptrData; // Will need to update this once we allow updates to _slideLocations
private final long _type;
private final byte[] _header;
private byte[] _ptrData; // Will need to update this once we allow updates to _slideLocations
private final long _type;
/**
* Holds the lookup for slides to their position on disk.
* You always need to check the most recent PersistPtrHolder
* that knows about a given slide to find the right location
*/
private final Map<Integer,Integer> _slideLocations;
/**
* Holds the lookup for slides to their position on disk.
* You always need to check the most recent PersistPtrHolder
* that knows about a given slide to find the right location
*/
private final Map<Integer,Integer> _slideLocations;
private static final BitField persistIdFld = BitFieldFactory.getInstance(0X000FFFFF);
private static final BitField cntPersistFld = BitFieldFactory.getInstance(0XFFF00000);
private static final BitField persistIdFld = BitFieldFactory.getInstance(0X000FFFFF);
private static final BitField cntPersistFld = BitFieldFactory.getInstance(0XFFF00000);
/**
* Return the value we were given at creation, be it 6001 or 6002
@ -71,74 +71,74 @@ public final class PersistPtrHolder extends PositionDependentRecordAtom
@Override
public long getRecordType() { return _type; }
/**
* Get the list of slides that this PersistPtrHolder knows about.
* (They will be the keys in the map for looking up the positions
* of these slides)
*/
public int[] getKnownSlideIDs() {
int[] ids = new int[_slideLocations.size()];
int i = 0;
for (Integer slideId : _slideLocations.keySet()) {
ids[i++] = slideId;
}
return ids;
}
/**
* Get the list of slides that this PersistPtrHolder knows about.
* (They will be the keys in the map for looking up the positions
* of these slides)
*/
public int[] getKnownSlideIDs() {
int[] ids = new int[_slideLocations.size()];
int i = 0;
for (Integer slideId : _slideLocations.keySet()) {
ids[i++] = slideId;
}
return ids;
}
/**
* Get the lookup from slide numbers to byte offsets, for the slides
* known about by this PersistPtrHolder.
*/
public Map<Integer,Integer> getSlideLocationsLookup() {
return Collections.unmodifiableMap(_slideLocations);
}
/**
* Get the lookup from slide numbers to byte offsets, for the slides
* known about by this PersistPtrHolder.
*/
public Map<Integer,Integer> getSlideLocationsLookup() {
return Collections.unmodifiableMap(_slideLocations);
}
/**
* Create a new holder for a PersistPtr record
*/
protected PersistPtrHolder(byte[] source, int start, int len) {
// Sanity Checking - including whole header, so treat
// length as based of 0, not 8 (including header size based)
if(len < 8) { len = 8; }
/**
* Create a new holder for a PersistPtr record
*/
protected PersistPtrHolder(byte[] source, int start, int len) {
// Sanity Checking - including whole header, so treat
// length as based of 0, not 8 (including header size based)
if(len < 8) { len = 8; }
// Treat as an atom, grab and hold everything
_header = Arrays.copyOfRange(source, start, start+8);
_type = LittleEndian.getUShort(_header,2);
// Treat as an atom, grab and hold everything
_header = Arrays.copyOfRange(source, start, start+8);
_type = LittleEndian.getUShort(_header,2);
// Try to make sense of the data part:
// Data part is made up of a number of these sets:
// 32 bit info value
// 12 bits count of # of entries
// base number for these entries
// count * 32 bit offsets
// Repeat as many times as you have data
_slideLocations = new HashMap<>();
_ptrData = IOUtils.safelyClone(source, start+8, len-8, MAX_RECORD_LENGTH);
// Try to make sense of the data part:
// Data part is made up of a number of these sets:
// 32 bit info value
// 12 bits count of # of entries
// base number for these entries
// count * 32 bit offsets
// Repeat as many times as you have data
_slideLocations = new HashMap<>();
_ptrData = IOUtils.safelyClone(source, start+8, len-8, MAX_RECORD_LENGTH);
int pos = 0;
while(pos < _ptrData.length) {
// Grab the info field
int info = LittleEndian.getInt(_ptrData,pos);
int pos = 0;
while(pos < _ptrData.length) {
// Grab the info field
int info = LittleEndian.getInt(_ptrData,pos);
// First 20 bits = offset number
// Remaining 12 bits = offset count
// First 20 bits = offset number
// Remaining 12 bits = offset count
int offset_no = persistIdFld.getValue(info);
int offset_count = cntPersistFld.getValue(info);
int offset_count = cntPersistFld.getValue(info);
// Wind on by the 4 byte info header
pos += 4;
// Wind on by the 4 byte info header
pos += 4;
// Grab the offsets for each of the sheets
for(int i=0; i<offset_count; i++) {
int sheet_no = offset_no + i;
int sheet_offset = (int)LittleEndian.getUInt(_ptrData,pos);
_slideLocations.put(sheet_no, sheet_offset);
// Grab the offsets for each of the sheets
for(int i=0; i<offset_count; i++) {
int sheet_no = offset_no + i;
int sheet_offset = (int)LittleEndian.getUInt(_ptrData,pos);
_slideLocations.put(sheet_no, sheet_offset);
// Wind on by 4 bytes per sheet found
pos += 4;
}
}
}
// Wind on by 4 bytes per sheet found
pos += 4;
}
}
}
/**
* remove all slide references
@ -160,88 +160,88 @@ public final class PersistPtrHolder extends PositionDependentRecordAtom
_slideLocations.put(slideID, posOnDisk);
}
/**
* At write-out time, update the references to the sheets to their
* new positions
*/
@Override
public void updateOtherRecordReferences(Map<Integer,Integer> oldToNewReferencesLookup) {
// Loop over all the slides we know about
// Find where they used to live, and where they now live
for (Entry<Integer,Integer> me : _slideLocations.entrySet()) {
Integer oldPos = me.getValue();
Integer newPos = oldToNewReferencesLookup.get(oldPos);
/**
* At write-out time, update the references to the sheets to their
* new positions
*/
@Override
public void updateOtherRecordReferences(Map<Integer,Integer> oldToNewReferencesLookup) {
// Loop over all the slides we know about
// Find where they used to live, and where they now live
for (Entry<Integer,Integer> me : _slideLocations.entrySet()) {
Integer oldPos = me.getValue();
Integer newPos = oldToNewReferencesLookup.get(oldPos);
if (newPos == null) {
Integer id = me.getKey();
LOG.atWarn().log("Couldn't find the new location of the \"slide\" with id {} that used to " +
"be at {}. Not updating the position of it, you probably won't be able to find it any more " +
"(if you ever could!)", id, oldPos);
LOG.atWarn().log("Couldn't find the new location of the \"slide\" with id {} that used to " +
"be at {}. Not updating the position of it, you probably won't be able to find it any more " +
"(if you ever could!)", id, oldPos);
} else {
me.setValue(newPos);
}
}
}
}
}
private void normalizePersistDirectory() {
// Building the info block
// First 20 bits = offset number = slide ID (persistIdFld, i.e. first slide ID of a continuous group)
// Remaining 12 bits = offset count = 1 (cntPersistFld, i.e. continuous entries in a group)
//
// the info block is then followed by the slide offset (32 bits)
private void normalizePersistDirectory() {
// Building the info block
// First 20 bits = offset number = slide ID (persistIdFld, i.e. first slide ID of a continuous group)
// Remaining 12 bits = offset count = 1 (cntPersistFld, i.e. continuous entries in a group)
//
// the info block is then followed by the slide offset (32 bits)
int[] infoBlocks = new int[_slideLocations.size()*2];
int lastSlideId = -1;
int lastPersistIdx = 0;
int lastIdx = -1;
int entryCnt = 0;
int baseSlideId = -1;
int[] infoBlocks = new int[_slideLocations.size()*2];
int lastSlideId = -1;
int lastPersistIdx = 0;
int lastIdx = -1;
int entryCnt = 0;
int baseSlideId = -1;
Iterable<Entry<Integer,Integer>> iter = _slideLocations.entrySet().stream()
.sorted(Comparator.comparingInt(Entry::getKey))::iterator;
for (Entry<Integer, Integer> me : iter) {
int nextSlideId = me.getKey();
if (lastSlideId + 1 < nextSlideId) {
// start new PersistDirectoryEntry
lastPersistIdx = ++lastIdx;
entryCnt = 0;
baseSlideId = nextSlideId;
}
Iterable<Entry<Integer,Integer>> iter = _slideLocations.entrySet().stream()
.sorted(Comparator.comparingInt(Entry::getKey))::iterator;
for (Entry<Integer, Integer> me : iter) {
int nextSlideId = me.getKey();
if (lastSlideId + 1 < nextSlideId) {
// start new PersistDirectoryEntry
lastPersistIdx = ++lastIdx;
entryCnt = 0;
baseSlideId = nextSlideId;
}
int infoBlock = persistIdFld.setValue(0, baseSlideId);
infoBlock = cntPersistFld.setValue(infoBlock, ++entryCnt);
infoBlocks[lastPersistIdx] = infoBlock;
// add the offset
infoBlocks[++lastIdx] = me.getValue();
int infoBlock = persistIdFld.setValue(0, baseSlideId);
infoBlock = cntPersistFld.setValue(infoBlock, ++entryCnt);
infoBlocks[lastPersistIdx] = infoBlock;
// add the offset
infoBlocks[++lastIdx] = me.getValue();
lastSlideId = nextSlideId;
}
lastSlideId = nextSlideId;
}
// Save the new ptr data
_ptrData = new byte[(lastIdx+1)*LittleEndianConsts.INT_SIZE];
for (int idx = 0; idx<=lastIdx; idx++) {
LittleEndian.putInt(_ptrData, idx*LittleEndianConsts.INT_SIZE, infoBlocks[idx]);
}
// Save the new ptr data
_ptrData = new byte[(lastIdx+1)*LittleEndianConsts.INT_SIZE];
for (int idx = 0; idx<=lastIdx; idx++) {
LittleEndian.putInt(_ptrData, idx*LittleEndianConsts.INT_SIZE, infoBlocks[idx]);
}
// Update the atom header
LittleEndian.putInt(_header, 4, _ptrData.length);
}
// Update the atom header
LittleEndian.putInt(_header, 4, _ptrData.length);
}
/**
* Write the contents of the record back, so it can be written
* to disk
*/
@Override
/**
* Write the contents of the record back, so it can be written
* to disk
*/
@Override
public void writeOut(OutputStream out) throws IOException {
normalizePersistDirectory();
out.write(_header);
out.write(_ptrData);
}
normalizePersistDirectory();
out.write(_header);
out.write(_ptrData);
}
@Override
public Map<String, Supplier<?>> getGenericProperties() {
return GenericRecordUtil.getGenericProperties(
"slideLocations", this::getSlideLocationsLookup
);
}
@Override
public Map<String, Supplier<?>> getGenericProperties() {
return GenericRecordUtil.getGenericProperties(
"slideLocations", this::getSlideLocationsLookup
);
}
}

View File

@ -32,18 +32,18 @@ import java.util.Map;
public interface PositionDependentRecord
{
/** Fetch our location on the disk, as of the last write out */
public int getLastOnDiskOffset();
/** Fetch our location on the disk, as of the last write out */
public int getLastOnDiskOffset();
/**
* Update the Record's idea of where on disk it lives, after a write out.
* Use with care...
*/
public void setLastOnDiskOffset(int offset);
/**
* Update the Record's idea of where on disk it lives, after a write out.
* Use with care...
*/
public void setLastOnDiskOffset(int offset);
/**
* Offer the record the list of records that have changed their
* location as part of the writeout.
*/
public void updateOtherRecordReferences(Map<Integer,Integer> oldToNewReferencesLookup);
/**
* Offer the record the list of records that have changed their
* location as part of the writeout.
*/
public void updateOtherRecordReferences(Map<Integer,Integer> oldToNewReferencesLookup);
}

View File

@ -26,25 +26,25 @@ import java.util.Map;
public abstract class PositionDependentRecordAtom extends RecordAtom implements PositionDependentRecord
{
/** Our location on the disk, as of the last write out */
private int myLastOnDiskOffset;
/** Our location on the disk, as of the last write out */
private int myLastOnDiskOffset;
/** Fetch our location on the disk, as of the last write out */
public int getLastOnDiskOffset() { return myLastOnDiskOffset; }
/** Fetch our location on the disk, as of the last write out */
public int getLastOnDiskOffset() { return myLastOnDiskOffset; }
/**
* Update the Record's idea of where on disk it lives, after a write out.
* Use with care...
*/
public void setLastOnDiskOffset(int offset) {
myLastOnDiskOffset = offset;
}
/**
* Update the Record's idea of where on disk it lives, after a write out.
* Use with care...
*/
public void setLastOnDiskOffset(int offset) {
myLastOnDiskOffset = offset;
}
/**
* Offer the record the list of records that have changed their
* location as part of the writeout.
* Allows records to update their internal pointers to other records
* locations
*/
public abstract void updateOtherRecordReferences(Map<Integer,Integer> oldToNewReferencesLookup);
/**
* Offer the record the list of records that have changed their
* location as part of the writeout.
* Allows records to update their internal pointers to other records
* locations
*/
public abstract void updateOtherRecordReferences(Map<Integer,Integer> oldToNewReferencesLookup);
}

View File

@ -26,38 +26,38 @@ import java.util.Map;
public abstract class PositionDependentRecordContainer extends RecordContainer implements PositionDependentRecord
{
private int sheetId; // Found from PersistPtrHolder
private int sheetId; // Found from PersistPtrHolder
/**
* Fetch our sheet ID, as found from a PersistPtrHolder.
* Should match the RefId of our matching SlidePersistAtom
*/
public int getSheetId() { return sheetId; }
/**
* Fetch our sheet ID, as found from a PersistPtrHolder.
* Should match the RefId of our matching SlidePersistAtom
*/
public int getSheetId() { return sheetId; }
/**
* Set our sheet ID, as found from a PersistPtrHolder
*/
public void setSheetId(int id) { sheetId = id; }
/**
* Set our sheet ID, as found from a PersistPtrHolder
*/
public void setSheetId(int id) { sheetId = id; }
/** Our location on the disk, as of the last write out */
private int myLastOnDiskOffset;
/** Our location on the disk, as of the last write out */
private int myLastOnDiskOffset;
/** Fetch our location on the disk, as of the last write out */
public int getLastOnDiskOffset() { return myLastOnDiskOffset; }
/** Fetch our location on the disk, as of the last write out */
public int getLastOnDiskOffset() { return myLastOnDiskOffset; }
/**
* Update the Record's idea of where on disk it lives, after a write out.
* Use with care...
*/
public void setLastOnDiskOffset(int offset) {
myLastOnDiskOffset = offset;
}
/**
* Update the Record's idea of where on disk it lives, after a write out.
* Use with care...
*/
public void setLastOnDiskOffset(int offset) {
myLastOnDiskOffset = offset;
}
/**
* Since we're a container, we don't mind if other records move about.
* If we're told they have, just return straight off.
*/
public void updateOtherRecordReferences(Map<Integer,Integer> oldToNewReferencesLookup) {
}
/**
* Since we're a container, we don't mind if other records move about.
* If we're told they have, just return straight off.
*/
public void updateOtherRecordReferences(Map<Integer,Integer> oldToNewReferencesLookup) {
}
}

View File

@ -43,169 +43,169 @@ import static org.apache.logging.log4j.util.Unbox.box;
public abstract class Record implements GenericRecord
{
// For logging
protected static final Logger LOG = LogManager.getLogger(Record.class);
protected static final Logger LOG = LogManager.getLogger(Record.class);
/**
* Is this record type an Atom record (only has data),
* or is it a non-Atom record (has other records)?
*/
public abstract boolean isAnAtom();
/**
* Is this record type an Atom record (only has data),
* or is it a non-Atom record (has other records)?
*/
public abstract boolean isAnAtom();
/**
* Returns the type (held as a little endian in bytes 3 and 4)
* that this class handles
*/
public abstract long getRecordType();
/**
* Returns the type (held as a little endian in bytes 3 and 4)
* that this class handles
*/
public abstract long getRecordType();
/**
* Fetch all the child records of this record
* If this record is an atom, will return null
* If this record is a non-atom, but has no children, will return
* an empty array
*/
public abstract Record[] getChildRecords();
/**
* Fetch all the child records of this record
* If this record is an atom, will return null
* If this record is a non-atom, but has no children, will return
* an empty array
*/
public abstract Record[] getChildRecords();
/**
* Have the contents printer out into an OutputStream, used when
* writing a file back out to disk
* (Normally, atom classes will keep their bytes around, but
* non atom classes will just request the bytes from their
* children, then chuck on their header and return)
*/
public abstract void writeOut(OutputStream o) throws IOException;
/**
* Have the contents printer out into an OutputStream, used when
* writing a file back out to disk
* (Normally, atom classes will keep their bytes around, but
* non atom classes will just request the bytes from their
* children, then chuck on their header and return)
*/
public abstract void writeOut(OutputStream o) throws IOException;
@Override
public Enum getGenericRecordType() {
return RecordTypes.forTypeID((int)getRecordType());
}
@Override
public Enum getGenericRecordType() {
return RecordTypes.forTypeID((int)getRecordType());
}
@Override
public List<Record> getGenericChildren() {
Record[] recs = getChildRecords();
return (recs == null) ? null : Arrays.asList(recs);
}
@Override
public List<Record> getGenericChildren() {
Record[] recs = getChildRecords();
return (recs == null) ? null : Arrays.asList(recs);
}
/**
* When writing out, write out a signed int (32bit) in Little Endian format
*/
public static void writeLittleEndian(int i,OutputStream o) throws IOException {
byte[] bi = new byte[4];
LittleEndian.putInt(bi,0,i);
o.write(bi);
}
/**
* When writing out, write out a signed short (16bit) in Little Endian format
*/
public static void writeLittleEndian(short s,OutputStream o) throws IOException {
byte[] bs = new byte[2];
LittleEndian.putShort(bs,0,s);
o.write(bs);
}
/**
* When writing out, write out a signed int (32bit) in Little Endian format
*/
public static void writeLittleEndian(int i,OutputStream o) throws IOException {
byte[] bi = new byte[4];
LittleEndian.putInt(bi,0,i);
o.write(bi);
}
/**
* When writing out, write out a signed short (16bit) in Little Endian format
*/
public static void writeLittleEndian(short s,OutputStream o) throws IOException {
byte[] bs = new byte[2];
LittleEndian.putShort(bs,0,s);
o.write(bs);
}
/**
* Build and return the Record at the given offset.
* Note - does less error checking and handling than findChildRecords
* @param b The byte array to build from
* @param offset The offset to build at
*/
public static Record buildRecordAtOffset(byte[] b, int offset) {
long type = LittleEndian.getUShort(b,offset+2);
long rlen = LittleEndian.getUInt(b,offset+4);
/**
* Build and return the Record at the given offset.
* Note - does less error checking and handling than findChildRecords
* @param b The byte array to build from
* @param offset The offset to build at
*/
public static Record buildRecordAtOffset(byte[] b, int offset) {
long type = LittleEndian.getUShort(b,offset+2);
long rlen = LittleEndian.getUInt(b,offset+4);
// Sanity check the length
int rleni = (int)rlen;
if(rleni < 0) { rleni = 0; }
// Sanity check the length
int rleni = (int)rlen;
if(rleni < 0) { rleni = 0; }
return createRecordForType(type,b,offset,8+rleni);
}
return createRecordForType(type,b,offset,8+rleni);
}
/**
* Default method for finding child records of a container record
*/
public static Record[] findChildRecords(byte[] b, int start, int len) {
List<Record> children = new ArrayList<>(5);
/**
* Default method for finding child records of a container record
*/
public static Record[] findChildRecords(byte[] b, int start, int len) {
List<Record> children = new ArrayList<>(5);
// Jump our little way along, creating records as we go
int pos = start;
while(pos <= (start+len-8)) {
long type = LittleEndian.getUShort(b,pos+2);
long rlen = LittleEndian.getUInt(b,pos+4);
// Jump our little way along, creating records as we go
int pos = start;
while(pos <= (start+len-8)) {
long type = LittleEndian.getUShort(b,pos+2);
long rlen = LittleEndian.getUInt(b,pos+4);
// Sanity check the length
int rleni = (int)rlen;
if(rleni < 0) { rleni = 0; }
// Sanity check the length
int rleni = (int)rlen;
if(rleni < 0) { rleni = 0; }
// Abort if first record is of type 0000 and length FFFF,
// as that's a sign of a screwed up record
if(pos == 0 && type == 0L && rleni == 0xffff) {
throw new CorruptPowerPointFileException("Corrupt document - starts with record of type 0000 and length 0xFFFF");
}
// Abort if first record is of type 0000 and length FFFF,
// as that's a sign of a screwed up record
if(pos == 0 && type == 0L && rleni == 0xffff) {
throw new CorruptPowerPointFileException("Corrupt document - starts with record of type 0000 and length 0xFFFF");
}
Record r = createRecordForType(type,b,pos,8+rleni);
if(r != null) {
children.add(r);
}
pos += 8;
pos += rleni;
}
Record r = createRecordForType(type,b,pos,8+rleni);
if(r != null) {
children.add(r);
}
pos += 8;
pos += rleni;
}
// Turn the vector into an array, and return
// Turn the vector into an array, and return
return children.toArray(new Record[0]);
}
}
/**
* For a given type (little endian bytes 3 and 4 in record header),
* byte array, start position and length:
* will return a Record object that will handle that record
*
* Remember that while PPT stores the record lengths as 8 bytes short
* (not including the size of the header), this code assumes you're
* passing in corrected lengths
*/
public static Record createRecordForType(long type, byte[] b, int start, int len) {
// We use the RecordTypes class to provide us with the right
// class to use for a given type
// A spot of reflection gets us the (byte[],int,int) constructor
// From there, we instanciate the class
// Any special record handling occurs once we have the class
RecordTypes recordType = RecordTypes.forTypeID((short) type);
RecordConstructor c = recordType.recordConstructor;
if (c == null) {
// How odd. RecordTypes normally substitutes in
// a default handler class if it has heard of the record
// type but there's no support for it. Explicitly request
// that now
LOG.atDebug().log(() -> new StringFormattedMessage("Known but unhandled record type %d (0x%04x) at offset %d", type, type, start));
c = RecordTypes.UnknownRecordPlaceholder.recordConstructor;
} else if (recordType == RecordTypes.UnknownRecordPlaceholder) {
LOG.atDebug().log(() -> new StringFormattedMessage("Unknown placeholder type %d (0x%04x) at offset %d", type, type, start));
}
/**
* For a given type (little endian bytes 3 and 4 in record header),
* byte array, start position and length:
* will return a Record object that will handle that record
*
* Remember that while PPT stores the record lengths as 8 bytes short
* (not including the size of the header), this code assumes you're
* passing in corrected lengths
*/
public static Record createRecordForType(long type, byte[] b, int start, int len) {
// We use the RecordTypes class to provide us with the right
// class to use for a given type
// A spot of reflection gets us the (byte[],int,int) constructor
// From there, we instanciate the class
// Any special record handling occurs once we have the class
RecordTypes recordType = RecordTypes.forTypeID((short) type);
RecordConstructor c = recordType.recordConstructor;
if (c == null) {
// How odd. RecordTypes normally substitutes in
// a default handler class if it has heard of the record
// type but there's no support for it. Explicitly request
// that now
LOG.atDebug().log(() -> new StringFormattedMessage("Known but unhandled record type %d (0x%04x) at offset %d", type, type, start));
c = RecordTypes.UnknownRecordPlaceholder.recordConstructor;
} else if (recordType == RecordTypes.UnknownRecordPlaceholder) {
LOG.atDebug().log(() -> new StringFormattedMessage("Unknown placeholder type %d (0x%04x) at offset %d", type, type, start));
}
final Record toReturn;
try {
toReturn = c.apply(b, start, len);
} catch(RuntimeException e) {
// Handle case of a corrupt last record, whose claimed length
// would take us passed the end of the file
if(start + len > b.length ) {
LOG.atWarn().log("Warning: Skipping record of type {} at position {} which claims to be longer than the file! ({} vs {})", type, box(start), box(len), box(b.length - start));
return null;
}
final Record toReturn;
try {
toReturn = c.apply(b, start, len);
} catch(RuntimeException e) {
// Handle case of a corrupt last record, whose claimed length
// would take us passed the end of the file
if(start + len > b.length ) {
LOG.atWarn().log("Warning: Skipping record of type {} at position {} which claims to be longer than the file! ({} vs {})", type, box(start), box(len), box(b.length - start));
return null;
}
throw new HSLFException("Couldn't instantiate the class for type with id " + type + " on class " + c + " : " + e, e);
}
throw new HSLFException("Couldn't instantiate the class for type with id " + type + " on class " + c + " : " + e, e);
}
// Handling for special kinds of records follow
// Handling for special kinds of records follow
// If it's a position aware record, tell it where it is
if(toReturn instanceof PositionDependentRecord) {
PositionDependentRecord pdr = (PositionDependentRecord)toReturn;
pdr.setLastOnDiskOffset(start);
}
// If it's a position aware record, tell it where it is
if(toReturn instanceof PositionDependentRecord) {
PositionDependentRecord pdr = (PositionDependentRecord)toReturn;
pdr.setLastOnDiskOffset(start);
}
// Return the created record
return toReturn;
}
// Return the created record
return toReturn;
}
}

View File

@ -23,13 +23,13 @@ package org.apache.poi.hslf.record;
public abstract class RecordAtom extends Record
{
/**
* We are an atom
*/
public boolean isAnAtom() { return true; }
/**
* We are an atom
*/
public boolean isAnAtom() { return true; }
/**
* We're an atom, returns null
*/
public org.apache.poi.hslf.record.Record[] getChildRecords() { return null; }
/**
* We're an atom, returns null
*/
public org.apache.poi.hslf.record.Record[] getChildRecords() { return null; }
}

View File

@ -35,102 +35,102 @@ import org.apache.poi.util.LittleEndian;
public abstract class RecordContainer extends Record
{
protected Record[] _children;
protected Record[] _children;
/**
* Return any children
*/
@Override
/**
* Return any children
*/
@Override
public org.apache.poi.hslf.record.Record[] getChildRecords() { return _children; }
/**
* We're not an atom
*/
@Override
/**
* We're not an atom
*/
@Override
public boolean isAnAtom() { return false; }
/* ===============================================================
* Internal Move Helpers
* ===============================================================
*/
/* ===============================================================
* Internal Move Helpers
* ===============================================================
*/
/**
* Finds the location of the given child record
*/
private int findChildLocation(Record child) {
int i=0;
for(org.apache.poi.hslf.record.Record r : _children) {
if (r.equals(child)) {
return i;
}
i++;
}
return -1;
}
/**
* Finds the location of the given child record
*/
private int findChildLocation(Record child) {
int i=0;
for(org.apache.poi.hslf.record.Record r : _children) {
if (r.equals(child)) {
return i;
}
i++;
}
return -1;
}
/**
* Adds a child record, at the very end.
* @param newChild The child record to add
* @return the position of the added child
*/
private int appendChild(Record newChild) {
// Copy over, and pop the child in at the end
Record[] nc = Arrays.copyOf(_children, _children.length+1, org.apache.poi.hslf.record.Record[].class);
// Switch the arrays
nc[_children.length] = newChild;
_children = nc;
return _children.length;
}
/**
* Adds a child record, at the very end.
* @param newChild The child record to add
* @return the position of the added child
*/
private int appendChild(Record newChild) {
// Copy over, and pop the child in at the end
Record[] nc = Arrays.copyOf(_children, _children.length+1, org.apache.poi.hslf.record.Record[].class);
// Switch the arrays
nc[_children.length] = newChild;
_children = nc;
return _children.length;
}
/**
* Adds the given new Child Record at the given location,
* shuffling everything from there on down by one
*
* @param newChild The record to be added as child-record.
* @param position The index where the child should be added, 0-based
*/
private void addChildAt(Record newChild, int position) {
// Firstly, have the child added in at the end
appendChild(newChild);
/**
* Adds the given new Child Record at the given location,
* shuffling everything from there on down by one
*
* @param newChild The record to be added as child-record.
* @param position The index where the child should be added, 0-based
*/
private void addChildAt(Record newChild, int position) {
// Firstly, have the child added in at the end
appendChild(newChild);
// Now, have them moved to the right place
moveChildRecords( (_children.length-1), position, 1 );
}
// Now, have them moved to the right place
moveChildRecords( (_children.length-1), position, 1 );
}
/**
* Moves {@code number} child records from {@code oldLoc} to {@code newLoc}.
* @param oldLoc the current location of the records to move
* @param newLoc the new location for the records
* @param number the number of records to move
*/
private void moveChildRecords(int oldLoc, int newLoc, int number) {
if(oldLoc == newLoc) { return; }
if(number == 0) { return; }
/**
* Moves {@code number} child records from {@code oldLoc} to {@code newLoc}.
* @param oldLoc the current location of the records to move
* @param newLoc the new location for the records
* @param number the number of records to move
*/
private void moveChildRecords(int oldLoc, int newLoc, int number) {
if(oldLoc == newLoc) { return; }
if(number == 0) { return; }
// Check that we're not asked to move too many
if(oldLoc+number > _children.length) {
throw new IllegalArgumentException("Asked to move more records than there are!");
}
// Check that we're not asked to move too many
if(oldLoc+number > _children.length) {
throw new IllegalArgumentException("Asked to move more records than there are!");
}
// Do the move
ArrayUtil.arrayMoveWithin(_children, oldLoc, newLoc, number);
}
// Do the move
ArrayUtil.arrayMoveWithin(_children, oldLoc, newLoc, number);
}
/**
* Finds the first child record of the given type,
* or null if none of the child records are of the
* given type. Does not descend.
*/
public org.apache.poi.hslf.record.Record findFirstOfType(long type) {
for (org.apache.poi.hslf.record.Record r : _children) {
if (r.getRecordType() == type) {
return r;
}
}
return null;
}
/**
* Finds the first child record of the given type,
* or null if none of the child records are of the
* given type. Does not descend.
*/
public org.apache.poi.hslf.record.Record findFirstOfType(long type) {
for (org.apache.poi.hslf.record.Record r : _children) {
if (r.getRecordType() == type) {
return r;
}
}
return null;
}
/**
* Remove a child record from this record container
@ -153,55 +153,55 @@ public abstract class RecordContainer extends Record
}
/* ===============================================================
* External Move Methods
* ===============================================================
*/
* External Move Methods
* ===============================================================
*/
/**
* Add a new child record onto a record's list of children.
*
* @param newChild the child record to be added
* @return the position of the added child within the list, i.e. the last index
*/
public int appendChildRecord(Record newChild) {
return appendChild(newChild);
}
/**
* Add a new child record onto a record's list of children.
*
* @param newChild the child record to be added
* @return the position of the added child within the list, i.e. the last index
*/
public int appendChildRecord(Record newChild) {
return appendChild(newChild);
}
/**
* Adds the given Child Record after the supplied record
* @param newChild The record to add as new child.
* @param after The record after which the given record should be added.
* @return the position of the added child within the list
*/
public int addChildAfter(Record newChild, Record after) {
// Decide where we're going to put it
int loc = findChildLocation(after);
if(loc == -1) {
throw new IllegalArgumentException("Asked to add a new child after another record, but that record wasn't one of our children!");
}
// Add one place after the supplied record
addChildAt(newChild, loc+1);
return loc+1;
}
/**
* Adds the given Child Record before the supplied record
* @param newChild The record to add as new child.
* @param before The record before which the given record should be added.
/**
* Adds the given Child Record after the supplied record
* @param newChild The record to add as new child.
* @param after The record after which the given record should be added.
* @return the position of the added child within the list
*/
public int addChildBefore(Record newChild, Record before) {
// Decide where we're going to put it
int loc = findChildLocation(before);
if(loc == -1) {
throw new IllegalArgumentException("Asked to add a new child before another record, but that record wasn't one of our children!");
}
*/
public int addChildAfter(Record newChild, Record after) {
// Decide where we're going to put it
int loc = findChildLocation(after);
if(loc == -1) {
throw new IllegalArgumentException("Asked to add a new child after another record, but that record wasn't one of our children!");
}
// Add at the place of the supplied record
addChildAt(newChild, loc);
return loc;
}
// Add one place after the supplied record
addChildAt(newChild, loc+1);
return loc+1;
}
/**
* Adds the given Child Record before the supplied record
* @param newChild The record to add as new child.
* @param before The record before which the given record should be added.
* @return the position of the added child within the list
*/
public int addChildBefore(Record newChild, Record before) {
// Decide where we're going to put it
int loc = findChildLocation(before);
if(loc == -1) {
throw new IllegalArgumentException("Asked to add a new child before another record, but that record wasn't one of our children!");
}
// Add at the place of the supplied record
addChildAt(newChild, loc);
return loc;
}
/**
* Set child records.
@ -212,46 +212,46 @@ public abstract class RecordContainer extends Record
this._children = records.clone();
}
/* ===============================================================
* External Serialisation Methods
* ===============================================================
*/
/* ===============================================================
* External Serialisation Methods
* ===============================================================
*/
/**
* Write out our header, and our children.
* @param headerA the first byte of the header
* @param headerB the second byte of the header
* @param type the record type
* @param children our child records
* @param out the stream to write to
*/
public void writeOut(byte headerA, byte headerB, long type, Record[] children, OutputStream out) throws IOException {
// Create a UnsynchronizedByteArrayOutputStream to hold everything in
UnsynchronizedByteArrayOutputStream baos = new UnsynchronizedByteArrayOutputStream();
/**
* Write out our header, and our children.
* @param headerA the first byte of the header
* @param headerB the second byte of the header
* @param type the record type
* @param children our child records
* @param out the stream to write to
*/
public void writeOut(byte headerA, byte headerB, long type, Record[] children, OutputStream out) throws IOException {
// Create a UnsynchronizedByteArrayOutputStream to hold everything in
UnsynchronizedByteArrayOutputStream baos = new UnsynchronizedByteArrayOutputStream();
// Write out our header, less the size
baos.write(new byte[] {headerA,headerB});
byte[] typeB = new byte[2];
LittleEndian.putShort(typeB,0,(short)type);
baos.write(typeB);
baos.write(new byte[] {0,0,0,0});
// Write out our header, less the size
baos.write(new byte[] {headerA,headerB});
byte[] typeB = new byte[2];
LittleEndian.putShort(typeB,0,(short)type);
baos.write(typeB);
baos.write(new byte[] {0,0,0,0});
// Write out our children
for (Record aChildren : children) {
aChildren.writeOut(baos);
}
// Write out our children
for (Record aChildren : children) {
aChildren.writeOut(baos);
}
// Grab the bytes back
byte[] toWrite = baos.toByteArray();
// Grab the bytes back
byte[] toWrite = baos.toByteArray();
// Update our header with the size
// Don't forget to knock 8 more off, since we don't include the
// header in the size
LittleEndian.putInt(toWrite,4,(toWrite.length-8));
// Update our header with the size
// Don't forget to knock 8 more off, since we don't include the
// header in the size
LittleEndian.putInt(toWrite,4,(toWrite.length-8));
// Write out the bytes
out.write(toWrite);
}
// Write out the bytes
out.write(toWrite);
}
/**
* Find the records that are parent-aware, and tell them who their parent is
@ -270,8 +270,8 @@ public abstract class RecordContainer extends Record
}
}
@Override
public Map<String, Supplier<?>> getGenericProperties() {
return null;
}
@Override
public Map<String, Supplier<?>> getGenericProperties() {
return null;
}
}

View File

@ -30,84 +30,84 @@ import org.apache.poi.util.LittleEndian;
public final class Slide extends SheetContainer
{
private byte[] _header;
private static long _type = 1006l;
private byte[] _header;
private static long _type = 1006l;
// Links to our more interesting children
private SlideAtom slideAtom;
private PPDrawing ppDrawing;
// Links to our more interesting children
private SlideAtom slideAtom;
private PPDrawing ppDrawing;
private ColorSchemeAtom _colorScheme;
/**
* Returns the SlideAtom of this Slide
*/
public SlideAtom getSlideAtom() { return slideAtom; }
/**
* Returns the SlideAtom of this Slide
*/
public SlideAtom getSlideAtom() { return slideAtom; }
/**
* Returns the PPDrawing of this Slide, which has all the
* interesting data in it
*/
public PPDrawing getPPDrawing() { return ppDrawing; }
/**
* Returns the PPDrawing of this Slide, which has all the
* interesting data in it
*/
public PPDrawing getPPDrawing() { return ppDrawing; }
/**
* Set things up, and find our more interesting children
*/
protected Slide(byte[] source, int start, int len) {
// Grab the header
_header = Arrays.copyOfRange(source, start, start+8);
/**
* Set things up, and find our more interesting children
*/
protected Slide(byte[] source, int start, int len) {
// Grab the header
_header = Arrays.copyOfRange(source, start, start+8);
// Find our children
_children = Record.findChildRecords(source,start+8,len-8);
// Find our children
_children = Record.findChildRecords(source,start+8,len-8);
// Find the interesting ones in there
for (Record child : _children) {
if (child instanceof SlideAtom) {
slideAtom = (SlideAtom) child;
} else if (child instanceof PPDrawing) {
ppDrawing = (PPDrawing) child;
}
// Find the interesting ones in there
for (Record child : _children) {
if (child instanceof SlideAtom) {
slideAtom = (SlideAtom) child;
} else if (child instanceof PPDrawing) {
ppDrawing = (PPDrawing) child;
}
if (ppDrawing != null && child instanceof ColorSchemeAtom) {
_colorScheme = (ColorSchemeAtom) child;
}
}
}
if (ppDrawing != null && child instanceof ColorSchemeAtom) {
_colorScheme = (ColorSchemeAtom) child;
}
}
}
/**
* Create a new, empty, Slide, along with its required
* child records.
*/
public Slide(){
_header = new byte[8];
LittleEndian.putUShort(_header, 0, 15);
LittleEndian.putUShort(_header, 2, (int)_type);
LittleEndian.putInt(_header, 4, 0);
/**
* Create a new, empty, Slide, along with its required
* child records.
*/
public Slide(){
_header = new byte[8];
LittleEndian.putUShort(_header, 0, 15);
LittleEndian.putUShort(_header, 2, (int)_type);
LittleEndian.putInt(_header, 4, 0);
slideAtom = new SlideAtom();
ppDrawing = new PPDrawing();
slideAtom = new SlideAtom();
ppDrawing = new PPDrawing();
ColorSchemeAtom colorAtom = new ColorSchemeAtom();
ColorSchemeAtom colorAtom = new ColorSchemeAtom();
_children = new org.apache.poi.hslf.record.Record[] {
slideAtom,
ppDrawing,
colorAtom
};
}
_children = new org.apache.poi.hslf.record.Record[] {
slideAtom,
ppDrawing,
colorAtom
};
}
/**
* We are of type 1006
*/
public long getRecordType() { return _type; }
/**
* We are of type 1006
*/
public long getRecordType() { return _type; }
/**
* Write the contents of the record back, so it can be written
* to disk
*/
public void writeOut(OutputStream out) throws IOException {
writeOut(_header[0],_header[1],_type,_children,out);
}
/**
* Write the contents of the record back, so it can be written
* to disk
*/
public void writeOut(OutputStream out) throws IOException {
writeOut(_header[0],_header[1],_type,_children,out);
}
public ColorSchemeAtom getColorScheme(){
return _colorScheme;

View File

@ -38,137 +38,137 @@ public final class SlideAtom extends RecordAtom {
public static final int USES_MASTER_SLIDE_ID = 0x80000000;
// private static final int MASTER_SLIDE_ID = 0x00000000;
//arbitrarily selected; may need to increase
private static final int MAX_RECORD_LENGTH = 1_000_000;
//arbitrarily selected; may need to increase
private static final int MAX_RECORD_LENGTH = 1_000_000;
private byte[] _header;
private static long _type = 1007l;
private byte[] _header;
private static long _type = 1007l;
private int masterID;
private int notesID;
private int masterID;
private int notesID;
private boolean followMasterObjects;
private boolean followMasterScheme;
private boolean followMasterBackground;
private SlideAtomLayout layoutAtom;
private byte[] reserved;
private boolean followMasterObjects;
private boolean followMasterScheme;
private boolean followMasterBackground;
private SlideAtomLayout layoutAtom;
private byte[] reserved;
/** Get the ID of the master slide used. 0 if this is a master slide, otherwise -2147483648 */
public int getMasterID() { return masterID; }
/** Get the ID of the master slide used. 0 if this is a master slide, otherwise -2147483648 */
public int getMasterID() { return masterID; }
/** Change slide master. */
public void setMasterID(int id) { masterID = id; }
/** Get the ID of the notes for this slide. 0 if doesn't have one */
public int getNotesID() { return notesID; }
/** Get the embedded SSlideLayoutAtom */
public SlideAtomLayout getSSlideLayoutAtom() { return layoutAtom; }
/** Get the ID of the notes for this slide. 0 if doesn't have one */
public int getNotesID() { return notesID; }
/** Get the embedded SSlideLayoutAtom */
public SlideAtomLayout getSSlideLayoutAtom() { return layoutAtom; }
/** Change the ID of the notes for this slide. 0 if it no longer has one */
public void setNotesID(int id) { notesID = id; }
/** Change the ID of the notes for this slide. 0 if it no longer has one */
public void setNotesID(int id) { notesID = id; }
public boolean getFollowMasterObjects() { return followMasterObjects; }
public boolean getFollowMasterScheme() { return followMasterScheme; }
public boolean getFollowMasterBackground() { return followMasterBackground; }
public void setFollowMasterObjects(boolean flag) { followMasterObjects = flag; }
public void setFollowMasterScheme(boolean flag) { followMasterScheme = flag; }
public void setFollowMasterBackground(boolean flag) { followMasterBackground = flag; }
public boolean getFollowMasterObjects() { return followMasterObjects; }
public boolean getFollowMasterScheme() { return followMasterScheme; }
public boolean getFollowMasterBackground() { return followMasterBackground; }
public void setFollowMasterObjects(boolean flag) { followMasterObjects = flag; }
public void setFollowMasterScheme(boolean flag) { followMasterScheme = flag; }
public void setFollowMasterBackground(boolean flag) { followMasterBackground = flag; }
/* *************** record code follows ********************** */
/* *************** record code follows ********************** */
/**
* For the Slide Atom
*/
protected SlideAtom(byte[] source, int start, int len) {
// Sanity Checking
if(len < 30) { len = 30; }
/**
* For the Slide Atom
*/
protected SlideAtom(byte[] source, int start, int len) {
// Sanity Checking
if(len < 30) { len = 30; }
// Get the header
_header = Arrays.copyOfRange(source, start, start+8);
// Get the header
_header = Arrays.copyOfRange(source, start, start+8);
// Grab the 12 bytes that is "SSlideLayoutAtom"
byte[] SSlideLayoutAtomData = Arrays.copyOfRange(source,start+8, start+12+8);
// Use them to build up the SSlideLayoutAtom
layoutAtom = new SlideAtomLayout(SSlideLayoutAtomData);
// Grab the 12 bytes that is "SSlideLayoutAtom"
byte[] SSlideLayoutAtomData = Arrays.copyOfRange(source,start+8, start+12+8);
// Use them to build up the SSlideLayoutAtom
layoutAtom = new SlideAtomLayout(SSlideLayoutAtomData);
// Get the IDs of the master and notes
masterID = LittleEndian.getInt(source,start+12+8);
notesID = LittleEndian.getInt(source,start+16+8);
// Get the IDs of the master and notes
masterID = LittleEndian.getInt(source,start+12+8);
notesID = LittleEndian.getInt(source,start+16+8);
// Grok the flags, stored as bits
int flags = LittleEndian.getUShort(source,start+20+8);
followMasterBackground = (flags & 4) == 4;
followMasterScheme = (flags & 2) == 2;
followMasterObjects = (flags & 1) == 1;
// Grok the flags, stored as bits
int flags = LittleEndian.getUShort(source,start+20+8);
followMasterBackground = (flags & 4) == 4;
followMasterScheme = (flags & 2) == 2;
followMasterObjects = (flags & 1) == 1;
// If there's any other bits of data, keep them about
// 8 bytes header + 20 bytes to flags + 2 bytes flags = 30 bytes
reserved = IOUtils.safelyClone(source,start+30, len-30, MAX_RECORD_LENGTH);
}
// If there's any other bits of data, keep them about
// 8 bytes header + 20 bytes to flags + 2 bytes flags = 30 bytes
reserved = IOUtils.safelyClone(source,start+30, len-30, MAX_RECORD_LENGTH);
}
/**
* Create a new SlideAtom, to go with a new Slide
*/
public SlideAtom(){
_header = new byte[8];
LittleEndian.putUShort(_header, 0, 2);
LittleEndian.putUShort(_header, 2, (int)_type);
LittleEndian.putInt(_header, 4, 24);
/**
* Create a new SlideAtom, to go with a new Slide
*/
public SlideAtom(){
_header = new byte[8];
LittleEndian.putUShort(_header, 0, 2);
LittleEndian.putUShort(_header, 2, (int)_type);
LittleEndian.putInt(_header, 4, 24);
byte[] ssdate = new byte[12];
layoutAtom = new SlideAtomLayout(ssdate);
layoutAtom.setGeometryType(SlideLayoutType.BLANK_SLIDE);
byte[] ssdate = new byte[12];
layoutAtom = new SlideAtomLayout(ssdate);
layoutAtom.setGeometryType(SlideLayoutType.BLANK_SLIDE);
followMasterObjects = true;
followMasterScheme = true;
followMasterBackground = true;
masterID = USES_MASTER_SLIDE_ID; // -2147483648;
notesID = 0;
reserved = new byte[2];
}
followMasterObjects = true;
followMasterScheme = true;
followMasterBackground = true;
masterID = USES_MASTER_SLIDE_ID; // -2147483648;
notesID = 0;
reserved = new byte[2];
}
/**
* We are of type 1007
*/
@Override
/**
* We are of type 1007
*/
@Override
public long getRecordType() { return _type; }
/**
* Write the contents of the record back, so it can be written
* to disk
*/
@Override
/**
* Write the contents of the record back, so it can be written
* to disk
*/
@Override
public void writeOut(OutputStream out) throws IOException {
// Header
out.write(_header);
// Header
out.write(_header);
// SSSlideLayoutAtom stuff
layoutAtom.writeOut(out);
// SSSlideLayoutAtom stuff
layoutAtom.writeOut(out);
// IDs
writeLittleEndian(masterID,out);
writeLittleEndian(notesID,out);
// IDs
writeLittleEndian(masterID,out);
writeLittleEndian(notesID,out);
// Flags
short flags = 0;
if(followMasterObjects) { flags += 1; }
if(followMasterScheme) { flags += 2; }
if(followMasterBackground) { flags += 4; }
writeLittleEndian(flags,out);
// Flags
short flags = 0;
if(followMasterObjects) { flags += 1; }
if(followMasterScheme) { flags += 2; }
if(followMasterBackground) { flags += 4; }
writeLittleEndian(flags,out);
// Reserved data
out.write(reserved);
}
// Reserved data
out.write(reserved);
}
@Override
public Map<String, Supplier<?>> getGenericProperties() {
return GenericRecordUtil.getGenericProperties(
"masterID", this::getMasterID,
"notesID", this::getNotesID,
"followMasterObjects", this::getFollowMasterObjects,
"followMasterScheme", this::getFollowMasterScheme,
"followMasterBackground", this::getFollowMasterBackground,
"layoutAtom", this::getSSlideLayoutAtom
);
}
@Override
public Map<String, Supplier<?>> getGenericProperties() {
return GenericRecordUtil.getGenericProperties(
"masterID", this::getMasterID,
"notesID", this::getNotesID,
"followMasterObjects", this::getFollowMasterObjects,
"followMasterScheme", this::getFollowMasterScheme,
"followMasterBackground", this::getFollowMasterBackground,
"layoutAtom", this::getSSlideLayoutAtom
);
}
}

View File

@ -46,155 +46,155 @@ import org.apache.poi.util.LittleEndian;
// For now, pretend to be an atom
public final class SlideListWithText extends RecordContainer {
private static final long _type = RecordTypes.SlideListWithText.typeID;
private static final long _type = RecordTypes.SlideListWithText.typeID;
/**
* Instance filed of the record header indicates that this SlideListWithText stores
* references to slides
*/
public static final int SLIDES = 0;
/**
* Instance filed of the record header indicates that this SlideListWithText stores
* references to master slides
*/
public static final int MASTER = 1;
/**
* Instance filed of the record header indicates that this SlideListWithText stores
* references to notes
*/
public static final int NOTES = 2;
/**
* Instance filed of the record header indicates that this SlideListWithText stores
* references to slides
*/
public static final int SLIDES = 0;
/**
* Instance filed of the record header indicates that this SlideListWithText stores
* references to master slides
*/
public static final int MASTER = 1;
/**
* Instance filed of the record header indicates that this SlideListWithText stores
* references to notes
*/
public static final int NOTES = 2;
private final byte[] _header;
private final byte[] _header;
private SlideAtomsSet[] slideAtomsSets;
private SlideAtomsSet[] slideAtomsSets;
/**
* Create a new holder for slide records
*/
protected SlideListWithText(byte[] source, int start, int len) {
// Grab the header
_header = Arrays.copyOfRange(source, start, start+8);
/**
* Create a new holder for slide records
*/
protected SlideListWithText(byte[] source, int start, int len) {
// Grab the header
_header = Arrays.copyOfRange(source, start, start+8);
// Find our children
_children = Record.findChildRecords(source,start+8,len-8);
// Find our children
_children = Record.findChildRecords(source,start+8,len-8);
// Group our children together into SlideAtomsSets
// That way, model layer code can just grab the sets to use,
// without having to try to match the children together
List<SlideAtomsSet> sets = new ArrayList<>();
for(int i=0; i<_children.length; i++) {
if(_children[i] instanceof SlidePersistAtom) {
// Find where the next SlidePersistAtom is
int endPos = i+1;
while(endPos < _children.length && !(_children[endPos] instanceof SlidePersistAtom)) {
endPos += 1;
}
// Group our children together into SlideAtomsSets
// That way, model layer code can just grab the sets to use,
// without having to try to match the children together
List<SlideAtomsSet> sets = new ArrayList<>();
for(int i=0; i<_children.length; i++) {
if(_children[i] instanceof SlidePersistAtom) {
// Find where the next SlidePersistAtom is
int endPos = i+1;
while(endPos < _children.length && !(_children[endPos] instanceof SlidePersistAtom)) {
endPos += 1;
}
int clen = endPos - i - 1;
int clen = endPos - i - 1;
// Create a SlideAtomsSets, not caring if they're empty
//if(emptySet) { continue; }
org.apache.poi.hslf.record.Record[] spaChildren = Arrays.copyOfRange(_children,i+1, i+1+clen, org.apache.poi.hslf.record.Record[].class);
SlideAtomsSet set = new SlideAtomsSet((SlidePersistAtom)_children[i],spaChildren);
sets.add(set);
// Create a SlideAtomsSets, not caring if they're empty
//if(emptySet) { continue; }
org.apache.poi.hslf.record.Record[] spaChildren = Arrays.copyOfRange(_children,i+1, i+1+clen, org.apache.poi.hslf.record.Record[].class);
SlideAtomsSet set = new SlideAtomsSet((SlidePersistAtom)_children[i],spaChildren);
sets.add(set);
// Wind on
i += clen;
}
}
// Wind on
i += clen;
}
}
// Turn the list into an array
slideAtomsSets = sets.toArray(new SlideAtomsSet[0]);
}
/**
* Create a new, empty, SlideListWithText
*/
public SlideListWithText(){
_header = new byte[8];
LittleEndian.putUShort(_header, 0, 15);
LittleEndian.putUShort(_header, 2, (int)_type);
LittleEndian.putInt(_header, 4, 0);
// We have no children to start with
_children = new org.apache.poi.hslf.record.Record[0];
slideAtomsSets = new SlideAtomsSet[0];
}
/**
* Add a new SlidePersistAtom, to the end of the current list,
* and update the internal list of SlidePersistAtoms
*/
public void addSlidePersistAtom(SlidePersistAtom spa) {
// Add the new SlidePersistAtom at the end
appendChildRecord(spa);
SlideAtomsSet newSAS = new SlideAtomsSet(spa, new org.apache.poi.hslf.record.Record[0]);
// Update our SlideAtomsSets with this
SlideAtomsSet[] sas = new SlideAtomsSet[slideAtomsSets.length+1];
System.arraycopy(slideAtomsSets, 0, sas, 0, slideAtomsSets.length);
sas[sas.length-1] = newSAS;
slideAtomsSets = sas;
}
public int getInstance(){
return LittleEndian.getShort(_header, 0) >> 4;
}
public void setInstance(int inst){
LittleEndian.putShort(_header, 0, (short)((inst << 4) | 0xF));
}
/**
* Get access to the SlideAtomsSets of the children of this record
*/
public SlideAtomsSet[] getSlideAtomsSets() {
return slideAtomsSets;
// Turn the list into an array
slideAtomsSets = sets.toArray(new SlideAtomsSet[0]);
}
/**
* Get access to the SlideAtomsSets of the children of this record
*/
public void setSlideAtomsSets( SlideAtomsSet[] sas ) {
slideAtomsSets = sas.clone();
/**
* Create a new, empty, SlideListWithText
*/
public SlideListWithText(){
_header = new byte[8];
LittleEndian.putUShort(_header, 0, 15);
LittleEndian.putUShort(_header, 2, (int)_type);
LittleEndian.putInt(_header, 4, 0);
// We have no children to start with
_children = new org.apache.poi.hslf.record.Record[0];
slideAtomsSets = new SlideAtomsSet[0];
}
/**
* Return the value we were given at creation
*/
@Override
public long getRecordType() { return _type; }
/**
* Add a new SlidePersistAtom, to the end of the current list,
* and update the internal list of SlidePersistAtoms
*/
public void addSlidePersistAtom(SlidePersistAtom spa) {
// Add the new SlidePersistAtom at the end
appendChildRecord(spa);
/**
* Write the contents of the record back, so it can be written
* to disk
*/
@Override
public void writeOut(OutputStream out) throws IOException {
writeOut(_header[0],_header[1],_type,_children,out);
}
SlideAtomsSet newSAS = new SlideAtomsSet(spa, new org.apache.poi.hslf.record.Record[0]);
/**
* Inner class to wrap up a matching set of records that hold the
* text for a given sheet. Contains the leading SlidePersistAtom,
* and all of the records until the next SlidePersistAtom. This
* includes sets of TextHeaderAtom and TextBytesAtom/TextCharsAtom,
* along with some others.
*/
public static class SlideAtomsSet {
private final SlidePersistAtom slidePersistAtom;
private final org.apache.poi.hslf.record.Record[] slideRecords;
// Update our SlideAtomsSets with this
SlideAtomsSet[] sas = new SlideAtomsSet[slideAtomsSets.length+1];
System.arraycopy(slideAtomsSets, 0, sas, 0, slideAtomsSets.length);
sas[sas.length-1] = newSAS;
slideAtomsSets = sas;
}
/** Get the SlidePersistAtom, which gives details on the Slide this text is associated with */
public SlidePersistAtom getSlidePersistAtom() { return slidePersistAtom; }
/** Get the Text related records for this slide */
public org.apache.poi.hslf.record.Record[] getSlideRecords() { return slideRecords; }
public int getInstance(){
return LittleEndian.getShort(_header, 0) >> 4;
}
/** Create one to hold the Records for one Slide's text */
public SlideAtomsSet(SlidePersistAtom s, org.apache.poi.hslf.record.Record[] r) {
slidePersistAtom = s;
slideRecords = r.clone();
}
}
public void setInstance(int inst){
LittleEndian.putShort(_header, 0, (short)((inst << 4) | 0xF));
}
/**
* Get access to the SlideAtomsSets of the children of this record
*/
public SlideAtomsSet[] getSlideAtomsSets() {
return slideAtomsSets;
}
/**
* Get access to the SlideAtomsSets of the children of this record
*/
public void setSlideAtomsSets( SlideAtomsSet[] sas ) {
slideAtomsSets = sas.clone();
}
/**
* Return the value we were given at creation
*/
@Override
public long getRecordType() { return _type; }
/**
* Write the contents of the record back, so it can be written
* to disk
*/
@Override
public void writeOut(OutputStream out) throws IOException {
writeOut(_header[0],_header[1],_type,_children,out);
}
/**
* Inner class to wrap up a matching set of records that hold the
* text for a given sheet. Contains the leading SlidePersistAtom,
* and all of the records until the next SlidePersistAtom. This
* includes sets of TextHeaderAtom and TextBytesAtom/TextCharsAtom,
* along with some others.
*/
public static class SlideAtomsSet {
private final SlidePersistAtom slidePersistAtom;
private final org.apache.poi.hslf.record.Record[] slideRecords;
/** Get the SlidePersistAtom, which gives details on the Slide this text is associated with */
public SlidePersistAtom getSlidePersistAtom() { return slidePersistAtom; }
/** Get the Text related records for this slide */
public org.apache.poi.hslf.record.Record[] getSlideRecords() { return slideRecords; }
/** Create one to hold the Records for one Slide's text */
public SlideAtomsSet(SlidePersistAtom s, org.apache.poi.hslf.record.Record[] r) {
slidePersistAtom = s;
slideRecords = r.clone();
}
}
}

View File

@ -35,123 +35,123 @@ import org.apache.poi.util.LittleEndian;
*/
public final class SlidePersistAtom extends RecordAtom {
//arbitrarily selected; may need to increase
private static final int MAX_RECORD_LENGTH = 32;
//arbitrarily selected; may need to increase
private static final int MAX_RECORD_LENGTH = 32;
private static final long _type = 1011l;
private static final int HAS_SHAPES_OTHER_THAN_PLACEHOLDERS = 4;
private static final long _type = 1011l;
private static final int HAS_SHAPES_OTHER_THAN_PLACEHOLDERS = 4;
private static final int[] FLAGS_MASKS = { HAS_SHAPES_OTHER_THAN_PLACEHOLDERS };
private static final String[] FLAGS_NAMES = { "HAS_SHAPES_OTHER_THAN_PLACEHOLDERS" };
private static final int[] FLAGS_MASKS = { HAS_SHAPES_OTHER_THAN_PLACEHOLDERS };
private static final String[] FLAGS_NAMES = { "HAS_SHAPES_OTHER_THAN_PLACEHOLDERS" };
private final byte[] _header;
private final byte[] _header;
/** Slide reference ID. Should correspond to the PersistPtr "sheet ID" of the matching slide/notes record */
private int refID;
private int flags;
/** Slide reference ID. Should correspond to the PersistPtr "sheet ID" of the matching slide/notes record */
private int refID;
private int flags;
/** Number of placeholder texts that will follow in the SlideListWithText */
private int numPlaceholderTexts;
/** The internal identifier (256+), which is used to tie slides and notes together */
private int slideIdentifier;
/** Reserved fields. Who knows what they do */
private byte[] reservedFields;
/** Number of placeholder texts that will follow in the SlideListWithText */
private int numPlaceholderTexts;
/** The internal identifier (256+), which is used to tie slides and notes together */
private int slideIdentifier;
/** Reserved fields. Who knows what they do */
private byte[] reservedFields;
public int getRefID() {
return refID;
}
public int getRefID() {
return refID;
}
public int getSlideIdentifier() {
return slideIdentifier;
}
public int getSlideIdentifier() {
return slideIdentifier;
}
public int getNumPlaceholderTexts() {
return numPlaceholderTexts;
}
public int getNumPlaceholderTexts() {
return numPlaceholderTexts;
}
public boolean getHasShapesOtherThanPlaceholders() {
return (flags & HAS_SHAPES_OTHER_THAN_PLACEHOLDERS) != 0;
}
public boolean getHasShapesOtherThanPlaceholders() {
return (flags & HAS_SHAPES_OTHER_THAN_PLACEHOLDERS) != 0;
}
// Only set these if you know what you're doing!
public void setRefID(int id) {
refID = id;
}
public void setSlideIdentifier(int id) {
slideIdentifier = id;
}
// Only set these if you know what you're doing!
public void setRefID(int id) {
refID = id;
}
public void setSlideIdentifier(int id) {
slideIdentifier = id;
}
/* *************** record code follows ********************** */
/* *************** record code follows ********************** */
/**
* For the SlidePersist Atom
*/
protected SlidePersistAtom(byte[] source, int start, int len) {
// Sanity Checking
if(len < 8) { len = 8; }
/**
* For the SlidePersist Atom
*/
protected SlidePersistAtom(byte[] source, int start, int len) {
// Sanity Checking
if(len < 8) { len = 8; }
// Get the header
_header = Arrays.copyOfRange(source, start, start+8);
// Get the header
_header = Arrays.copyOfRange(source, start, start+8);
// Grab the reference ID
refID = LittleEndian.getInt(source,start+8);
// Grab the reference ID
refID = LittleEndian.getInt(source,start+8);
// Next up is a set of flags, but only bit 3 is used!
flags = LittleEndian.getInt(source,start+12);
// Next up is a set of flags, but only bit 3 is used!
flags = LittleEndian.getInt(source,start+12);
// Now the number of Placeholder Texts
numPlaceholderTexts = LittleEndian.getInt(source,start+16);
// Now the number of Placeholder Texts
numPlaceholderTexts = LittleEndian.getInt(source,start+16);
// Last useful one is the unique slide identifier
slideIdentifier = LittleEndian.getInt(source,start+20);
// Last useful one is the unique slide identifier
slideIdentifier = LittleEndian.getInt(source,start+20);
// Finally you have typically 4 or 8 bytes of reserved fields,
// all zero running from 24 bytes in to the end
reservedFields = IOUtils.safelyClone(source,start+24, len-24, MAX_RECORD_LENGTH);
}
// Finally you have typically 4 or 8 bytes of reserved fields,
// all zero running from 24 bytes in to the end
reservedFields = IOUtils.safelyClone(source,start+24, len-24, MAX_RECORD_LENGTH);
}
/**
* Create a new SlidePersistAtom, for use with a new Slide
*/
public SlidePersistAtom() {
_header = new byte[8];
LittleEndian.putUShort(_header, 0, 0);
LittleEndian.putUShort(_header, 2, (int)_type);
LittleEndian.putInt(_header, 4, 20);
/**
* Create a new SlidePersistAtom, for use with a new Slide
*/
public SlidePersistAtom() {
_header = new byte[8];
LittleEndian.putUShort(_header, 0, 0);
LittleEndian.putUShort(_header, 2, (int)_type);
LittleEndian.putInt(_header, 4, 20);
flags = HAS_SHAPES_OTHER_THAN_PLACEHOLDERS;
reservedFields = new byte[4];
}
flags = HAS_SHAPES_OTHER_THAN_PLACEHOLDERS;
reservedFields = new byte[4];
}
/**
* We are of type 1011
*/
public long getRecordType() { return _type; }
/**
* We are of type 1011
*/
public long getRecordType() { return _type; }
/**
* Write the contents of the record back, so it can be written
* to disk
*/
public void writeOut(OutputStream out) throws IOException {
// Header - size or type unchanged
out.write(_header);
/**
* Write the contents of the record back, so it can be written
* to disk
*/
public void writeOut(OutputStream out) throws IOException {
// Header - size or type unchanged
out.write(_header);
// Write out our fields
writeLittleEndian(refID,out);
writeLittleEndian(flags,out);
writeLittleEndian(numPlaceholderTexts,out);
writeLittleEndian(slideIdentifier,out);
out.write(reservedFields);
}
// Write out our fields
writeLittleEndian(refID,out);
writeLittleEndian(flags,out);
writeLittleEndian(numPlaceholderTexts,out);
writeLittleEndian(slideIdentifier,out);
out.write(reservedFields);
}
@Override
public Map<String, Supplier<?>> getGenericProperties() {
return GenericRecordUtil.getGenericProperties(
"refID", this::getRefID,
"flags", getBitsAsString(() -> flags, FLAGS_MASKS, FLAGS_NAMES),
"numPlaceholderTexts", this::getNumPlaceholderTexts,
"slideIdentifier", this::getSlideIdentifier
);
}
@Override
public Map<String, Supplier<?>> getGenericProperties() {
return GenericRecordUtil.getGenericProperties(
"refID", this::getRefID,
"flags", getBitsAsString(() -> flags, FLAGS_MASKS, FLAGS_NAMES),
"numPlaceholderTexts", this::getNumPlaceholderTexts,
"slideIdentifier", this::getSlideIdentifier
);
}
}

View File

@ -38,88 +38,88 @@ import org.apache.poi.util.StringUtil;
public final class TextBytesAtom extends RecordAtom {
public static final long _type = RecordTypes.TextBytesAtom.typeID;
//arbitrarily selected; may need to increase
private static final int MAX_RECORD_LENGTH = 1_000_000;
//arbitrarily selected; may need to increase
private static final int MAX_RECORD_LENGTH = 1_000_000;
private byte[] _header;
private byte[] _header;
/** The bytes that make up the text */
private byte[] _text;
/** The bytes that make up the text */
private byte[] _text;
/** Grabs the text. Uses the default codepage */
public String getText() {
return StringUtil.getFromCompressedUnicode(_text,0,_text.length);
}
/** Grabs the text. Uses the default codepage */
public String getText() {
return StringUtil.getFromCompressedUnicode(_text,0,_text.length);
}
/** Updates the text in the Atom. Must be 8 bit ascii */
public void setText(byte[] b) {
// Set the text
_text = b.clone();
/** Updates the text in the Atom. Must be 8 bit ascii */
public void setText(byte[] b) {
// Set the text
_text = b.clone();
// Update the size (header bytes 5-8)
LittleEndian.putInt(_header,4,_text.length);
}
// Update the size (header bytes 5-8)
LittleEndian.putInt(_header,4,_text.length);
}
/* *************** record code follows ********************** */
/* *************** record code follows ********************** */
/**
* For the TextBytes Atom
*/
protected TextBytesAtom(byte[] source, int start, int len) {
// Sanity Checking
if(len < 8) { len = 8; }
/**
* For the TextBytes Atom
*/
protected TextBytesAtom(byte[] source, int start, int len) {
// Sanity Checking
if(len < 8) { len = 8; }
// Get the header
_header = Arrays.copyOfRange(source, start, start+8);
// Get the header
_header = Arrays.copyOfRange(source, start, start+8);
// Grab the text
_text = IOUtils.safelyClone(source, start+8, len-8, MAX_RECORD_LENGTH);
}
// Grab the text
_text = IOUtils.safelyClone(source, start+8, len-8, MAX_RECORD_LENGTH);
}
/**
* Create an empty TextBytes Atom
*/
public TextBytesAtom() {
_header = new byte[8];
LittleEndian.putUShort(_header, 0, 0);
LittleEndian.putUShort(_header, 2, (int)_type);
LittleEndian.putInt(_header, 4, 0);
/**
* Create an empty TextBytes Atom
*/
public TextBytesAtom() {
_header = new byte[8];
LittleEndian.putUShort(_header, 0, 0);
LittleEndian.putUShort(_header, 2, (int)_type);
LittleEndian.putInt(_header, 4, 0);
_text = new byte[]{};
}
_text = new byte[]{};
}
/**
* We are of type 4008
*/
@Override
/**
* We are of type 4008
*/
@Override
public long getRecordType() { return _type; }
/**
* Write the contents of the record back, so it can be written
* to disk
*/
@Override
/**
* Write the contents of the record back, so it can be written
* to disk
*/
@Override
public void writeOut(OutputStream out) throws IOException {
// Header - size or type unchanged
out.write(_header);
// Header - size or type unchanged
out.write(_header);
// Write out our text
out.write(_text);
}
// Write out our text
out.write(_text);
}
/**
* dump debug info; use getText() to return a string
* representation of the atom
*/
@Override
/**
* dump debug info; use getText() to return a string
* representation of the atom
*/
@Override
public String toString() {
return GenericRecordJsonWriter.marshal(this);
}
return GenericRecordJsonWriter.marshal(this);
}
@Override
public Map<String, Supplier<?>> getGenericProperties() {
return GenericRecordUtil.getGenericProperties(
"text", this::getText
);
}
@Override
public Map<String, Supplier<?>> getGenericProperties() {
return GenericRecordUtil.getGenericProperties(
"text", this::getText
);
}
}

View File

@ -36,86 +36,86 @@ import org.apache.poi.util.StringUtil;
public final class TextCharsAtom extends RecordAtom {
public static final long _type = RecordTypes.TextCharsAtom.typeID;
//arbitrarily selected; may need to increase
private static final int MAX_RECORD_LENGTH = 1_000_000;
//arbitrarily selected; may need to increase
private static final int MAX_RECORD_LENGTH = 1_000_000;
private byte[] _header;
private byte[] _header;
/** The bytes that make up the text */
private byte[] _text;
/** The bytes that make up the text */
private byte[] _text;
/** Grabs the text. */
public String getText() {
return StringUtil.getFromUnicodeLE(_text);
}
/** Grabs the text. */
public String getText() {
return StringUtil.getFromUnicodeLE(_text);
}
/** Updates the text in the Atom. */
public void setText(String text) {
// Convert to little endian unicode
_text = IOUtils.safelyAllocate(text.length() * 2L, MAX_RECORD_LENGTH);
StringUtil.putUnicodeLE(text,_text,0);
/** Updates the text in the Atom. */
public void setText(String text) {
// Convert to little endian unicode
_text = IOUtils.safelyAllocate(text.length() * 2L, MAX_RECORD_LENGTH);
StringUtil.putUnicodeLE(text,_text,0);
// Update the size (header bytes 5-8)
LittleEndian.putInt(_header,4,_text.length);
}
// Update the size (header bytes 5-8)
LittleEndian.putInt(_header,4,_text.length);
}
/* *************** record code follows ********************** */
/* *************** record code follows ********************** */
/**
* For the TextChars Atom
*/
protected TextCharsAtom(byte[] source, int start, int len) {
// Sanity Checking
if(len < 8) { len = 8; }
/**
* For the TextChars Atom
*/
protected TextCharsAtom(byte[] source, int start, int len) {
// Sanity Checking
if(len < 8) { len = 8; }
// Get the header
_header = Arrays.copyOfRange(source, start, start+8);
// Get the header
_header = Arrays.copyOfRange(source, start, start+8);
// Grab the text
_text = IOUtils.safelyClone(source, start+8, len-8, MAX_RECORD_LENGTH);
}
/**
* Create an empty TextCharsAtom
*/
public TextCharsAtom() {
// 0 length header
_header = new byte[] { 0, 0, 0xA0-256, 0x0f, 0, 0, 0, 0 };
// Empty text
_text = new byte[0];
}
// Grab the text
_text = IOUtils.safelyClone(source, start+8, len-8, MAX_RECORD_LENGTH);
}
/**
* Create an empty TextCharsAtom
*/
public TextCharsAtom() {
// 0 length header
_header = new byte[] { 0, 0, 0xA0-256, 0x0f, 0, 0, 0, 0 };
// Empty text
_text = new byte[0];
}
/**
* We are of type 4000
*/
@Override
/**
* We are of type 4000
*/
@Override
public long getRecordType() { return _type; }
/**
* Write the contents of the record back, so it can be written
* to disk
*/
@Override
/**
* Write the contents of the record back, so it can be written
* to disk
*/
@Override
public void writeOut(OutputStream out) throws IOException {
// Header - size or type unchanged
out.write(_header);
// Header - size or type unchanged
out.write(_header);
// Write out our text
out.write(_text);
}
// Write out our text
out.write(_text);
}
/**
* dump debug info; use getText() to return a string
* representation of the atom
*/
@Override
/**
* dump debug info; use getText() to return a string
* representation of the atom
*/
@Override
public String toString() {
return GenericRecordJsonWriter.marshal(this);
}
return GenericRecordJsonWriter.marshal(this);
}
@Override
public Map<String, Supplier<?>> getGenericProperties() {
return GenericRecordUtil.getGenericProperties(
"text", this::getText
);
}
@Override
public Map<String, Supplier<?>> getGenericProperties() {
return GenericRecordUtil.getGenericProperties(
"text", this::getText
);
}
}

View File

@ -36,97 +36,97 @@ import org.apache.poi.util.LittleEndian;
public final class TextHeaderAtom extends RecordAtom implements ParentAwareRecord {
public static final long _type = RecordTypes.TextHeaderAtom.typeID;
private byte[] _header;
private org.apache.poi.hslf.record.RecordContainer parentRecord;
private byte[] _header;
private org.apache.poi.hslf.record.RecordContainer parentRecord;
/** The kind of text it is */
private int textType;
/** position in the owning SlideListWithText */
private int index = -1;
/** The kind of text it is */
private int textType;
/** position in the owning SlideListWithText */
private int index = -1;
public int getTextType() { return textType; }
public void setTextType(int type) { textType = type; }
public int getTextType() { return textType; }
public void setTextType(int type) { textType = type; }
public TextPlaceholder getTextTypeEnum() {
return TextPlaceholder.fromNativeId(textType);
}
public TextPlaceholder getTextTypeEnum() {
return TextPlaceholder.fromNativeId(textType);
}
public void setTextTypeEnum(TextPlaceholder placeholder) {
textType = placeholder.nativeId;
}
public void setTextTypeEnum(TextPlaceholder placeholder) {
textType = placeholder.nativeId;
}
/**
* @return 0-based index of the text run in the SLWT container
*/
public int getIndex() { return index; }
public int getIndex() { return index; }
/**
* @param index 0-based index of the text run in the SLWT container
*/
public void setIndex(int index) { this.index = index; }
public void setIndex(int index) { this.index = index; }
@Override
@Override
public org.apache.poi.hslf.record.RecordContainer getParentRecord() { return parentRecord; }
@Override
@Override
public void setParentRecord(RecordContainer record) { this.parentRecord = record; }
/* *************** record code follows ********************** */
/* *************** record code follows ********************** */
/**
* For the TextHeader Atom
*/
protected TextHeaderAtom(byte[] source, int start, int len) {
// Sanity Checking - we're always 12 bytes long
if(len < 12) {
len = 12;
if(source.length - start < 12) {
throw new HSLFException("Not enough data to form a TextHeaderAtom (always 12 bytes long) - found " + (source.length - start));
}
}
/**
* For the TextHeader Atom
*/
protected TextHeaderAtom(byte[] source, int start, int len) {
// Sanity Checking - we're always 12 bytes long
if(len < 12) {
len = 12;
if(source.length - start < 12) {
throw new HSLFException("Not enough data to form a TextHeaderAtom (always 12 bytes long) - found " + (source.length - start));
}
}
// Get the header
_header = Arrays.copyOfRange(source, start, start+8);
// Get the header
_header = Arrays.copyOfRange(source, start, start+8);
// Grab the type
textType = LittleEndian.getInt(source,start+8);
}
// Grab the type
textType = LittleEndian.getInt(source,start+8);
}
/**
* Create a new TextHeader Atom, for an unknown type of text
*/
public TextHeaderAtom() {
_header = new byte[8];
LittleEndian.putUShort(_header, 0, 0);
LittleEndian.putUShort(_header, 2, (int)_type);
LittleEndian.putInt(_header, 4, 4);
/**
* Create a new TextHeader Atom, for an unknown type of text
*/
public TextHeaderAtom() {
_header = new byte[8];
LittleEndian.putUShort(_header, 0, 0);
LittleEndian.putUShort(_header, 2, (int)_type);
LittleEndian.putInt(_header, 4, 4);
textType = TextPlaceholder.OTHER.nativeId;
}
textType = TextPlaceholder.OTHER.nativeId;
}
/**
* We are of type 3999
*/
@Override
/**
* We are of type 3999
*/
@Override
public long getRecordType() { return _type; }
/**
* Write the contents of the record back, so it can be written
* to disk
*/
@Override
/**
* Write the contents of the record back, so it can be written
* to disk
*/
@Override
public void writeOut(OutputStream out) throws IOException {
// Header - size or type unchanged
out.write(_header);
// Header - size or type unchanged
out.write(_header);
// Write out our type
writeLittleEndian(textType,out);
}
// Write out our type
writeLittleEndian(textType,out);
}
@Override
public Map<String, Supplier<?>> getGenericProperties() {
return GenericRecordUtil.getGenericProperties(
"index", this::getIndex,
"textType", this::getTextTypeEnum
);
}
@Override
public Map<String, Supplier<?>> getGenericProperties() {
return GenericRecordUtil.getGenericProperties(
"index", this::getIndex,
"textType", this::getTextTypeEnum
);
}
}

Some files were not shown because too many files have changed in this diff Show More