git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1738674 13f79535-47bb-0310-9956-ffa450edef68tags/REL_3_15_BETA2
@@ -22,13 +22,16 @@ import java.io.FileNotFoundException; | |||
import java.io.FileOutputStream; | |||
import java.io.IOException; | |||
import java.io.OutputStreamWriter; | |||
import java.util.Map; | |||
import java.util.Map; | |||
import java.util.Map.Entry; | |||
import org.apache.poi.util.StringUtil; | |||
/** | |||
* This tool extracts out the source of all VBA Modules of an office file, | |||
* both OOXML (eg XLSM) and OLE2/POIFS (eg DOC), to STDOUT or a directory. | |||
* both OOXML (eg XLSM) and OLE2/POIFS (eg DOC), to STDOUT or a directory. | |||
* | |||
* @since 3.15-beta2 | |||
*/ | |||
public class VBAMacroExtractor { | |||
public static void main(String args[]) throws IOException { | |||
@@ -47,15 +50,30 @@ public class VBAMacroExtractor { | |||
output = new File(args[1]); | |||
} | |||
VBAMacroExtractor extract = new VBAMacroExtractor(); | |||
extract.extract(input, output); | |||
VBAMacroExtractor extractor = new VBAMacroExtractor(); | |||
extractor.extract(input, output); | |||
} | |||
public void extract(File input, File outputDir) throws IOException { | |||
/** | |||
* Extracts the VBA modules from a macro-enabled office file and writes them | |||
* to files in <tt>outputDir</tt>. | |||
* | |||
* Creates the <tt>outputDir</tt>, directory, including any necessary but | |||
* nonexistent parent directories, if <tt>outputDir</tt> does not exist. | |||
* If <tt>outputDir</tt> is null, writes the contents to standard out instead. | |||
* | |||
* @param input the macro-enabled office file. | |||
* @param outputDir the directory to write the extracted VBA modules to. | |||
* @param extension file extension of the extracted VBA modules | |||
* @since 3.15-beta2 | |||
*/ | |||
public void extract(File input, File outputDir, String extension) throws IOException { | |||
if (! input.exists()) throw new FileNotFoundException(input.toString()); | |||
System.err.print("Extracting VBA Macros from " + input + " to "); | |||
if (outputDir != null) { | |||
if (! outputDir.exists()) outputDir.mkdir(); | |||
if (!outputDir.exists() && !outputDir.mkdirs()) { | |||
throw new IOException("Output directory " + outputDir + " could not be created"); | |||
} | |||
System.err.println(outputDir); | |||
} else { | |||
System.err.println("STDOUT"); | |||
@@ -66,17 +84,19 @@ public class VBAMacroExtractor { | |||
reader.close(); | |||
final String divider = "---------------------------------------"; | |||
for (String macro : macros.keySet()) { | |||
for (Entry<String, String> entry : macros.entrySet()) { | |||
String moduleName = entry.getKey(); | |||
String moduleCode = entry.getValue(); | |||
if (outputDir == null) { | |||
System.out.println(divider); | |||
System.out.println(macro); | |||
System.out.println(moduleName); | |||
System.out.println(""); | |||
System.out.println(macros.get(macro)); | |||
System.out.println(moduleCode); | |||
} else { | |||
File out = new File(outputDir, macro + ".vba"); | |||
File out = new File(outputDir, moduleName + extension); | |||
FileOutputStream fout = new FileOutputStream(out); | |||
OutputStreamWriter fwriter = new OutputStreamWriter(fout, StringUtil.UTF8); | |||
fwriter.write(macros.get(macro)); | |||
fwriter.write(moduleCode); | |||
fwriter.close(); | |||
fout.close(); | |||
System.out.println("Extracted " + out); | |||
@@ -85,5 +105,21 @@ public class VBAMacroExtractor { | |||
if (outputDir == null) { | |||
System.out.println(divider); | |||
} | |||
} | |||
/** | |||
* Extracts the VBA modules from a macro-enabled office file and writes them | |||
* to <tt>.vba</tt> files in <tt>outputDir</tt>. | |||
* | |||
* Creates the <tt>outputDir</tt>, directory, including any necessary but | |||
* nonexistent parent directories, if <tt>outputDir</tt> does not exist. | |||
* If <tt>outputDir</tt> is null, writes the contents to standard out instead. | |||
* | |||
* @param input the macro-enabled office file. | |||
* @param outputDir the directory to write the extracted VBA modules to. | |||
* @since 3.15-beta2 | |||
*/ | |||
public void extract(File input, File outputDir) throws IOException { | |||
extract(input, outputDir, ".vba"); | |||
} | |||
} |
@@ -45,7 +45,9 @@ import org.apache.poi.util.RLEDecompressingInputStream; | |||
/** | |||
* Finds all VBA Macros in an office file (OLE2/POIFS and OOXML/OPC), | |||
* and returns them. | |||
* and returns them. | |||
* | |||
* @since 3.15-beta2 | |||
*/ | |||
public class VBAMacroReader implements Closeable { | |||
protected static final String VBA_PROJECT_OOXML = "vbaProject.bin"; | |||
@@ -104,7 +106,9 @@ public class VBAMacroReader implements Closeable { | |||
/** | |||
* Reads all macros from all modules of the opened office file. | |||
* @return All the macros and their contents | |||
* @return All the macros and their contents | |||
* | |||
* @since 3.15-beta2 | |||
*/ | |||
public Map<String, String> readMacros() throws IOException { | |||
final ModuleMap modules = new ModuleMap(); | |||
@@ -135,7 +139,8 @@ public class VBAMacroReader implements Closeable { | |||
* | |||
* @param dir | |||
* @param modules | |||
* @throws IOException | |||
* @throws IOException | |||
* @since 3.15-beta2 | |||
*/ | |||
protected void findMacros(DirectoryNode dir, ModuleMap modules) throws IOException { | |||
if (VBA_PROJECT_POIFS.equalsIgnoreCase(dir.getName())) { | |||
@@ -164,8 +169,28 @@ public class VBAMacroReader implements Closeable { | |||
byte[] buffer = new byte[length]; | |||
int count = stream.read(buffer); | |||
return new String(buffer, 0, count, charset); | |||
} | |||
/** | |||
* Skips <tt>n</tt> bytes in an input stream, throwing IOException if the | |||
* number of bytes skipped is different than requested. | |||
* @throws IOException | |||
*/ | |||
private static void trySkip(InputStream in, long n) throws IOException { | |||
long skippedBytes = in.skip(n); | |||
if (skippedBytes != n) { | |||
throw new IOException( | |||
"Skipped only " + skippedBytes + " while trying to skip " + n + " bytes. " + | |||
" This should never happen."); | |||
} | |||
} | |||
/* | |||
* Reads VBA Project modules from a VBA Project directory located at | |||
* <tt>macroDir</tt> into <tt>modules</tt>. | |||
* | |||
* @since 3.15-beta2 | |||
*/ | |||
protected void readMacros(DirectoryNode macroDir, ModuleMap modules) throws IOException { | |||
for (Entry entry : macroDir) { | |||
if (! (entry instanceof DocumentNode)) { continue; } | |||
@@ -185,7 +210,7 @@ public class VBAMacroReader implements Closeable { | |||
int len = in.readInt(); | |||
switch (id) { | |||
case 0x0009: // PROJECTVERSION | |||
in.skip(6); | |||
trySkip(in, 6); | |||
break; | |||
case 0x0003: // PROJECTCODEPAGE | |||
int codepage = in.readShort(); | |||
@@ -211,8 +236,8 @@ public class VBAMacroReader implements Closeable { | |||
modules.put(streamName, module); | |||
} | |||
break; | |||
default: | |||
in.skip(len); | |||
default: | |||
trySkip(in, len); | |||
break; | |||
} | |||
} |
@@ -33,7 +33,8 @@ public class RLEDecompressingInputStream extends InputStream { | |||
/** | |||
* Bitmasks for performance | |||
*/ | |||
private static final int[] POWER2 = new int[] { 0x0001, // 0 | |||
private static final int[] POWER2 = new int[] { | |||
0x0001, // 0 | |||
0x0002, // 1 | |||
0x0004, // 2 | |||
0x0008, // 3 | |||
@@ -48,14 +49,14 @@ public class RLEDecompressingInputStream extends InputStream { | |||
0x1000, // 12 | |||
0x2000, // 13 | |||
0x4000, // 14 | |||
0x8000 // 15 | |||
0x8000 // 15 | |||
}; | |||
/** the wrapped inputstream */ | |||
private InputStream in; | |||
private final InputStream in; | |||
/** a byte buffer with size 4096 for storing a single chunk */ | |||
private byte[] buf; | |||
private final byte[] buf; | |||
/** the current position in the byte buffer for reading */ | |||
private int pos; | |||
@@ -229,7 +230,7 @@ public class RLEDecompressingInputStream extends InputStream { | |||
/** | |||
* Convenience method for read a 2-bytes short in little endian encoding. | |||
* | |||
* @return | |||
* @return short value from the stream | |||
* @throws IOException | |||
*/ | |||
public int readShort() throws IOException { | |||
@@ -239,7 +240,7 @@ public class RLEDecompressingInputStream extends InputStream { | |||
/** | |||
* Convenience method for read a 4-bytes int in little endian encoding. | |||
* | |||
* @return | |||
* @return integer value from the stream | |||
* @throws IOException | |||
*/ | |||
public int readInt() throws IOException { |