sub 1024g/5E0E1748 2004-03-24 [expires: 2009-03-23]
-----BEGIN PGP PUBLIC KEY BLOCK-----
-Version: GnuPG v1.4.6 (GNU/Linux)
+Version: GnuPG v1.4.7 (GNU/Linux)
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
-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
-vuh58AIoldgrDIeIJA==
-=xCFv
+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
+=j3Fb
-----END PGP PUBLIC KEY BLOCK-----
<menu-item label="HDGF" href="hdgf/index.html"/>
<menu-item label="POI-Ruby" href="poi-ruby.html"/>
<menu-item label="POI-Utils" href="utils/index.html"/>
+ <menu-item label="Text Extraction" href="text-extraction.html"/>
<menu-item label="Download" href="ext:download"/>
</menu>
<menu label="POIFS">
<menu-item label="Overview" href="index.html"/>
<menu-item label="How To" href="how-to.html"/>
+ <menu-item label="Embeded Documents" href="embeded.html"/>
<menu-item label="File System Documentation" href="fileformat.html"/>
<menu-item label="Use Cases" href="usecases.html"/>
</menu>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ====================================================================
+ 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.
+ ====================================================================
+-->
+<!DOCTYPE document PUBLIC "-//APACHE//DTD Documentation V1.1//EN" "../dtd/document-v11.dtd">
+<document>
+ <header>
+ <title>Apache POI - POIFS - Documents embeded in other documents</title>
+ <subtitle>Overview</subtitle>
+ <authors>
+ <person name="Nick Burch" email="nick@apache.org"/>
+ <person name="Yegor Kozlov" email="yegor@apache.org"/>
+ </authors>
+ </header>
+ <body>
+ <section><title>Overview</title>
+ <p>It is possible for one OLE 2 based document to have other
+ OLE 2 documents embeded in it. For example, and Excel file
+ may have a word document and a powerpoint slideshow
+ embeded as part of it.</p>
+ <p>Normally, these other documents are stored in subdirectories
+ of the OLE 2 (POIFS) filesystem. The exact location of the
+ embeded documents will vary depending on the type of the
+ master document, and the exact directory names will differ
+ each time. To figure out exactly which directory to look
+ in, you will either need to process the appropriate OLE 2
+ linking entry in the master document, or simple iterate
+ over all the directories in the filesystem.</p>
+ <p>As a general rule, you will find the same OLE 2 entries
+ in the subdirectories, as you would've found at the root
+ of the filesystem were a document to not be embeded.</p>
+
+ <section><title>Files embeded in Excel</title>
+ <p>Excel normally stores embeded files in subdirectories
+ of the filesystem root. Typically these subdirectories
+ are named starting with MBD, with 8 hex characters following.</p>
+ </section>
+
+ <section><title>Files embeded in Word</title>
+ <p>Word normally stores embeded files in subdirectories
+ of the ObjectPool directory, itself a subdirectory of the
+ filesystem root. Typically these subdirectories and named
+ starting with an underscore, followed by 10 numbers.</p>
+ </section>
+
+ <section><title>Files embeded in PowerPoint</title>
+ <p>PowerPoint does not normally store embeded files
+ in the OLE2 layer. Instead, they are held within records
+ of the main PowerPoint file. To get at them, you need to
+ find the appropriate data within the PowerPoint stream,
+ and work from that.</p>
+ </section>
+ </section>
+
+ <section><title>Listing POIFS contents</title>
+ <p>POIFS provides a simple tool for listing the contents of
+ OLE2 files. This can allow you to see what your POIFS file
+ contents, and hence if it has any embeded documents in it,
+ and where.</p>
+ <p>The tool to use is <em>org.apache.poi.poifs.dev.POIFSLister</em>.
+ This tool may be run from the command line, and takes a filename
+ as its parameter. It will print out all the directories and
+ files contained within the POIFS file.</p>
+ </section>
+
+ <section><title>Opening embeded files</title>
+ <p>All of the POIDocument classes (HSSFWorkbook, HSLFSlideShow,
+ HWPFDocument and HDGFDiagram) can either be opened from
+ a POIFSFileSystem, or from a specific directory within a
+ POIFSFileSystem. So, to open embeded files, simply locate the
+ appropriate DirectoryNode that represents the subdirectory
+ of interest, and pass this + the overall POIFSFileSystem to
+ the constructor.</p>
+ <p>I you want to extract the textual contents of the embeded file,
+ then open the appropriate POIDocument, and then pass this to
+ the extractor class, instead of simply passing the POIFSFilesystem
+ to the extractor.</p>
+ </section>
+ </body>
+</document>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ====================================================================
+ 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.
+ ====================================================================
+-->
+<!DOCTYPE document PUBLIC "-//APACHE//DTD Documentation V1.3//EN" "./dtd/document-v13.dtd">
+
+<document>
+ <header>
+ <title>POI Text Extraction</title>
+ <authors>
+ <person id="NB" name="Nick Burch" email="nick@apache.org"/>
+ </authors>
+ </header>
+
+ <body>
+ <section><title>Overview</title>
+ <p>POI provides text extraction for all the supported file
+ formats. In addition, it provides access to the metadata
+ associated with a given file, such as title and author.</p>
+ <p>In addition to providing direct text extraction classes,
+ POI works closely with the
+ <link href="http://incubator.apache.org/tika/">Apache Tika</link>
+ text extraction library. Users may wish to simply utilise
+ the functionality provided by Tika.</p>
+ </section>
+
+ <section><title>Common functionality</title>
+ <p>All of the POI text extractors extend from
+ <em>org.apache.poi.POITextExtractor</em>. This provides a common
+ method across all extractors, getText(). For many cases, the text
+ returned will be all you need. However, many extractors do provide
+ more targetted text extraction methods, so you may wish to use
+ these in some cases.</p>
+ <p>All POIFS / OLE 2 based text extractors also extend from
+ <em>org.apache.poi.POIOLE2TextExtractor</em>. This additionally
+ provides common methods to get at the <link href="hpfs/">HPFS
+ document metadata</link>.</p>
+ <p>All OOXML based text extractors (available in POI 3.5 and later)
+ also extend from
+ <em>org.apache.poi.POIOOXMLTextExtractor</em>. This additionally
+ provides common methods to get at the OOXML metadata.</p>
+ </section>
+
+ <section><title>Text Extractor Factory - POI 3.5 or later</title>
+ <p>A new class in POI 3.5,
+ <em>org.apache.poi.extractor.ExtractorFactory</em> provides a
+ similar function to WorkbookFactory. You simply pass it an
+ InputStream, a file, a POIFSFileSystem or a OOXML Package. It
+ figures out the correct text extractor for you, and returns it.</p>
+ </section>
+
+ <section><title>Excel</title>
+ <p>For .xls files, there is
+ <em>org.apache.poi.hssf.extractor.ExcelExtractor</em>, which will
+ return text, optionally with formulas instead of their contents.
+ Those using POI 3.5 can also use
+ <em>org.apache.poi.xssf.extractor.XSSFExcelExtractor</em>, to perform
+ a similar task for .xlsx files.</p>
+ <p>In addition, there is a second text extractor for .xls files,
+ <em>org.apache.poi.hssf.extractor.EventBasedExcelExtractor</em>. This
+ is based on the streaming EventUserModel code, and will generally
+ deliver a lower memory footprint for extraction. However, it will
+ have problems correctly outputting more complex formulas, as it
+ works with records as they pass, and so doesn't have access to all
+ parts of complex and shared formulas.</p>
+ </section>
+
+ <section><title>Word</title>
+ <p>For .doc files, in scratchpad there is
+ <em>org.apache.poi.hwpf.extractor.WordExtractor</em>, which will
+ return text for your document. Those using POI 3.5 can also use
+ <em>org.apache.poi.xwpf.extractor.XPFFWordExtractor</em>, to perform
+ a similar task for .docx files.</p>
+ </section>
+
+ <section><title>PowerPoint</title>
+ <p>For .ppt files, in scratchpad there is
+ <em>org.apache.poi.hslf.extractor.PowerPointExtractor</em>, which
+ will return text for your slideshow, optionally restricted to just
+ slides text or notes text. Those using POI 3.5 can also use
+ <em>org.apache.poi.xslf.extractor.XSLFPowerPointExtractor</em>, to
+ perform a similar task for .pptx files.</p>
+ </section>
+
+ <section><title>Visio</title>
+ <p>For .vsd files, in scratchpad there is
+ <em>org.apache.poi.hdgf.extractor.VisioTextExtractor</em>, which
+ will return text for your file.</p>
+ </section>
+ </body>
+
+ <footer>
+ <legal>
+ Copyright 2005 The Apache Software Foundation or its licensors, as applicable.
+ $Revision: 639487 $ $Date: 2008-03-20 22:31:15 +0000 (Thu, 20 Mar 2008) $
+ </legal>
+ </footer>
+</document>
--- /dev/null
+/* ====================================================================
+ 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.hssf.eventusermodel.examples;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.text.DateFormat;
+import java.text.DecimalFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import org.apache.poi.hssf.eventusermodel.FormatTrackingHSSFListener;
+import org.apache.poi.hssf.eventusermodel.HSSFEventFactory;
+import org.apache.poi.hssf.eventusermodel.HSSFListener;
+import org.apache.poi.hssf.eventusermodel.HSSFRequest;
+import org.apache.poi.hssf.eventusermodel.MissingRecordAwareHSSFListener;
+import org.apache.poi.hssf.eventusermodel.dummyrecord.LastCellOfRowDummyRecord;
+import org.apache.poi.hssf.eventusermodel.dummyrecord.MissingCellDummyRecord;
+import org.apache.poi.hssf.model.FormulaParser;
+import org.apache.poi.hssf.record.BlankRecord;
+import org.apache.poi.hssf.record.BoolErrRecord;
+import org.apache.poi.hssf.record.CellValueRecordInterface;
+import org.apache.poi.hssf.record.FormulaRecord;
+import org.apache.poi.hssf.record.LabelRecord;
+import org.apache.poi.hssf.record.LabelSSTRecord;
+import org.apache.poi.hssf.record.NoteRecord;
+import org.apache.poi.hssf.record.NumberRecord;
+import org.apache.poi.hssf.record.RKRecord;
+import org.apache.poi.hssf.record.Record;
+import org.apache.poi.hssf.record.SSTRecord;
+import org.apache.poi.hssf.record.StringRecord;
+import org.apache.poi.hssf.usermodel.HSSFDateUtil;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+
+/**
+ * A XLS -> CSV processor, that uses the MissingRecordAware
+ * EventModel code to ensure it outputs all columns and rows.
+ * @author Nick Burch
+ */
+public class XLS2CSVmra implements HSSFListener {
+ private int minColumns;
+ private POIFSFileSystem fs;
+ private PrintStream output;
+
+ private int lastRowNumber;
+ private int lastColumnNumber;
+
+ /** Should we output the formula, or the value it has? */
+ private boolean outputFormulaValues = true;
+
+ // Records we pick up as we process
+ private SSTRecord sstRecord;
+ private FormatTrackingHSSFListener formatListener;
+
+ // For handling formulas with string results
+ private int nextRow;
+ private int nextColumn;
+ private boolean outputNextStringRecord;
+
+ /**
+ * Creates a new XLS -> CSV converter
+ * @param fs The POIFSFileSystem to process
+ * @param output The PrintStream to output the CSV to
+ * @param minColumns The minimum number of columns to output, or -1 for no minimum
+ */
+ public XLS2CSVmra(POIFSFileSystem fs, PrintStream output, int minColumns) {
+ this.fs = fs;
+ this.output = output;
+ this.minColumns = minColumns;
+ }
+
+ /**
+ * Creates a new XLS -> CSV converter
+ * @param filename The file to process
+ * @param minColumns The minimum number of columns to output, or -1 for no minimum
+ * @throws IOException
+ * @throws FileNotFoundException
+ */
+ public XLS2CSVmra(String filename, int minColumns) throws IOException, FileNotFoundException {
+ this(
+ new POIFSFileSystem(new FileInputStream(filename)),
+ System.out, minColumns
+ );
+ }
+
+ /**
+ * Initiates the processing of the XLS file to CSV
+ */
+ public void process() throws IOException {
+ MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(this);
+ formatListener = new FormatTrackingHSSFListener(listener);
+
+ HSSFEventFactory factory = new HSSFEventFactory();
+ HSSFRequest request = new HSSFRequest();
+ request.addListenerForAllRecords(formatListener);
+
+ factory.processWorkbookEvents(request, fs);
+ }
+
+ /**
+ * Main HSSFListener method, processes events, and outputs the
+ * CSV as the file is processed.
+ */
+ public void processRecord(Record record) {
+ int thisRow = -1;
+ int thisColumn = -1;
+ String thisStr = null;
+
+ switch (record.getSid())
+ {
+ case SSTRecord.sid:
+ sstRecord = (SSTRecord) record;
+ break;
+
+ case BlankRecord.sid:
+ BlankRecord brec = (BlankRecord) record;
+
+ thisRow = brec.getRow();
+ thisColumn = brec.getColumn();
+ thisStr = "";
+ break;
+ case BoolErrRecord.sid:
+ BoolErrRecord berec = (BoolErrRecord) record;
+
+ thisRow = berec.getRow();
+ thisColumn = berec.getColumn();
+ thisStr = "";
+ break;
+
+ case FormulaRecord.sid:
+ FormulaRecord frec = (FormulaRecord) record;
+
+ thisRow = frec.getRow();
+ thisColumn = frec.getColumn();
+
+ if(outputFormulaValues) {
+ if(Double.isNaN( frec.getValue() )) {
+ // Formula result is a string
+ // This is stored in the next record
+ outputNextStringRecord = true;
+ nextRow = frec.getRow();
+ nextColumn = frec.getColumn();
+ } else {
+ thisStr = formatNumberDateCell(frec, frec.getValue());
+ }
+ } else {
+ thisStr = '"' +
+ FormulaParser.toFormulaString(null, frec.getParsedExpression()) + '"';
+ }
+ break;
+ case StringRecord.sid:
+ if(outputNextStringRecord) {
+ // String for formula
+ StringRecord srec = (StringRecord)record;
+ thisStr = srec.getString();
+ thisRow = nextRow;
+ thisColumn = nextColumn;
+ outputNextStringRecord = false;
+ }
+ break;
+
+ case LabelRecord.sid:
+ LabelRecord lrec = (LabelRecord) record;
+
+ thisRow = lrec.getRow();
+ thisColumn = lrec.getColumn();
+ thisStr = '"' + lrec.getValue() + '"';
+ break;
+ case LabelSSTRecord.sid:
+ LabelSSTRecord lsrec = (LabelSSTRecord) record;
+
+ thisRow = lsrec.getRow();
+ thisColumn = lsrec.getColumn();
+ if(sstRecord == null) {
+ thisStr = '"' + "(No SST Record, can't identify string)" + '"';
+ } else {
+ thisStr = '"' + sstRecord.getString(lsrec.getSSTIndex()).toString() + '"';
+ }
+ break;
+ case NoteRecord.sid:
+ NoteRecord nrec = (NoteRecord) record;
+
+ thisRow = nrec.getRow();
+ thisColumn = nrec.getColumn();
+ // TODO: Find object to match nrec.getShapeId()
+ thisStr = '"' + "(TODO)" + '"';
+ break;
+ case NumberRecord.sid:
+ NumberRecord numrec = (NumberRecord) record;
+
+ thisRow = numrec.getRow();
+ thisColumn = numrec.getColumn();
+
+ // Format
+ thisStr = formatNumberDateCell(numrec, numrec.getValue());
+ break;
+ case RKRecord.sid:
+ RKRecord rkrec = (RKRecord) record;
+
+ thisRow = rkrec.getRow();
+ thisColumn = rkrec.getColumn();
+ thisStr = '"' + "(TODO)" + '"';
+ break;
+ default:
+ break;
+ }
+
+ // Handle new row
+ if(thisRow != -1 && thisRow != lastRowNumber) {
+ lastColumnNumber = -1;
+ }
+
+ // Handle missing column
+ if(record instanceof MissingCellDummyRecord) {
+ MissingCellDummyRecord mc = (MissingCellDummyRecord)record;
+ thisRow = mc.getRow();
+ thisColumn = mc.getColumn();
+ thisStr = "";
+ }
+
+ // If we got something to print out, do so
+ if(thisStr != null) {
+ if(thisColumn > 0) {
+ output.print(',');
+ }
+ output.print(thisStr);
+ }
+
+ // Update column and row count
+ if(thisRow > -1)
+ lastRowNumber = thisRow;
+ if(thisColumn > -1)
+ lastColumnNumber = thisColumn;
+
+ // Handle end of row
+ if(record instanceof LastCellOfRowDummyRecord) {
+ // Print out any missing commas if needed
+ if(minColumns > 0) {
+ // Columns are 0 based
+ if(lastColumnNumber == -1) { lastColumnNumber = 0; }
+ for(int i=lastColumnNumber; i<(minColumns); i++) {
+ output.print(',');
+ }
+ }
+
+ // We're onto a new row
+ lastColumnNumber = -1;
+
+ // End the row
+ output.println();
+ }
+ }
+
+ /**
+ * Formats a number or date cell, be that a real number, or the
+ * answer to a formula
+ */
+ private String formatNumberDateCell(CellValueRecordInterface cell, double value) {
+ // Get the built in format, if there is one
+ int formatIndex = formatListener.getFormatIndex(cell);
+ String formatString = formatListener.getFormatString(cell);
+
+ if(formatString == null) {
+ return Double.toString(value);
+ } else {
+ // Is it a date?
+ if(HSSFDateUtil.isADateFormat(formatIndex,formatString) &&
+ HSSFDateUtil.isValidExcelDate(value)) {
+ // Java wants M not m for month
+ formatString = formatString.replace('m','M');
+ // Change \- into -, if it's there
+ formatString = formatString.replaceAll("\\\\-","-");
+
+ // Format as a date
+ Date d = HSSFDateUtil.getJavaDate(value, false);
+ DateFormat df = new SimpleDateFormat(formatString);
+ return df.format(d);
+ } else {
+ if(formatString == "General") {
+ // Some sort of wierd default
+ return Double.toString(value);
+ }
+
+ // Format as a number
+ DecimalFormat df = new DecimalFormat(formatString);
+ return df.format(value);
+ }
+ }
+ }
+
+
+ public static void main(String[] args) throws Exception {
+ if(args.length < 1) {
+ System.err.println("Use:");
+ System.err.println(" XLS2CSVmra <xls file> [min columns]");
+ System.exit(1);
+ }
+
+ int minColumns = -1;
+ if(args.length >= 2) {
+ minColumns = Integer.parseInt(args[1]);
+ }
+
+ XLS2CSVmra xls2csv = new XLS2CSVmra(args[0], minColumns);
+ xls2csv.process();
+ }
+}
--- /dev/null
+/* ====================================================================
+ 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.hssf.extractor;
+
+import java.io.IOException;
+import java.text.DateFormat;
+import java.text.DecimalFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import org.apache.poi.POIOLE2TextExtractor;
+import org.apache.poi.hpsf.DocumentSummaryInformation;
+import org.apache.poi.hpsf.SummaryInformation;
+import org.apache.poi.hssf.eventusermodel.FormatTrackingHSSFListener;
+import org.apache.poi.hssf.eventusermodel.HSSFEventFactory;
+import org.apache.poi.hssf.eventusermodel.HSSFListener;
+import org.apache.poi.hssf.eventusermodel.HSSFRequest;
+import org.apache.poi.hssf.model.FormulaParser;
+import org.apache.poi.hssf.record.BOFRecord;
+import org.apache.poi.hssf.record.BoundSheetRecord;
+import org.apache.poi.hssf.record.CellValueRecordInterface;
+import org.apache.poi.hssf.record.FormulaRecord;
+import org.apache.poi.hssf.record.LabelRecord;
+import org.apache.poi.hssf.record.LabelSSTRecord;
+import org.apache.poi.hssf.record.NoteRecord;
+import org.apache.poi.hssf.record.NumberRecord;
+import org.apache.poi.hssf.record.Record;
+import org.apache.poi.hssf.record.SSTRecord;
+import org.apache.poi.hssf.record.StringRecord;
+import org.apache.poi.hssf.usermodel.HSSFDateUtil;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+
+/**
+ * A text extractor for Excel files, that is based
+ * on the hssf eventusermodel api.
+ * It will typically use less memory than
+ * {@link ExcelExtractor}, but may not provide
+ * the same richness of formatting.
+ * Returns the textual content of the file, suitable for
+ * indexing by something like Lucene, but not really
+ * intended for display to the user.
+ * To turn an excel file into a CSV or similar, then see
+ * the XLS2CSVmra example
+ * @see org.apache.poi.hssf.eventusermodel.examples.XLS2CSVmra
+ */
+public class EventBasedExcelExtractor extends POIOLE2TextExtractor {
+ private POIFSFileSystem fs;
+ private boolean includeSheetNames = true;
+ private boolean formulasNotResults = false;
+
+ public EventBasedExcelExtractor(POIFSFileSystem fs) throws IOException {
+ super(null);
+ this.fs = fs;
+ }
+
+ /**
+ * Would return the document information metadata for the document,
+ * if we supported it
+ */
+ public DocumentSummaryInformation getDocSummaryInformation() {
+ throw new IllegalStateException("Metadata extraction not supported in streaming mode, please use ExcelExtractor");
+ }
+ /**
+ * Would return the summary information metadata for the document,
+ * if we supported it
+ */
+ public SummaryInformation getSummaryInformation() {
+ throw new IllegalStateException("Metadata extraction not supported in streaming mode, please use ExcelExtractor");
+ }
+
+
+ /**
+ * Should sheet names be included? Default is true
+ */
+ public void setIncludeSheetNames(boolean includeSheetNames) {
+ this.includeSheetNames = includeSheetNames;
+ }
+ /**
+ * Should we return the formula itself, and not
+ * the result it produces? Default is false
+ */
+ public void setFormulasNotResults(boolean formulasNotResults) {
+ this.formulasNotResults = formulasNotResults;
+ }
+
+
+ /**
+ * Retreives the text contents of the file
+ */
+ public String getText() {
+ String text = null;
+ try {
+ TextListener tl = triggerExtraction();
+
+ text = tl.text.toString();
+ if(! text.endsWith("\n")) {
+ text = text + "\n";
+ }
+ } catch(IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ return text;
+ }
+
+ private TextListener triggerExtraction() throws IOException {
+ TextListener tl = new TextListener();
+ FormatTrackingHSSFListener ft = new FormatTrackingHSSFListener(tl);
+ tl.ft = ft;
+
+ // Register and process
+ HSSFEventFactory factory = new HSSFEventFactory();
+ HSSFRequest request = new HSSFRequest();
+ request.addListenerForAllRecords(ft);
+
+ factory.processWorkbookEvents(request, fs);
+
+ return tl;
+ }
+
+ private class TextListener implements HSSFListener {
+ private FormatTrackingHSSFListener ft;
+ private SSTRecord sstRecord;
+
+ private List sheetNames = new ArrayList();
+ private StringBuffer text = new StringBuffer();
+ private int sheetNum = -1;
+ private int rowNum;
+
+ private boolean outputNextStringValue = false;
+ private int nextRow = -1;
+
+ public void processRecord(Record record) {
+ String thisText = null;
+ int thisRow = -1;
+
+ switch(record.getSid()) {
+ case BoundSheetRecord.sid:
+ BoundSheetRecord sr = (BoundSheetRecord)record;
+ sheetNames.add(sr.getSheetname());
+ break;
+ case BOFRecord.sid:
+ BOFRecord bof = (BOFRecord)record;
+ if(bof.getType() == BOFRecord.TYPE_WORKSHEET) {
+ sheetNum++;
+ rowNum = -1;
+
+ if(includeSheetNames) {
+ if(text.length() > 0) text.append("\n");
+ text.append(sheetNames.get(sheetNum));
+ }
+ }
+ break;
+ case SSTRecord.sid:
+ sstRecord = (SSTRecord)record;
+ break;
+
+ case FormulaRecord.sid:
+ FormulaRecord frec = (FormulaRecord) record;
+ thisRow = frec.getRow();
+
+ if(formulasNotResults) {
+ thisText = FormulaParser.toFormulaString(null, frec.getParsedExpression());
+ } else {
+ if(Double.isNaN( frec.getValue() )) {
+ // Formula result is a string
+ // This is stored in the next record
+ outputNextStringValue = true;
+ nextRow = frec.getRow();
+ } else {
+ thisText = formatNumberDateCell(frec, frec.getValue());
+ }
+ }
+ break;
+ case StringRecord.sid:
+ if(outputNextStringValue) {
+ // String for formula
+ StringRecord srec = (StringRecord)record;
+ thisText = srec.getString();
+ thisRow = nextRow;
+ outputNextStringValue = false;
+ }
+ break;
+ case LabelRecord.sid:
+ LabelRecord lrec = (LabelRecord) record;
+ thisRow = lrec.getRow();
+ thisText = lrec.getValue();
+ break;
+ case LabelSSTRecord.sid:
+ LabelSSTRecord lsrec = (LabelSSTRecord) record;
+ thisRow = lsrec.getRow();
+ if(sstRecord == null) {
+ throw new IllegalStateException("No SST record found");
+ }
+ thisText = sstRecord.getString(lsrec.getSSTIndex()).toString();
+ break;
+ case NoteRecord.sid:
+ NoteRecord nrec = (NoteRecord) record;
+ thisRow = nrec.getRow();
+ // TODO: Find object to match nrec.getShapeId()
+ break;
+ case NumberRecord.sid:
+ NumberRecord numrec = (NumberRecord) record;
+ thisRow = numrec.getRow();
+ thisText = formatNumberDateCell(numrec, numrec.getValue());
+ break;
+ default:
+ break;
+ }
+
+ if(thisText != null) {
+ if(thisRow != rowNum) {
+ rowNum = thisRow;
+ if(text.length() > 0)
+ text.append("\n");
+ } else {
+ text.append("\t");
+ }
+ text.append(thisText);
+ }
+ }
+
+ /**
+ * Formats a number or date cell, be that a real number, or the
+ * answer to a formula
+ */
+ private String formatNumberDateCell(CellValueRecordInterface cell, double value) {
+ // Get the built in format, if there is one
+ int formatIndex = ft.getFormatIndex(cell);
+ String formatString = ft.getFormatString(cell);
+
+ if(formatString == null) {
+ return Double.toString(value);
+ } else {
+ // Is it a date?
+ if(HSSFDateUtil.isADateFormat(formatIndex,formatString) &&
+ HSSFDateUtil.isValidExcelDate(value)) {
+ // Java wants M not m for month
+ formatString = formatString.replace('m','M');
+ // Change \- into -, if it's there
+ formatString = formatString.replaceAll("\\\\-","-");
+
+ // Format as a date
+ Date d = HSSFDateUtil.getJavaDate(value, false);
+ DateFormat df = new SimpleDateFormat(formatString);
+ return df.format(d);
+ } else {
+ if(formatString == "General") {
+ // Some sort of wierd default
+ return Double.toString(value);
+ }
+
+ // Format as a number
+ DecimalFormat df = new DecimalFormat(formatString);
+ return df.format(value);
+ }
+ }
+ }
+ }
+}
import org.apache.poi.hssf.record.formula.eval.ValueEval;
import org.apache.poi.hssf.record.formula.functions.LookupUtils.ValueVector;
/**
- * Implementation of the VLOOKUP() function.<p/>
+ * Implementation of the HLOOKUP() function.<p/>
*
- * HLOOKUP finds a column in a lookup table by the first row value and returns the value from another row.
+ * HLOOKUP finds a column in a lookup table by the first row value and returns the value from another row.<br/>
*
* <b>Syntax</b>:<br/>
* <b>HLOOKUP</b>(<b>lookup_value</b>, <b>table_array</b>, <b>row_index_num</b>, range_lookup)<p/>
*
* <b>lookup_value</b> The value to be found in the first column of the table array.<br/>
- * <b>table_array</> An area reference for the lookup data. <br/>
+ * <b>table_array</b> An area reference for the lookup data. <br/>
* <b>row_index_num</b> a 1 based index specifying which row value of the lookup data will be returned.<br/>
* <b>range_lookup</b> If TRUE (default), HLOOKUP finds the largest value less than or equal to
* the lookup_value. If FALSE, only exact matches will be considered<br/>
/**
* Implementation of the VLOOKUP() function.<p/>
*
- * VLOOKUP finds a row in a lookup table by the first column value and returns the value from another column.
+ * VLOOKUP finds a row in a lookup table by the first column value and returns the value from another column.<br/>
*
* <b>Syntax</b>:<br/>
* <b>VLOOKUP</b>(<b>lookup_value</b>, <b>table_array</b>, <b>col_index_num</b>, range_lookup)<p/>
*
* <b>lookup_value</b> The value to be found in the first column of the table array.<br/>
- * <b>table_array</> An area reference for the lookup data. <br/>
+ * <b>table_array</b> An area reference for the lookup data. <br/>
* <b>col_index_num</b> a 1 based index specifying which column value of the lookup data will be returned.<br/>
* <b>range_lookup</b> If TRUE (default), VLOOKUP finds the largest value less than or equal to
* the lookup_value. If FALSE, only exact matches will be considered<br/>
+++ /dev/null
-/* ====================================================================
- 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.hssf.eventusermodel.examples;
-
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.PrintStream;
-import java.text.DateFormat;
-import java.text.DecimalFormat;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.Hashtable;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.poi.hssf.eventusermodel.FormatTrackingHSSFListener;
-import org.apache.poi.hssf.eventusermodel.HSSFEventFactory;
-import org.apache.poi.hssf.eventusermodel.HSSFListener;
-import org.apache.poi.hssf.eventusermodel.HSSFRequest;
-import org.apache.poi.hssf.eventusermodel.MissingRecordAwareHSSFListener;
-import org.apache.poi.hssf.eventusermodel.dummyrecord.LastCellOfRowDummyRecord;
-import org.apache.poi.hssf.eventusermodel.dummyrecord.MissingCellDummyRecord;
-import org.apache.poi.hssf.record.BlankRecord;
-import org.apache.poi.hssf.record.BoolErrRecord;
-import org.apache.poi.hssf.record.CellValueRecordInterface;
-import org.apache.poi.hssf.record.ExtendedFormatRecord;
-import org.apache.poi.hssf.record.FormatRecord;
-import org.apache.poi.hssf.record.FormulaRecord;
-import org.apache.poi.hssf.record.LabelRecord;
-import org.apache.poi.hssf.record.LabelSSTRecord;
-import org.apache.poi.hssf.record.NoteRecord;
-import org.apache.poi.hssf.record.NumberRecord;
-import org.apache.poi.hssf.record.RKRecord;
-import org.apache.poi.hssf.record.Record;
-import org.apache.poi.hssf.record.SSTRecord;
-import org.apache.poi.hssf.usermodel.HSSFDataFormat;
-import org.apache.poi.hssf.usermodel.HSSFDateUtil;
-import org.apache.poi.poifs.filesystem.POIFSFileSystem;
-
-/**
- * A XLS -> CSV processor, that uses the MissingRecordAware
- * EventModel code to ensure it outputs all columns and rows.
- * @author Nick Burch
- */
-public class XLS2CSVmra implements HSSFListener {
- private int minColumns;
- private POIFSFileSystem fs;
- private PrintStream output;
-
- private int lastRowNumber;
- private int lastColumnNumber;
-
- /** Should we output the formula, or the value it has? */
- private boolean outputFormulaValues = true;
-
- // Records we pick up as we process
- private SSTRecord sstRecord;
- private FormatTrackingHSSFListener formatListener;
-
- /**
- * Creates a new XLS -> CSV converter
- * @param fs The POIFSFileSystem to process
- * @param output The PrintStream to output the CSV to
- * @param minColumns The minimum number of columns to output, or -1 for no minimum
- */
- public XLS2CSVmra(POIFSFileSystem fs, PrintStream output, int minColumns) {
- this.fs = fs;
- this.output = output;
- this.minColumns = minColumns;
- }
-
- /**
- * Creates a new XLS -> CSV converter
- * @param filename The file to process
- * @param minColumns The minimum number of columns to output, or -1 for no minimum
- * @throws IOException
- * @throws FileNotFoundException
- */
- public XLS2CSVmra(String filename, int minColumns) throws IOException, FileNotFoundException {
- this(
- new POIFSFileSystem(new FileInputStream(filename)),
- System.out, minColumns
- );
- }
-
- /**
- * Initiates the processing of the XLS file to CSV
- */
- public void process() throws IOException {
- MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(this);
- formatListener = new FormatTrackingHSSFListener(listener);
-
- HSSFEventFactory factory = new HSSFEventFactory();
- HSSFRequest request = new HSSFRequest();
- request.addListenerForAllRecords(formatListener);
-
- factory.processWorkbookEvents(request, fs);
- }
-
- /**
- * Main HSSFListener method, processes events, and outputs the
- * CSV as the file is processed.
- */
- public void processRecord(Record record) {
- int thisRow = -1;
- int thisColumn = -1;
- String thisStr = null;
-
- switch (record.getSid())
- {
- case SSTRecord.sid:
- sstRecord = (SSTRecord) record;
- break;
-
- case BlankRecord.sid:
- BlankRecord brec = (BlankRecord) record;
-
- thisRow = brec.getRow();
- thisColumn = brec.getColumn();
- thisStr = "";
- break;
- case BoolErrRecord.sid:
- BoolErrRecord berec = (BoolErrRecord) record;
-
- thisRow = berec.getRow();
- thisColumn = berec.getColumn();
- thisStr = "";
- break;
- case FormulaRecord.sid:
- FormulaRecord frec = (FormulaRecord) record;
-
- thisRow = frec.getRow();
- thisColumn = frec.getColumn();
-
- if(outputFormulaValues) {
- thisStr = formatNumberDateCell(frec, frec.getValue());
- } else {
- // TODO: Output the formula string
- thisStr = '"' + frec.toString() + '"';
- }
- break;
- case LabelRecord.sid:
- LabelRecord lrec = (LabelRecord) record;
-
- thisRow = lrec.getRow();
- thisColumn = lrec.getColumn();
- thisStr = '"' + lrec.getValue() + '"';
- break;
- case LabelSSTRecord.sid:
- LabelSSTRecord lsrec = (LabelSSTRecord) record;
-
- thisRow = lsrec.getRow();
- thisColumn = lsrec.getColumn();
- if(sstRecord == null) {
- thisStr = '"' + "(No SST Record, can't identify string)" + '"';
- } else {
- thisStr = '"' + sstRecord.getString(lsrec.getSSTIndex()).toString() + '"';
- }
- break;
- case NoteRecord.sid:
- NoteRecord nrec = (NoteRecord) record;
-
- thisRow = nrec.getRow();
- thisColumn = nrec.getColumn();
- // TODO: Find object to match nrec.getShapeId()
- thisStr = '"' + "(TODO)" + '"';
- break;
- case NumberRecord.sid:
- NumberRecord numrec = (NumberRecord) record;
-
- thisRow = numrec.getRow();
- thisColumn = numrec.getColumn();
-
- // Format
- thisStr = formatNumberDateCell(numrec, numrec.getValue());
- break;
- case RKRecord.sid:
- RKRecord rkrec = (RKRecord) record;
-
- thisRow = rkrec.getRow();
- thisColumn = rkrec.getColumn();
- thisStr = '"' + "(TODO)" + '"';
- break;
- default:
- break;
- }
-
- // Handle new row
- if(thisRow != -1 && thisRow != lastRowNumber) {
- lastColumnNumber = -1;
- }
-
- // Handle missing column
- if(record instanceof MissingCellDummyRecord) {
- MissingCellDummyRecord mc = (MissingCellDummyRecord)record;
- thisRow = mc.getRow();
- thisColumn = mc.getColumn();
- thisStr = "";
- }
-
- // If we got something to print out, do so
- if(thisStr != null) {
- if(thisColumn > 0) {
- output.print(',');
- }
- output.print(thisStr);
- }
-
- // Update column and row count
- if(thisRow > -1)
- lastRowNumber = thisRow;
- if(thisColumn > -1)
- lastColumnNumber = thisColumn;
-
- // Handle end of row
- if(record instanceof LastCellOfRowDummyRecord) {
- // Print out any missing commas if needed
- if(minColumns > 0) {
- // Columns are 0 based
- if(lastColumnNumber == -1) { lastColumnNumber = 0; }
- for(int i=lastColumnNumber; i<(minColumns); i++) {
- output.print(',');
- }
- }
-
- // We're onto a new row
- lastColumnNumber = -1;
-
- // End the row
- output.println();
- }
- }
-
- /**
- * Formats a number or date cell, be that a real number, or the
- * answer to a formula
- */
- private String formatNumberDateCell(CellValueRecordInterface cell, double value) {
- // Get the built in format, if there is one
- int formatIndex = formatListener.getFormatIndex(cell);
- String formatString = formatListener.getFormatString(cell);
-
- if(formatString == null) {
- return Double.toString(value);
- } else {
- // Is it a date?
- if(HSSFDateUtil.isADateFormat(formatIndex,formatString) &&
- HSSFDateUtil.isValidExcelDate(value)) {
- // Java wants M not m for month
- formatString = formatString.replace('m','M');
- // Change \- into -, if it's there
- formatString = formatString.replaceAll("\\\\-","-");
-
- // Format as a date
- Date d = HSSFDateUtil.getJavaDate(value, false);
- DateFormat df = new SimpleDateFormat(formatString);
- return df.format(d);
- } else {
- if(formatString == "General") {
- // Some sort of wierd default
- return Double.toString(value);
- }
-
- // Format as a number
- DecimalFormat df = new DecimalFormat(formatString);
- return df.format(value);
- }
- }
- }
-
-
- public static void main(String[] args) throws Exception {
- if(args.length < 1) {
- System.err.println("Use:");
- System.err.println(" XLS2CSVmra <xls file> [min columns]");
- System.exit(1);
- }
-
- int minColumns = -1;
- if(args.length >= 2) {
- minColumns = Integer.parseInt(args[1]);
- }
-
- XLS2CSVmra xls2csv = new XLS2CSVmra(args[0], minColumns);
- xls2csv.process();
- }
-}
assertEquals("Sheet1\nUPPER(\"xyz\")\nSheet2\nSheet3\n", extractor.getText());
}
+
+ public void testEventExtractor() throws Exception {
+ EventBasedExcelExtractor extractor;
+
+ // First up, a simple file with string
+ // based formulas in it
+ extractor = new EventBasedExcelExtractor(
+ new POIFSFileSystem(
+ HSSFTestDataSamples.openSampleFileStream("SimpleWithFormula.xls")
+ )
+ );
+ extractor.setIncludeSheetNames(true);
+
+ String text = extractor.getText();
+ assertEquals("Sheet1\nreplaceme\nreplaceme\nreplacemereplaceme\nSheet2\nSheet3\n", text);
+
+ extractor.setIncludeSheetNames(false);
+ extractor.setFormulasNotResults(true);
+
+ text = extractor.getText();
+ assertEquals("replaceme\nreplaceme\nCONCATENATE(A1,A2)\n", text);
+
+
+ // Now, a slightly longer file with numeric formulas
+ extractor = new EventBasedExcelExtractor(
+ new POIFSFileSystem(
+ HSSFTestDataSamples.openSampleFileStream("sumifformula.xls")
+ )
+ );
+ extractor.setIncludeSheetNames(false);
+ extractor.setFormulasNotResults(true);
+
+ text = extractor.getText();
+ assertEquals(
+ "1000.0\t1.0\tSUMIF(A1:A5,\">4000\",B1:B5)\n" +
+ "2000.0\t2.0\n" +
+ "3000.0\t3.0\n" +
+ "4000.0\t4.0\n" +
+ "5000.0\t5.0\n",
+ text
+ );
+ }
+
+
/**
* Embded in a non-excel file
*/