mirror of
https://github.com/apache/poi.git
synced 2026-02-27 20:40:08 +08:00
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:
parent
db53f9b860
commit
33f6381c43
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 + ">";
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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');
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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; }
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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!");
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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 + "''");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 ) {
|
||||
|
||||
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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; }
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -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; }
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
Loading…
x
Reference in New Issue
Block a user