git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1765696 13f79535-47bb-0310-9956-ffa450edef68tags/REL_3_16_BETA1
import org.apache.poi.util.RLEDecompressingInputStream; | import org.apache.poi.util.RLEDecompressingInputStream; | ||||
/** | /** | ||||
* Finds all VBA Macros in an office file (OLE2/POIFS and OOXML/OPC), | |||||
* <p>Finds all VBA Macros in an office file (OLE2/POIFS and OOXML/OPC), | |||||
* and returns them. | * and returns them. | ||||
* </p> | |||||
* <p> | |||||
* <b>NOTE:</b> This does not read macros from .ppt files. | |||||
* See org.apache.poi.hslf.usermodel.TestBugs.getMacrosFromHSLF() in the scratchpad | |||||
* module for an example of how to do this. Patches that make macro | |||||
* extraction from .ppt more elegant are welcomed! | |||||
* </p> | |||||
* | * | ||||
* @since 3.15-beta2 | * @since 3.15-beta2 | ||||
*/ | */ |
/* ==================================================================== | |||||
Licensed to the Apache Software Foundation (ASF) under one or more | |||||
contributor license agreements. See the NOTICE file distributed with | |||||
this work for additional information regarding copyright ownership. | |||||
The ASF licenses this file to You under the Apache License, Version 2.0 | |||||
(the "License"); you may not use this file except in compliance with | |||||
the License. You may obtain a copy of the License at | |||||
http://www.apache.org/licenses/LICENSE-2.0 | |||||
Unless required by applicable law or agreed to in writing, software | |||||
distributed under the License is distributed on an "AS IS" BASIS, | |||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
See the License for the specific language governing permissions and | |||||
limitations under the License. | |||||
==================================================================== */ | |||||
package org.apache.poi.hslf.record; | |||||
import java.io.IOException; | |||||
import java.io.OutputStream; | |||||
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 long _type = RecordTypes.List.typeID; | |||||
// 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 = new byte[8]; | |||||
System.arraycopy(source,start,_header,0,8); | |||||
// 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() { | |||||
} | |||||
/** | |||||
* Create a new DocInfoListContainer, with blank fields - not yet supported | |||||
*/ | |||||
private DocInfoListContainer() { | |||||
_header = new byte[8]; | |||||
_children = new Record[0]; | |||||
// Setup our header block | |||||
_header[0] = 0x0f; // We are a container record | |||||
LittleEndian.putShort(_header, 2, (short)_type); | |||||
// Setup our child records | |||||
findInterestingChildren(); | |||||
} | |||||
/** | |||||
* 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); | |||||
} | |||||
} |
return RecordTypes.ExOleObjStg.typeID; | return RecordTypes.ExOleObjStg.typeID; | ||||
} | } | ||||
/** | |||||
* Gets the record instance from the header | |||||
* | |||||
* @return record instance | |||||
*/ | |||||
public int getRecordInstance() { | |||||
return (LittleEndian.getUShort(_header, 0) >>> 4); | |||||
} | |||||
/** | /** | ||||
* Write the contents of the record back, so it can be written | * Write the contents of the record back, so it can be written | ||||
* to disk. | * to disk. |
ViewInfo(1020,null), | ViewInfo(1020,null), | ||||
ViewInfoAtom(1021,null), | ViewInfoAtom(1021,null), | ||||
SlideViewInfoAtom(1022,null), | SlideViewInfoAtom(1022,null), | ||||
VBAInfo(1023,null), | |||||
VBAInfoAtom(1024,null), | |||||
VBAInfo(1023,VBAInfoContainer.class), | |||||
VBAInfoAtom(1024,VBAInfoAtom.class), | |||||
SSDocInfoAtom(1025,null), | SSDocInfoAtom(1025,null), | ||||
Summary(1026,null), | Summary(1026,null), | ||||
DocRoutingSlip(1030,null), | DocRoutingSlip(1030,null), | ||||
NamedShowSlides(1042,null), | NamedShowSlides(1042,null), | ||||
SheetProperties(1044,null), | SheetProperties(1044,null), | ||||
RoundTripCustomTableStyles12Atom(1064,null), | RoundTripCustomTableStyles12Atom(1064,null), | ||||
List(2000,null), | |||||
List(2000,DocInfoListContainer.class), | |||||
FontCollection(2005,FontCollection.class), | FontCollection(2005,FontCollection.class), | ||||
BookmarkCollection(2019,null), | BookmarkCollection(2019,null), | ||||
SoundCollection(2020,SoundCollection.class), | SoundCollection(2020,SoundCollection.class), |
/* ==================================================================== | |||||
Licensed to the Apache Software Foundation (ASF) under one or more | |||||
contributor license agreements. See the NOTICE file distributed with | |||||
this work for additional information regarding copyright ownership. | |||||
The ASF licenses this file to You under the Apache License, Version 2.0 | |||||
(the "License"); you may not use this file except in compliance with | |||||
the License. You may obtain a copy of the License at | |||||
http://www.apache.org/licenses/LICENSE-2.0 | |||||
Unless required by applicable law or agreed to in writing, software | |||||
distributed under the License is distributed on an "AS IS" BASIS, | |||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
See the License for the specific language governing permissions and | |||||
limitations under the License. | |||||
==================================================================== */ | |||||
package org.apache.poi.hslf.record; | |||||
import java.io.ByteArrayOutputStream; | |||||
import java.io.IOException; | |||||
import java.io.OutputStream; | |||||
import org.apache.poi.util.LittleEndian; | |||||
/** | |||||
* An atom record that specifies a reference to the VBA project storage. | |||||
*/ | |||||
public final class VBAInfoAtom extends RecordAtom { | |||||
private static final long _type = RecordTypes.VBAInfoAtom.typeID; | |||||
/** | |||||
* Record header. | |||||
*/ | |||||
private byte[] _header; | |||||
/** | |||||
* Record data. | |||||
*/ | |||||
private long persistIdRef; | |||||
private boolean hasMacros; | |||||
private long version; | |||||
/** | |||||
* Constructs an empty atom - not yet supported | |||||
*/ | |||||
private VBAInfoAtom() { | |||||
_header = new byte[8]; | |||||
// TODO: fix me | |||||
LittleEndian.putUInt(_header, 0, _type); | |||||
persistIdRef = 0; | |||||
hasMacros = true; | |||||
version = 2; | |||||
} | |||||
/** | |||||
* Constructs the vba atom record from its source data. | |||||
* | |||||
* @param source the source data as a byte array. | |||||
* @param start the start offset into the byte array. | |||||
* @param len the length of the slice in the byte array. | |||||
*/ | |||||
public VBAInfoAtom(byte[] source, int start, int len) { | |||||
// Get the header. | |||||
_header = new byte[8]; | |||||
System.arraycopy(source,start,_header,0,8); | |||||
// Get the record data. | |||||
persistIdRef = LittleEndian.getUInt(source, start+8); | |||||
hasMacros = (LittleEndian.getUInt(source, start+12) == 1); | |||||
version = LittleEndian.getUInt(source, start+16); | |||||
} | |||||
/** | |||||
* Gets the record type. | |||||
* @return the record type. | |||||
*/ | |||||
public long getRecordType() { return _type; } | |||||
/** | |||||
* Write the contents of the record back, so it can be written | |||||
* to disk | |||||
* | |||||
* @param out the output stream to write to. | |||||
* @throws java.io.IOException if an error occurs. | |||||
*/ | |||||
public void writeOut(OutputStream out) throws IOException { | |||||
out.write(_header); | |||||
LittleEndian.putUInt(persistIdRef, out); | |||||
LittleEndian.putUInt(hasMacros ? 1 : 0, out); | |||||
LittleEndian.putUInt(version, out); | |||||
} | |||||
public long getPersistIdRef() { | |||||
return persistIdRef; | |||||
} | |||||
public void setPersistIdRef(long persistIdRef) { | |||||
this.persistIdRef = persistIdRef; | |||||
} | |||||
public boolean isHasMacros() { | |||||
return hasMacros; | |||||
} | |||||
public void setHasMacros(boolean hasMacros) { | |||||
this.hasMacros = hasMacros; | |||||
} | |||||
public long getVersion() { | |||||
return version; | |||||
} | |||||
public void setVersion(long version) { | |||||
this.version = version; | |||||
} | |||||
} |
/* ==================================================================== | |||||
Licensed to the Apache Software Foundation (ASF) under one or more | |||||
contributor license agreements. See the NOTICE file distributed with | |||||
this work for additional information regarding copyright ownership. | |||||
The ASF licenses this file to You under the Apache License, Version 2.0 | |||||
(the "License"); you may not use this file except in compliance with | |||||
the License. You may obtain a copy of the License at | |||||
http://www.apache.org/licenses/LICENSE-2.0 | |||||
Unless required by applicable law or agreed to in writing, software | |||||
distributed under the License is distributed on an "AS IS" BASIS, | |||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
See the License for the specific language governing permissions and | |||||
limitations under the License. | |||||
==================================================================== */ | |||||
package org.apache.poi.hslf.record; | |||||
import java.io.IOException; | |||||
import java.io.OutputStream; | |||||
import org.apache.poi.util.LittleEndian; | |||||
/** | |||||
* A container record that specifies VBA information for the document. | |||||
*/ | |||||
public final class VBAInfoContainer extends RecordContainer { | |||||
private byte[] _header; | |||||
private static long _type = RecordTypes.VBAInfo.typeID; | |||||
// Links to our more interesting children | |||||
/** | |||||
* Set things up, and find our more interesting children | |||||
*/ | |||||
protected VBAInfoContainer(byte[] source, int start, int len) { | |||||
// Grab the header | |||||
_header = new byte[8]; | |||||
System.arraycopy(source, start, _header, 0, 8); | |||||
// 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() { | |||||
} | |||||
/** | |||||
* Create a new VBAInfoContainer, with blank fields - not yet supported | |||||
*/ | |||||
private VBAInfoContainer() { | |||||
_header = new byte[8]; | |||||
_children = new Record[0]; | |||||
// Setup our header block | |||||
_header[0] = 0x0f; // We are a container record | |||||
LittleEndian.putShort(_header, 2, (short) _type); | |||||
// Setup our child records | |||||
findInterestingChildren(); | |||||
} | |||||
/** | |||||
* We are of type 0x3FF | |||||
*/ | |||||
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); | |||||
} | |||||
} |
package org.apache.poi.hslf.usermodel; | package org.apache.poi.hslf.usermodel; | ||||
import static org.apache.poi.POITestCase.assertContains; | |||||
import static org.junit.Assert.assertEquals; | import static org.junit.Assert.assertEquals; | ||||
import static org.junit.Assert.assertFalse; | import static org.junit.Assert.assertFalse; | ||||
import static org.junit.Assert.assertNotNull; | import static org.junit.Assert.assertNotNull; | ||||
import java.awt.geom.Rectangle2D; | import java.awt.geom.Rectangle2D; | ||||
import java.awt.image.BufferedImage; | import java.awt.image.BufferedImage; | ||||
import java.io.File; | import java.io.File; | ||||
import java.io.FileInputStream; | |||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.io.InputStream; | |||||
import java.io.OutputStream; | import java.io.OutputStream; | ||||
import java.io.PrintStream; | import java.io.PrintStream; | ||||
import java.text.AttributedCharacterIterator; | import java.text.AttributedCharacterIterator; | ||||
import java.util.Map; | import java.util.Map; | ||||
import java.util.Set; | import java.util.Set; | ||||
import org.apache.poi.POIDataSamples; | |||||
import org.apache.poi.ddf.AbstractEscherOptRecord; | import org.apache.poi.ddf.AbstractEscherOptRecord; | ||||
import org.apache.poi.ddf.EscherArrayProperty; | import org.apache.poi.ddf.EscherArrayProperty; | ||||
import org.apache.poi.ddf.EscherColorRef; | import org.apache.poi.ddf.EscherColorRef; | ||||
import org.apache.poi.hslf.exceptions.OldPowerPointFormatException; | import org.apache.poi.hslf.exceptions.OldPowerPointFormatException; | ||||
import org.apache.poi.hslf.extractor.PowerPointExtractor; | import org.apache.poi.hslf.extractor.PowerPointExtractor; | ||||
import org.apache.poi.hslf.model.HeadersFooters; | import org.apache.poi.hslf.model.HeadersFooters; | ||||
import org.apache.poi.hslf.record.DocInfoListContainer; | |||||
import org.apache.poi.hslf.record.Document; | import org.apache.poi.hslf.record.Document; | ||||
import org.apache.poi.hslf.record.Record; | import org.apache.poi.hslf.record.Record; | ||||
import org.apache.poi.hslf.record.RecordTypes; | |||||
import org.apache.poi.hslf.record.SlideListWithText; | import org.apache.poi.hslf.record.SlideListWithText; | ||||
import org.apache.poi.hslf.record.SlideListWithText.SlideAtomsSet; | import org.apache.poi.hslf.record.SlideListWithText.SlideAtomsSet; | ||||
import org.apache.poi.hslf.record.TextHeaderAtom; | import org.apache.poi.hslf.record.TextHeaderAtom; | ||||
import org.apache.poi.hslf.record.VBAInfoAtom; | |||||
import org.apache.poi.hslf.record.VBAInfoContainer; | |||||
import org.apache.poi.hssf.usermodel.DummyGraphics2d; | import org.apache.poi.hssf.usermodel.DummyGraphics2d; | ||||
import org.apache.poi.poifs.filesystem.NPOIFSFileSystem; | |||||
import org.apache.poi.poifs.macros.VBAMacroReader; | |||||
import org.apache.poi.sl.draw.DrawFactory; | import org.apache.poi.sl.draw.DrawFactory; | ||||
import org.apache.poi.sl.draw.DrawPaint; | import org.apache.poi.sl.draw.DrawPaint; | ||||
import org.apache.poi.sl.draw.DrawTextParagraph; | import org.apache.poi.sl.draw.DrawTextParagraph; | ||||
import org.apache.poi.sl.usermodel.TextParagraph; | import org.apache.poi.sl.usermodel.TextParagraph; | ||||
import org.apache.poi.sl.usermodel.TextParagraph.TextAlign; | import org.apache.poi.sl.usermodel.TextParagraph.TextAlign; | ||||
import org.apache.poi.sl.usermodel.TextRun; | import org.apache.poi.sl.usermodel.TextRun; | ||||
import org.apache.poi.util.IOUtils; | |||||
import org.apache.poi.util.LittleEndian; | import org.apache.poi.util.LittleEndian; | ||||
import org.apache.poi.util.StringUtil; | import org.apache.poi.util.StringUtil; | ||||
import org.apache.poi.util.Units; | import org.apache.poi.util.Units; | ||||
ppt2.close(); | ppt2.close(); | ||||
} | } | ||||
@Test | |||||
public void bug59302() throws IOException { | |||||
//add extraction from PPT | |||||
Map<String, String> macros = getMacrosFromHSLF("59302.ppt"); | |||||
assertNotNull("couldn't find macros", macros); | |||||
assertNotNull("couldn't find second module", macros.get("Module2")); | |||||
assertContains(macros.get("Module2"), "newMacro in Module2"); | |||||
assertNotNull("couldn't find first module", macros.get("Module1")); | |||||
assertContains(macros.get("Module1"), "Italicize"); | |||||
macros = getMacrosFromHSLF("SimpleMacro.ppt"); | |||||
assertNotNull(macros.get("Module1")); | |||||
assertContains(macros.get("Module1"), "This is a macro slideshow"); | |||||
} | |||||
//It isn't pretty, but it works... | |||||
private Map<String, String> getMacrosFromHSLF(String fileName) throws IOException { | |||||
InputStream is = null; | |||||
NPOIFSFileSystem npoifs = null; | |||||
try { | |||||
is = new FileInputStream(POIDataSamples.getSlideShowInstance().getFile(fileName)); | |||||
npoifs = new NPOIFSFileSystem(is); | |||||
//TODO: should we run the VBAMacroReader on this npoifs? | |||||
//TBD: We know that ppt typically don't store macros in the regular place, | |||||
//but _can_ they? | |||||
HSLFSlideShow ppt = new HSLFSlideShow(npoifs); | |||||
//get macro persist id | |||||
DocInfoListContainer list = (DocInfoListContainer)ppt.getDocumentRecord().findFirstOfType(RecordTypes.List.typeID); | |||||
VBAInfoContainer vbaInfo = (VBAInfoContainer)list.findFirstOfType(RecordTypes.VBAInfo.typeID); | |||||
VBAInfoAtom vbaAtom = (VBAInfoAtom)vbaInfo.findFirstOfType(RecordTypes.VBAInfoAtom.typeID); | |||||
long persistId = vbaAtom.getPersistIdRef(); | |||||
for (HSLFObjectData objData : ppt.getEmbeddedObjects()) { | |||||
if (objData.getExOleObjStg().getPersistId() == persistId) { | |||||
return new VBAMacroReader(objData.getData()).readMacros(); | |||||
} | |||||
} | |||||
} finally { | |||||
IOUtils.closeQuietly(npoifs); | |||||
IOUtils.closeQuietly(is); | |||||
} | |||||
return null; | |||||
} | |||||
} | } |
public void XSSFfromStream() throws Exception { | public void XSSFfromStream() throws Exception { | ||||
fromStream(POIDataSamples.getSpreadSheetInstance(), "SimpleMacro.xlsm"); | fromStream(POIDataSamples.getSpreadSheetInstance(), "SimpleMacro.xlsm"); | ||||
} | } | ||||
@Ignore("bug 59302: Found 0 macros") | |||||
@Ignore("bug 59302: Found 0 macros; See org.apache.poi.hslf.usermodel.TestBugs.getMacrosFromHSLF()" + | |||||
"for an example of how to get macros out of ppt. TODO: make integration across file formats more elegant") | |||||
@Test | @Test | ||||
public void HSLFfromStream() throws Exception { | public void HSLFfromStream() throws Exception { | ||||
fromStream(POIDataSamples.getSlideShowInstance(), "SimpleMacro.ppt"); | fromStream(POIDataSamples.getSlideShowInstance(), "SimpleMacro.ppt"); | ||||
public void XSSFfromFile() throws Exception { | public void XSSFfromFile() throws Exception { | ||||
fromFile(POIDataSamples.getSpreadSheetInstance(), "SimpleMacro.xlsm"); | fromFile(POIDataSamples.getSpreadSheetInstance(), "SimpleMacro.xlsm"); | ||||
} | } | ||||
@Ignore("bug 59302: Found 0 macros") | |||||
@Ignore("bug 59302: Found 0 macros; See org.apache.poi.hslf.usermodel.TestBugs.getMacrosFromHSLF()" + | |||||
"for an example of how to get macros out of ppt. TODO: make integration across file formats more elegant") | |||||
@Test | @Test | ||||
public void HSLFfromFile() throws Exception { | public void HSLFfromFile() throws Exception { | ||||
fromFile(POIDataSamples.getSlideShowInstance(), "SimpleMacro.ppt"); | fromFile(POIDataSamples.getSlideShowInstance(), "SimpleMacro.ppt"); |