Advertisement
kiwiwings

Extracting embedded objects of xls/x

Dec 6th, 2014
533
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 5 18.71 KB | None | 0 0
  1. package poijartest;
  2.  
  3. import java.awt.Color;
  4. import java.awt.image.BufferedImage;
  5. import java.io.ByteArrayInputStream;
  6. import java.io.ByteArrayOutputStream;
  7. import java.io.Closeable;
  8. import java.io.File;
  9. import java.io.FileOutputStream;
  10. import java.io.IOException;
  11. import java.io.InputStream;
  12. import java.lang.reflect.Method;
  13. import java.net.URL;
  14. import java.nio.charset.Charset;
  15. import java.util.ArrayList;
  16. import java.util.Iterator;
  17. import java.util.List;
  18. import java.util.regex.Matcher;
  19. import java.util.regex.Pattern;
  20.  
  21. import javax.imageio.ImageIO;
  22.  
  23. import org.apache.poi.ddf.EscherComplexProperty;
  24. import org.apache.poi.ddf.EscherOptRecord;
  25. import org.apache.poi.ddf.EscherProperty;
  26. import org.apache.poi.hpsf.ClassID;
  27. import org.apache.poi.hslf.usermodel.HSLFSlideShow;
  28. import org.apache.poi.hssf.usermodel.HSSFClientAnchor;
  29. import org.apache.poi.hssf.usermodel.HSSFObjectData;
  30. import org.apache.poi.hssf.usermodel.HSSFPatriarch;
  31. import org.apache.poi.hssf.usermodel.HSSFPicture;
  32. import org.apache.poi.hssf.usermodel.HSSFPictureData;
  33. import org.apache.poi.hssf.usermodel.HSSFShape;
  34. import org.apache.poi.hssf.usermodel.HSSFSheet;
  35. import org.apache.poi.hssf.usermodel.HSSFSimpleShape;
  36. import org.apache.poi.hssf.usermodel.HSSFWorkbook;
  37. import org.apache.poi.openxml4j.opc.PackagePart;
  38. import org.apache.poi.poifs.filesystem.DirectoryNode;
  39. import org.apache.poi.poifs.filesystem.Entry;
  40. import org.apache.poi.poifs.filesystem.Ole10Native;
  41. import org.apache.poi.poifs.filesystem.Ole10NativeException;
  42. import org.apache.poi.poifs.filesystem.POIFSFileSystem;
  43. import org.apache.poi.sl.usermodel.AutoShape;
  44. import org.apache.poi.sl.usermodel.ShapeType;
  45. import org.apache.poi.sl.usermodel.Slide;
  46. import org.apache.poi.ss.usermodel.Sheet;
  47. import org.apache.poi.ss.usermodel.Workbook;
  48. import org.apache.poi.ss.usermodel.WorkbookFactory;
  49. import org.apache.poi.util.IOUtils;
  50. import org.apache.poi.xssf.usermodel.XSSFDrawing;
  51. import org.apache.poi.xssf.usermodel.XSSFPicture;
  52. import org.apache.poi.xssf.usermodel.XSSFPictureData;
  53. import org.apache.poi.xssf.usermodel.XSSFShape;
  54. import org.apache.poi.xssf.usermodel.XSSFSheet;
  55. import org.apache.poi.xssf.usermodel.XSSFWorkbook;
  56. import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTPicture;
  57.  
  58. /**
  59.  * Tested with POI 3.16-beta1
  60.  *
  61.  * 17.12.2014: original version for
  62.  *    http://apache-poi.1045710.n5.nabble.com/How-to-get-the-full-file-name-of-a-picture-in-xls-file-td5717205.html
  63.  *
  64.  * 17.12.2016: added sample/dummy data for
  65.  *    http://stackoverflow.com/questions/41101012/how-to-export-embeded-file-which-from-excel-using-poi
  66.  */
  67. public class EmbeddedReader {
  68.  
  69.     private File excel_file;
  70.     private ImageReader image_reader;
  71.  
  72.     public static void main(String[] args) throws Exception {
  73.         File sample = new File("bla.xls");
  74.         getSampleEmbedded(sample);
  75.         ImageReader ir = new ImageReader(sample);
  76.  
  77.         for (EmbeddedData ed : ir.embeddings) {
  78.             System.out.println(ed.filename);
  79.             FileOutputStream fos = new FileOutputStream(ed.filename);
  80.             IOUtils.copy(ed.is, fos);
  81.             fos.close();
  82.         }
  83.  
  84.         ir.close();
  85.     }
  86.    
  87.     static void getSampleEmbedded(File sample) throws IOException {
  88.         HSSFWorkbook wb = new HSSFWorkbook();
  89.         int storageId = wb.addOlePackage(getSamplePPT(), "dummy.ppt", "dummy.ppt", "dummy.ppt");
  90.         int picId = wb.addPicture(getSamplePng(), HSSFPicture.PICTURE_TYPE_PNG);
  91.         HSSFSheet sheet = wb.createSheet();
  92.         HSSFPatriarch pat = sheet.createDrawingPatriarch();
  93.         HSSFClientAnchor anc = pat.createAnchor(0, 0, 0, 0, 1, 1, 3, 6);
  94.         HSSFObjectData od = pat.createObjectData(anc, storageId, picId);
  95.         od.setNoFill(true);
  96.         wb.write(sample);
  97.         wb.close();
  98.     }
  99.  
  100.     static byte[] getSamplePng() throws IOException {
  101.         ClassLoader cl = Thread.currentThread().getContextClassLoader();
  102.         URL imgUrl = cl.getResource("javax/swing/plaf/metal/icons/ocean/directory.gif");
  103.         BufferedImage img = ImageIO.read(imgUrl);
  104.         ByteArrayOutputStream bos = new ByteArrayOutputStream();
  105.         ImageIO.write(img, "PNG", bos);
  106.         return bos.toByteArray();
  107.     }
  108.  
  109.     static byte[] getSamplePPT() throws IOException {
  110.         HSLFSlideShow ppt = new HSLFSlideShow();
  111.         Slide<?,?> slide = ppt.createSlide();
  112.  
  113.         AutoShape<?,?> sh1 = slide.createAutoShape();
  114.         sh1.setShapeType(ShapeType.STAR_32);
  115.         sh1.setAnchor(new java.awt.Rectangle(50, 50, 100, 200));
  116.         sh1.setFillColor(Color.red);
  117.  
  118.         ByteArrayOutputStream bos = new ByteArrayOutputStream();
  119.         ppt.write(bos);
  120.         ppt.close();
  121.  
  122.         POIFSFileSystem poifs = new POIFSFileSystem(new ByteArrayInputStream(bos.toByteArray()));
  123.         poifs.getRoot().setStorageClsid(ClassID.PPT_SHOW);
  124.  
  125.         bos.reset();
  126.         poifs.writeFilesystem(bos);
  127.         poifs.close();
  128.  
  129.         return bos.toByteArray();
  130.     }
  131.  
  132.     public EmbeddedReader(String excel_path) throws IOException {
  133.         excel_file = new File(excel_path);
  134.         image_reader = new ImageReader(excel_file);
  135.     }
  136.  
  137.     public String[] get_file_names() {
  138.         ArrayList<String> file_names = new ArrayList<String>();
  139.         for (EmbeddedData ed : image_reader.embeddings) {
  140.             file_names.add(ed.filename);
  141.         }
  142.         return file_names.toArray(new String[file_names.size()]);
  143.     }
  144.  
  145.     public InputStream get_stream(String file_name) {
  146.         InputStream input_stream = null;
  147.         for (EmbeddedData ed : image_reader.embeddings) {
  148.             if(file_name.equals(ed.filename)) {
  149.                 input_stream = ed.is;
  150.                 break;
  151.             }
  152.         }
  153.         return input_stream;
  154.     }
  155.  
  156.     static class ImageReader implements Closeable {
  157.         EmbeddedExtractor extractors[] = {
  158.             new Ole10Extractor(), new PdfExtractor(), new WordExtractor(), new ExcelExtractor(), new FsExtractor()
  159.         };
  160.  
  161.         List<EmbeddedData> embeddings = new ArrayList<EmbeddedData>();
  162.         Workbook wb;
  163.  
  164.         public ImageReader(File excelfile) throws IOException {
  165.             try {
  166.                 wb = WorkbookFactory.create(excelfile);
  167.                 Sheet receiptImages = wb.getSheet("Receipt images");
  168.                 if (wb instanceof XSSFWorkbook) {
  169.                     addSheetPicsAndEmbedds((XSSFSheet)receiptImages);
  170.                 } else {
  171.                     addAllEmbedds((HSSFWorkbook)wb);
  172.                     addSheetPics((HSSFSheet)receiptImages);
  173.                 }
  174.             } catch (Exception e) {
  175.                 // todo: error handling
  176.             }
  177.         }
  178.  
  179.         protected void addSheetPicsAndEmbedds(XSSFSheet sheet) throws IOException {
  180.             if (sheet == null) return;
  181.             XSSFDrawing draw = sheet.createDrawingPatriarch();
  182.             for (XSSFShape shape : draw.getShapes()) {
  183.                 if (!(shape instanceof XSSFPicture)) continue;
  184.                 XSSFPicture picture = (XSSFPicture)shape;
  185.                 XSSFPictureData pd = picture.getPictureData();
  186.                 PackagePart pp = pd.getPackagePart();
  187.                 CTPicture ctPic = picture.getCTPicture();
  188.                 String filename = null;
  189.                 try {
  190.                     filename = ctPic.getNvPicPr().getCNvPr().getName();
  191.                 } catch (Exception e) {}
  192.                 if (filename == null || "".equals(filename)) {
  193.                     filename = new File(pp.getPartName().toString()).getName();
  194.                 }
  195.                 EmbeddedData ed = new EmbeddedData();
  196.                 ed.filename = fileNameWithoutPath(filename);
  197.                 ed.is = pp.getInputStream();
  198.                 embeddings.add(ed);
  199.             }
  200.         }
  201.  
  202.         protected void addAllEmbedds(HSSFWorkbook hwb) throws IOException {
  203.             for (HSSFObjectData od : hwb.getAllEmbeddedObjects()) {
  204.                 String alternativeName = getAlternativeName(od);
  205.                 if (od.hasDirectoryEntry()) {
  206.                     DirectoryNode src = (DirectoryNode)od.getDirectory();
  207.                     for (EmbeddedExtractor ee : extractors) {
  208.                         if (ee.canExtract(src)) {
  209.                             EmbeddedData ed = ee.extract(src);
  210.                             if (ed.filename == null || ed.filename.startsWith("MBD") || alternativeName != null) {
  211.                                 ed.filename = alternativeName;
  212.                             }
  213.                             ed.filename = fileNameWithoutPath(ed.filename);
  214.                             ed.source = "object";
  215.                             embeddings.add(ed);
  216.                             break;
  217.                         }
  218.                     }
  219.                 }
  220.             }
  221.         }
  222.  
  223.         protected String getAlternativeName(HSSFShape shape) {
  224.             EscherOptRecord eor = reflectEscherOptRecord(shape);
  225.             if (eor == null) return null;
  226.             for (EscherProperty ep : eor.getEscherProperties()) {
  227.                 if ("groupshape.shapename".equals(ep.getName()) && ep.isComplex()) {
  228.                     return new String(((EscherComplexProperty)ep).getComplexData(),
  229.                             Charset.forName("UTF-16LE"));
  230.                 }
  231.             }
  232.             return null;
  233.         }
  234.  
  235.         protected void addSheetPics(HSSFSheet sheet) {
  236.             if (sheet == null) return;
  237.             int picIdx=0;
  238.             int emfIdx = 0;
  239.             HSSFPatriarch patriarch = sheet.getDrawingPatriarch();
  240.             if (patriarch == null) return;
  241.             // Loop through the objects
  242.             for (HSSFShape shape : patriarch.getChildren()) {
  243.                 if (!(shape instanceof HSSFPicture)) {
  244.                     continue;
  245.                 }
  246.                 HSSFPicture picture = (HSSFPicture) shape;
  247.                 if (picture.getShapeType() != HSSFSimpleShape.OBJECT_TYPE_PICTURE) continue;
  248.                 HSSFPictureData pd = picture.getPictureData();
  249.                 byte pictureBytes[] = pd.getData();
  250.                 int pictureBytesOffset = 0;
  251.                 int pictureBytesLen = pictureBytes.length;
  252.                 String filename = picture.getFileName();
  253.                 // try to find an alternative name
  254.                 if (filename == null || "".equals(filename)) {
  255.                     filename = getAlternativeName(picture);
  256.                 }
  257.                 // default to dummy name
  258.                 if (filename == null || "".equals(filename)) {
  259.                     filename = "picture"+(picIdx++);
  260.                 }
  261.                 filename = filename.trim();
  262.  
  263.  
  264.                 // check for emf+ embedded pdf (poor mans style :( )
  265.                 // Mac Excel 2011 embeds pdf files with this method.
  266.                 boolean validFile = true;
  267.                 if (pd.getFormat() == Workbook.PICTURE_TYPE_EMF) {
  268.                     validFile = false;
  269.                     int idxStart = indexOf(pictureBytes, 0, "%PDF-".getBytes());
  270.                     if (idxStart != -1) {
  271.                         int idxEnd = indexOf(pictureBytes, idxStart, "%%EOF".getBytes());
  272.                         if (idxEnd != -1) {
  273.                             pictureBytesOffset = idxStart;
  274.                             pictureBytesLen = idxEnd-idxStart+6;
  275.                             validFile = true;
  276.                         }
  277.                     } else {
  278.                         // This shape was not a Mac Excel 2011 embedded pdf file.
  279.                         // So this is a shape related to a regular embedded object
  280.                         // Lets update the object filename with the shapes filename
  281.                         // if the object filename is of format ARGF1234.pdf
  282.                         EmbeddedData ed_obj = embeddings.get(emfIdx);
  283.                         Pattern pattern = Pattern.compile("^[A-Z0-9]{8}\\.[pdfPDF]{3}$");
  284.                         Matcher matcher = pattern.matcher(ed_obj.filename);
  285.                         if(matcher.matches()) {
  286.                             ed_obj.filename = filename;
  287.                         }
  288.                         emfIdx += 1;
  289.                     }
  290.                 }
  291.  
  292.                 EmbeddedData ed = new EmbeddedData();
  293.                 ed.filename = fileNameWithoutPath(filename);
  294.                 ed.is = new ByteArrayInputStream(pictureBytes, pictureBytesOffset, pictureBytesLen);
  295.                 if(fileNotInEmbeddings(ed.filename) && validFile) {
  296.                     embeddings.add(ed);
  297.                 }
  298.             }
  299.         }
  300.  
  301.         private static EscherOptRecord reflectEscherOptRecord(HSSFShape shape) {
  302.             try {
  303.                 Method m = HSSFShape.class.getDeclaredMethod("getOptRecord");
  304.                 m.setAccessible(true);
  305.                 return (EscherOptRecord)m.invoke(shape);
  306.             } catch (Exception e) {
  307.                 // todo: log ... well actually "should not happen" ;)
  308.                 return null;
  309.             }
  310.         }
  311.  
  312.         private String fileNameWithoutPath(String filename) {
  313.             int last_index = filename.lastIndexOf("\\");
  314.             return filename.substring(last_index + 1);
  315.         }
  316.  
  317.         private boolean fileNotInEmbeddings(String filename) {
  318.             boolean exists = true;
  319.             for(EmbeddedData ed : embeddings) {
  320.                 if(ed.filename.equals(filename)) {
  321.                     exists = false;
  322.                 }
  323.             }
  324.             return exists;
  325.         }
  326.  
  327.         public void close() throws IOException {
  328.             Iterator<EmbeddedData> ed = embeddings.iterator();
  329.             while (ed.hasNext()) {
  330.                 ed.next().is.close();
  331.             }
  332.             wb.close();
  333.         }
  334.     }
  335.  
  336.     static class EmbeddedData {
  337.         String filename;
  338.         InputStream is;
  339.         String source;
  340.     }
  341.  
  342.     static abstract class EmbeddedExtractor {
  343.         abstract boolean canExtract(DirectoryNode dn);
  344.         abstract EmbeddedData extract(DirectoryNode dn) throws IOException;
  345.         protected EmbeddedData extractFS(DirectoryNode dn, String filename) throws IOException {
  346.             assert(canExtract(dn));
  347.             POIFSFileSystem dest = new POIFSFileSystem();
  348.             copyNodes(dn, dest.getRoot());
  349.             EmbeddedData ed = new EmbeddedData();
  350.             ed.filename = filename;
  351.             ByteArrayOutputStream bos = new ByteArrayOutputStream();
  352.             dest.writeFilesystem(bos);
  353.             dest.close();
  354.             ed.is = new ByteArrayInputStream(bos.toByteArray());
  355.             return ed;
  356.         }
  357.     }
  358.  
  359.     static class Ole10Extractor extends EmbeddedExtractor {
  360.         public boolean canExtract(DirectoryNode dn) {
  361.             ClassID clsId = dn.getStorageClsid();
  362.             return ClassID.OLE10_PACKAGE.equals(clsId);
  363.         }
  364.         public EmbeddedData extract(DirectoryNode dn) throws IOException {
  365.             try {
  366.                 Ole10Native ole10 = Ole10Native.createFromEmbeddedOleObject(dn);
  367.                 EmbeddedData ed = new EmbeddedData();
  368.                 ed.filename = new File(ole10.getFileName()).getName();
  369.                 ed.is = new ByteArrayInputStream(ole10.getDataBuffer());
  370.                 return ed;
  371.             } catch (Ole10NativeException e) {
  372.                 throw new IOException(e);
  373.             }
  374.         }
  375.     }
  376.  
  377.     static class PdfExtractor extends EmbeddedExtractor {
  378.         static ClassID PdfClassID = new ClassID("{B801CA65-A1FC-11D0-85AD-444553540000}");
  379.         public boolean canExtract(DirectoryNode dn) {
  380.             ClassID clsId = dn.getStorageClsid();
  381.             return (PdfClassID.equals(clsId)
  382.             || dn.hasEntry("CONTENTS"));
  383.         }
  384.         public EmbeddedData extract(DirectoryNode dn) throws IOException {
  385.             EmbeddedData ed = new EmbeddedData();
  386.             ed.is = dn.createDocumentInputStream("CONTENTS");
  387.             ed.filename = dn.getName()+".pdf";
  388.             return ed;
  389.         }
  390.     }
  391.  
  392.     static class WordExtractor extends EmbeddedExtractor {
  393.         public boolean canExtract(DirectoryNode dn) {
  394.             ClassID clsId = dn.getStorageClsid();
  395.             return (ClassID.WORD95.equals(clsId)
  396.             || ClassID.WORD97.equals(clsId)
  397.             || dn.hasEntry("WordDocument"));
  398.         }
  399.         public EmbeddedData extract(DirectoryNode dn) throws IOException {
  400.             return extractFS(dn, dn.getName()+".doc");
  401.         }
  402.     }
  403.  
  404.     static class ExcelExtractor extends EmbeddedExtractor {
  405.         public boolean canExtract(DirectoryNode dn) {
  406.             ClassID clsId = dn.getStorageClsid();
  407.             return (ClassID.EXCEL95.equals(clsId)
  408.                     || ClassID.EXCEL97.equals(clsId)
  409.                     || dn.hasEntry("Workbook") /*...*/);
  410.         }
  411.         public EmbeddedData extract(DirectoryNode dn) throws IOException {
  412.             return extractFS(dn, dn.getName()+".xls");
  413.         }
  414.     }
  415.  
  416.     static class FsExtractor extends EmbeddedExtractor {
  417.         public boolean canExtract(DirectoryNode dn) {
  418.             return true;
  419.         }
  420.         public EmbeddedData extract(DirectoryNode dn) throws IOException {
  421.             return extractFS(dn, dn.getName()+".dat");
  422.         }
  423.     }
  424.  
  425.     private static void copyNodes(DirectoryNode src, DirectoryNode dest) throws IOException {
  426.         for (Entry e : src) {
  427.             if (e instanceof DirectoryNode) {
  428.                 DirectoryNode srcDir = (DirectoryNode)e;
  429.                 DirectoryNode destDir = (DirectoryNode)dest.createDirectory(srcDir.getName());
  430.                 destDir.setStorageClsid(srcDir.getStorageClsid());
  431.                 copyNodes(srcDir, destDir);
  432.             } else {
  433.                 InputStream is = src.createDocumentInputStream(e);
  434.                 dest.createDocument(e.getName(), is);
  435.                 is.close();
  436.             }
  437.         }
  438.     }
  439.  
  440.  
  441.     /**
  442.      * Knuth-Morris-Pratt Algorithm for Pattern Matching
  443.      * Finds the first occurrence of the pattern in the text.
  444.      */
  445.     private static int indexOf(byte[] data, int offset, byte[] pattern) {
  446.         int[] failure = computeFailure(pattern);
  447.  
  448.         int j = 0;
  449.         if (data.length == 0) return -1;
  450.  
  451.         for (int i = offset; i < data.length; i++) {
  452.             while (j > 0 && pattern[j] != data[i]) {
  453.                 j = failure[j - 1];
  454.             }
  455.             if (pattern[j] == data[i]) { j++; }
  456.             if (j == pattern.length) {
  457.                 return i - pattern.length + 1;
  458.             }
  459.         }
  460.         return -1;
  461.     }
  462.  
  463.     /**
  464.      * Computes the failure function using a boot-strapping process,
  465.      * where the pattern is matched against itself.
  466.      */
  467.     private static int[] computeFailure(byte[] pattern) {
  468.         int[] failure = new int[pattern.length];
  469.  
  470.         int j = 0;
  471.         for (int i = 1; i < pattern.length; i++) {
  472.             while (j > 0 && pattern[j] != pattern[i]) {
  473.                 j = failure[j - 1];
  474.             }
  475.             if (pattern[j] == pattern[i]) {
  476.                 j++;
  477.             }
  478.             failure[i] = j;
  479.         }
  480.  
  481.         return failure;
  482.     }
  483. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement