diff --git a/build.xml b/build.xml index 207acc4c68..ce3c83c860 100644 --- a/build.xml +++ b/build.xml @@ -60,6 +60,10 @@ under the License. + + + + - - - - - - - - - - @@ -1180,6 +1173,59 @@ under the License. + + + + + + + + + + + sun/java2d/pipe/AAShapePipe.renderTiles(Lsun/java2d/SunGraphics2D;Ljava/awt/Shape;Lsun/java2d/pipe/AATileGenerator;[I)V + sun/java2d/pipe/AlphaPaintPipe.renderPathTile(Ljava/lang/Object;[BIIIIII)V + java/awt/TexturePaintContext.getRaster(IIII)Ljava/awt/image/Raster; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1219,39 +1265,23 @@ under the License. - + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + - + @@ -1306,6 +1336,7 @@ under the License. + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + @@ -1500,45 +1514,23 @@ under the License. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + @@ -1559,72 +1551,35 @@ under the License. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1650,34 +1605,16 @@ under the License. unless="integration.test.notRequired" xmlns:jacoco="antlib:org.jacoco.ant"> - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + @@ -1739,33 +1676,16 @@ under the License. - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + @@ -1791,6 +1711,9 @@ under the License. + + + @@ -1828,7 +1751,7 @@ under the License. - + POI API Documentation]]> @@ -2113,6 +2036,7 @@ under the License. sonar/*/src/**, compile-lib/**, ooxml-lib/**, + ooxml-testlib/**, scripts/**, TEST*, *.ipr, @@ -2303,7 +2227,10 @@ under the License. - + + @@ -2453,7 +2380,7 @@ under the License. - + diff --git a/doap_POI.rdf b/doap_POI.rdf index f1d21a8cee..88ba48f04d 100644 --- a/doap_POI.rdf +++ b/doap_POI.rdf @@ -35,6 +35,13 @@ Java + + + Apache POI 4.0.1 + 2018-12-03 + 4.0.1 + + Apache POI 4.0.0 diff --git a/src/examples/src/org/apache/poi/xslf/usermodel/ChartFromScratch.java b/src/examples/src/org/apache/poi/xslf/usermodel/ChartFromScratch.java new file mode 100644 index 0000000000..12f96fa6fd --- /dev/null +++ b/src/examples/src/org/apache/poi/xslf/usermodel/ChartFromScratch.java @@ -0,0 +1,145 @@ +/* + * ==================================================================== + * 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.xslf.usermodel; + +import java.awt.geom.Rectangle2D; +import java.io.BufferedReader; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; + +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.xddf.usermodel.chart.AxisCrosses; +import org.apache.poi.xddf.usermodel.chart.AxisPosition; +import org.apache.poi.xddf.usermodel.chart.BarDirection; +import org.apache.poi.xddf.usermodel.chart.ChartTypes; +import org.apache.poi.xddf.usermodel.chart.LegendPosition; +import org.apache.poi.xddf.usermodel.chart.XDDFBarChartData; +import org.apache.poi.xddf.usermodel.chart.XDDFChart; +import org.apache.poi.xddf.usermodel.chart.XDDFChartAxis; +import org.apache.poi.xddf.usermodel.chart.XDDFChartLegend; +import org.apache.poi.xddf.usermodel.chart.XDDFDataSource; +import org.apache.poi.xddf.usermodel.chart.XDDFDataSourcesFactory; +import org.apache.poi.xddf.usermodel.chart.XDDFNumericalDataSource; +import org.apache.poi.xddf.usermodel.chart.XDDFValueAxis; + +/** + * Build a chart without reading template file + */ +public class ChartFromScratch { + private static void usage(){ + System.out.println("Usage: BarChartExample "); + System.out.println(" bar-chart-data.txt the model to set. First line is chart title, " + + "then go pairs {axis-label value}"); + } + + public static void main(String[] args) throws Exception { + if(args.length < 1) { + usage(); + return; + } + + try (BufferedReader modelReader = new BufferedReader(new FileReader(args[0]))) { + + String chartTitle = modelReader.readLine(); // first line is chart title + String[] series = modelReader.readLine().split(","); + + // Category Axis Data + List listLanguages = new ArrayList<>(10); + + // Values + List listCountries = new ArrayList<>(10); + List listSpeakers = new ArrayList<>(10); + + // set model + String ln; + while((ln = modelReader.readLine()) != null) { + String[] vals = ln.split(","); + listCountries.add(Double.valueOf(vals[0])); + listSpeakers.add(Double.valueOf(vals[1])); + listLanguages.add(vals[2]); + } + + String[] categories = listLanguages.toArray(new String[listLanguages.size()]); + Double[] values1 = listCountries.toArray(new Double[listCountries.size()]); + Double[] values2 = listSpeakers.toArray(new Double[listSpeakers.size()]); + + try { + + XMLSlideShow ppt = new XMLSlideShow(); + XSLFSlide slide = ppt.createSlide(); + XSLFChart chart = ppt.createChart(); + Rectangle2D rect2D = new java.awt.Rectangle(XDDFChart.DEFAULT_X, XDDFChart.DEFAULT_Y, + XDDFChart.DEFAULT_WIDTH, XDDFChart.DEFAULT_HEIGHT); + slide.addChart(chart, rect2D); + setBarData(chart, chartTitle, series, categories, values1, values2); + // save the result + try (OutputStream out = new FileOutputStream("bar-chart-demo-output.pptx")) { + ppt.write(out); + } + } + catch(Exception e) + { + e.printStackTrace(); + } + } + System.out.println("Done"); + } + + private static void setBarData(XSLFChart chart, String chartTitle, String[] series, String[] categories, Double[] values1, Double[] values2) { + // Use a category axis for the bottom axis. + XDDFChartAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM); + bottomAxis.setTitle(series[2]); + XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT); + leftAxis.setTitle(series[0]+","+series[1]); + leftAxis.setCrosses(AxisCrosses.AUTO_ZERO); + + final int numOfPoints = categories.length; + final String categoryDataRange = chart.formatRange(new CellRangeAddress(1, numOfPoints, 0, 0)); + final String valuesDataRange = chart.formatRange(new CellRangeAddress(1, numOfPoints, 1, 1)); + final String valuesDataRange2 = chart.formatRange(new CellRangeAddress(1, numOfPoints, 2, 2)); + final XDDFDataSource categoriesData = XDDFDataSourcesFactory.fromArray(categories, categoryDataRange, 0); + final XDDFNumericalDataSource valuesData = XDDFDataSourcesFactory.fromArray(values1, valuesDataRange, 1); + values1[6] = 16.0; // if you ever want to change the underlying data + final XDDFNumericalDataSource valuesData2 = XDDFDataSourcesFactory.fromArray(values2, valuesDataRange2, 2); + + + XDDFBarChartData bar = (XDDFBarChartData) chart.createData(ChartTypes.BAR, bottomAxis, leftAxis); + XDDFBarChartData.Series series1 = (XDDFBarChartData.Series) bar.addSeries(categoriesData, valuesData); + series1.setTitle(series[0], chart.setSheetTitle(series[0], 1)); + + XDDFBarChartData.Series series2 = (XDDFBarChartData.Series) bar.addSeries(categoriesData, valuesData2); + series2.setTitle(series[1], chart.setSheetTitle(series[1], 2)); + + bar.setVaryColors(true); + bar.setBarDirection(BarDirection.COL); + chart.plot(bar); + + XDDFChartLegend legend = chart.getOrAddLegend(); + legend.setPosition(LegendPosition.LEFT); + legend.setOverlay(false); + + chart.setTitleText(chartTitle); + chart.setTitleOverlay(false); + } +} + diff --git a/src/examples/src/org/apache/poi/xwpf/usermodel/examples/ChartFromScratch.java b/src/examples/src/org/apache/poi/xwpf/usermodel/examples/ChartFromScratch.java new file mode 100644 index 0000000000..4a1a78150f --- /dev/null +++ b/src/examples/src/org/apache/poi/xwpf/usermodel/examples/ChartFromScratch.java @@ -0,0 +1,140 @@ +/* + * ==================================================================== + * 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.xwpf.usermodel.examples; + +import java.io.BufferedReader; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; + +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.xddf.usermodel.chart.AxisCrosses; +import org.apache.poi.xddf.usermodel.chart.AxisPosition; +import org.apache.poi.xddf.usermodel.chart.BarDirection; +import org.apache.poi.xddf.usermodel.chart.ChartTypes; +import org.apache.poi.xddf.usermodel.chart.LegendPosition; +import org.apache.poi.xddf.usermodel.chart.XDDFBarChartData; +import org.apache.poi.xddf.usermodel.chart.XDDFChart; +import org.apache.poi.xddf.usermodel.chart.XDDFChartAxis; +import org.apache.poi.xddf.usermodel.chart.XDDFChartLegend; +import org.apache.poi.xddf.usermodel.chart.XDDFDataSource; +import org.apache.poi.xddf.usermodel.chart.XDDFDataSourcesFactory; +import org.apache.poi.xddf.usermodel.chart.XDDFNumericalDataSource; +import org.apache.poi.xddf.usermodel.chart.XDDFValueAxis; +import org.apache.poi.xwpf.usermodel.XWPFChart; +import org.apache.poi.xwpf.usermodel.XWPFDocument; + +/** + * Build a chart without reading template file + */ +public class ChartFromScratch { + private static void usage(){ + System.out.println("Usage: BarChartExample "); + System.out.println(" bar-chart-data.txt the model to set. First line is chart title, " + + "then go pairs {axis-label value}"); + } + + public static void main(String[] args) throws Exception { + if(args.length < 1) { + usage(); + return; + } + + try (BufferedReader modelReader = new BufferedReader(new FileReader(args[0]))) { + + String chartTitle = modelReader.readLine(); // first line is chart title + String[] series = modelReader.readLine().split(","); + + // Category Axis Data + List listLanguages = new ArrayList<>(10); + + // Values + List listCountries = new ArrayList<>(10); + List listSpeakers = new ArrayList<>(10); + + // set model + String ln; + while((ln = modelReader.readLine()) != null) { + String[] vals = ln.split(","); + listCountries.add(Double.valueOf(vals[0])); + listSpeakers.add(Double.valueOf(vals[1])); + listLanguages.add(vals[2]); + } + + String[] categories = listLanguages.toArray(new String[listLanguages.size()]); + Double[] values1 = listCountries.toArray(new Double[listCountries.size()]); + Double[] values2 = listSpeakers.toArray(new Double[listSpeakers.size()]); + + try (XWPFDocument doc = new XWPFDocument()) { + XWPFChart chart = doc.createChart(XDDFChart.DEFAULT_WIDTH, XDDFChart.DEFAULT_HEIGHT); + setBarData(chart, chartTitle, series, categories, values1, values2); + // save the result + try (OutputStream out = new FileOutputStream("bar-chart-demo-output.docx")) { + doc.write(out); + } + } + catch(Exception e) + { + e.printStackTrace(); + } + } + System.out.println("Done"); + } + + private static void setBarData(XWPFChart chart, String chartTitle, String[] series, String[] categories, Double[] values1, Double[] values2) { + // Use a category axis for the bottom axis. + XDDFChartAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM); + bottomAxis.setTitle(series[2]); + XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT); + leftAxis.setTitle(series[0]+","+series[1]); + leftAxis.setCrosses(AxisCrosses.AUTO_ZERO); + + final int numOfPoints = categories.length; + final String categoryDataRange = chart.formatRange(new CellRangeAddress(1, numOfPoints, 0, 0)); + final String valuesDataRange = chart.formatRange(new CellRangeAddress(1, numOfPoints, 1, 1)); + final String valuesDataRange2 = chart.formatRange(new CellRangeAddress(1, numOfPoints, 2, 2)); + final XDDFDataSource categoriesData = XDDFDataSourcesFactory.fromArray(categories, categoryDataRange, 0); + final XDDFNumericalDataSource valuesData = XDDFDataSourcesFactory.fromArray(values1, valuesDataRange, 1); + values1[6] = 16.0; // if you ever want to change the underlying data + final XDDFNumericalDataSource valuesData2 = XDDFDataSourcesFactory.fromArray(values2, valuesDataRange2, 2); + + + XDDFBarChartData bar = (XDDFBarChartData) chart.createData(ChartTypes.BAR, bottomAxis, leftAxis); + XDDFBarChartData.Series series1 = (XDDFBarChartData.Series) bar.addSeries(categoriesData, valuesData); + series1.setTitle(series[0], chart.setSheetTitle(series[0], 1)); + + XDDFBarChartData.Series series2 = (XDDFBarChartData.Series) bar.addSeries(categoriesData, valuesData2); + series2.setTitle(series[1], chart.setSheetTitle(series[1], 2)); + + bar.setVaryColors(true); + bar.setBarDirection(BarDirection.COL); + chart.plot(bar); + + XDDFChartLegend legend = chart.getOrAddLegend(); + legend.setPosition(LegendPosition.LEFT); + legend.setOverlay(false); + + chart.setTitleText(chartTitle); + chart.setTitleOverlay(false); + } +} + diff --git a/src/integrationtest/org/apache/poi/TestAllFiles.java b/src/integrationtest/org/apache/poi/TestAllFiles.java index 61c47b9e88..40f19f9c85 100644 --- a/src/integrationtest/org/apache/poi/TestAllFiles.java +++ b/src/integrationtest/org/apache/poi/TestAllFiles.java @@ -289,6 +289,7 @@ public class TestAllFiles { "document/Bug50955.doc", "document/57843.doc", "slideshow/PPT95.ppt", + "slideshow/pp40only.ppt", "slideshow/Divino_Revelado.pptx", "openxml4j/OPCCompliance_CoreProperties_DCTermsNamespaceLimitedUseFAIL.docx", "openxml4j/OPCCompliance_CoreProperties_DoNotUseCompatibilityMarkupFAIL.docx", diff --git a/src/integrationtest/org/apache/poi/stress/XSSFFileHandler.java b/src/integrationtest/org/apache/poi/stress/XSSFFileHandler.java index 5c47d9af7e..48a30a2569 100644 --- a/src/integrationtest/org/apache/poi/stress/XSSFFileHandler.java +++ b/src/integrationtest/org/apache/poi/stress/XSSFFileHandler.java @@ -35,7 +35,6 @@ import java.util.Iterator; import java.util.Locale; import java.util.Set; -import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerException; import org.apache.poi.EncryptedDocumentException; @@ -148,7 +147,7 @@ public class XSSFFileHandler extends SpreadsheetHandler { } private void exportToXML(XSSFWorkbook wb) throws SAXException, - ParserConfigurationException, TransformerException { + TransformerException { for (XSSFMap map : wb.getCustomXMLMappings()) { XSSFExportToXml exporter = new XSSFExportToXml(map); @@ -165,7 +164,6 @@ public class XSSFFileHandler extends SpreadsheetHandler { // zip-bomb EXPECTED_ADDITIONAL_FAILURES.add("spreadsheet/54764.xlsx"); EXPECTED_ADDITIONAL_FAILURES.add("spreadsheet/54764-2.xlsx"); - EXPECTED_ADDITIONAL_FAILURES.add("spreadsheet/54764.xlsx"); EXPECTED_ADDITIONAL_FAILURES.add("spreadsheet/poc-xmlbomb.xlsx"); EXPECTED_ADDITIONAL_FAILURES.add("spreadsheet/poc-xmlbomb-empty.xlsx"); // strict OOXML @@ -185,18 +183,19 @@ public class XSSFFileHandler extends SpreadsheetHandler { public void handleAdditional(File file) throws Exception { // redirect stdout as the examples often write lots of text PrintStream oldOut = System.out; + String testFile = file.getParentFile().getName() + "/" + file.getName(); try { System.setOut(new NullPrintStream()); FromHowTo.main(new String[]{file.getAbsolutePath()}); XLSX2CSV.main(new String[]{file.getAbsolutePath()}); assertFalse("Expected Extraction to fail for file " + file + " and handler " + this + ", but did not fail!", - EXPECTED_ADDITIONAL_FAILURES.contains(file.getParentFile().getName() + "/" + file.getName())); + EXPECTED_ADDITIONAL_FAILURES.contains(testFile)); } catch (OLE2NotOfficeXmlFileException e) { // we have some files that are not actually OOXML and thus cannot be tested here } catch (IllegalArgumentException | InvalidFormatException | POIXMLException | IOException e) { - if(!EXPECTED_ADDITIONAL_FAILURES.contains(file.getParentFile().getName() + "/" + file.getName())) { + if(!EXPECTED_ADDITIONAL_FAILURES.contains(testFile)) { throw e; } } finally { diff --git a/src/java/org/apache/poi/ddf/EscherProperties.java b/src/java/org/apache/poi/ddf/EscherProperties.java index acff6241c2..482bce95c5 100644 --- a/src/java/org/apache/poi/ddf/EscherProperties.java +++ b/src/java/org/apache/poi/ddf/EscherProperties.java @@ -26,6 +26,7 @@ import java.util.Map; * * @author Glen Stampoultzis (glens at apache.org) */ +@SuppressWarnings("WeakerAccess") public final class EscherProperties { // Property constants @@ -117,6 +118,15 @@ public final class EscherProperties { public static final short GEOMETRY__ADJUST8VALUE = 334; public static final short GEOMETRY__ADJUST9VALUE = 335; public static final short GEOMETRY__ADJUST10VALUE = 336; + public static final short GEOMETRY__PCONNECTIONSITES = 337; + public static final short GEOMETRY__PCONNECTIONSITESDIR = 338; + public static final short GEOMETRY__XLIMO = 339; + public static final short GEOMETRY__YLIMO = 340; + public static final short GEOMETRY__PADJUSTHANDLES = 341; + public static final short GEOMETRY__PGUIDES = 342; + public static final short GEOMETRY__PINSCRIBE = 343; + public static final short GEOMETRY__CXK = 344; + public static final short GEOMETRY__PFRAGMENTS = 345; public static final short GEOMETRY__SHADOWok = 378; public static final short GEOMETRY__3DOK = 379; public static final short GEOMETRY__LINEOK = 380; @@ -333,6 +343,9 @@ public final class EscherProperties { private static final Map properties = initProps(); + private EscherProperties() { + } + private static Map initProps() { Map m = new HashMap<>(); addProp(m, TRANSFORM__ROTATION, "transform.rotation"); @@ -423,6 +436,15 @@ public final class EscherProperties { addProp(m, GEOMETRY__ADJUST8VALUE, "geometry.adjust8value"); addProp(m, GEOMETRY__ADJUST9VALUE, "geometry.adjust9value"); addProp(m, GEOMETRY__ADJUST10VALUE, "geometry.adjust10value"); + addProp(m, GEOMETRY__PCONNECTIONSITES, "geometry.pConnectionSites"); + addProp(m, GEOMETRY__PCONNECTIONSITESDIR, "geometry.pConnectionSitesDir"); + addProp(m, GEOMETRY__XLIMO, "geometry.xLimo"); + addProp(m, GEOMETRY__YLIMO, "geometry.yLimo"); + addProp(m, GEOMETRY__PADJUSTHANDLES, "geometry.pAdjustHandles"); + addProp(m, GEOMETRY__PGUIDES, "geometry.pGuides"); + addProp(m, GEOMETRY__PINSCRIBE, "geometry.pInscribe"); + addProp(m, GEOMETRY__CXK, "geometry.cxk"); + addProp(m, GEOMETRY__PFRAGMENTS, "geometry.pFragments"); addProp(m, GEOMETRY__SHADOWok, "geometry.shadowOK"); addProp(m, GEOMETRY__3DOK, "geometry.3dok"); addProp(m, GEOMETRY__LINEOK, "geometry.lineok"); @@ -641,20 +663,20 @@ public final class EscherProperties { } private static void addProp(Map m, int s, String propName) { - m.put(Short.valueOf((short) s), new EscherPropertyMetaData(propName)); + m.put((short) s, new EscherPropertyMetaData(propName)); } private static void addProp(Map m, int s, String propName, byte type) { - m.put(Short.valueOf((short) s), new EscherPropertyMetaData(propName, type)); + m.put((short) s, new EscherPropertyMetaData(propName, type)); } public static String getPropertyName(short propertyId) { - EscherPropertyMetaData o = properties.get(Short.valueOf(propertyId)); + EscherPropertyMetaData o = properties.get(propertyId); return o == null ? "unknown" : o.getDescription(); } public static byte getPropertyType(short propertyId) { - EscherPropertyMetaData escherPropertyMetaData = properties.get(Short.valueOf(propertyId)); + EscherPropertyMetaData escherPropertyMetaData = properties.get(propertyId); return escherPropertyMetaData == null ? 0 : escherPropertyMetaData.getType(); } } diff --git a/src/java/org/apache/poi/hpsf/VariantSupport.java b/src/java/org/apache/poi/hpsf/VariantSupport.java index e60679027d..0e8dc08c6e 100644 --- a/src/java/org/apache/poi/hpsf/VariantSupport.java +++ b/src/java/org/apache/poi/hpsf/VariantSupport.java @@ -36,7 +36,7 @@ import org.apache.poi.util.POILogger; * Supports reading and writing of variant data.

* * FIXME (3): Reading and writing should be made more - * uniform than it is now. The following items should be resolved:

+ * uniform than it is now. The following items should be resolved: * *

    * diff --git a/src/java/org/apache/poi/hpsf/wellknown/PropertyIDMap.java b/src/java/org/apache/poi/hpsf/wellknown/PropertyIDMap.java index 130408e7b5..c01ded7fe8 100644 --- a/src/java/org/apache/poi/hpsf/wellknown/PropertyIDMap.java +++ b/src/java/org/apache/poi/hpsf/wellknown/PropertyIDMap.java @@ -33,8 +33,11 @@ import org.apache.poi.hpsf.SummaryInformation; * The methods {@link #getSummaryInformationProperties} and {@link * #getDocumentSummaryInformationProperties} return singleton {@link * PropertyIDMap}s. An application that wants to extend these maps - * should treat them as unmodifiable, copy them and modifiy the + * should treat them as unmodifiable, copy them and modify the * copies. + * + * Trying to modify the map directly will cause exceptions + * {@link UnsupportedOperationException} to be thrown. */ public class PropertyIDMap implements Map { @@ -490,11 +493,13 @@ public class PropertyIDMap implements Map { @Override public String put(Long key, String value) { + //noinspection ConstantConditions return idMap.put(key, value); } @Override public String remove(Object key) { + //noinspection ConstantConditions return idMap.remove(key); } diff --git a/src/java/org/apache/poi/hssf/model/InternalWorkbook.java b/src/java/org/apache/poi/hssf/model/InternalWorkbook.java index 46d8678229..4ff370125c 100644 --- a/src/java/org/apache/poi/hssf/model/InternalWorkbook.java +++ b/src/java/org/apache/poi/hssf/model/InternalWorkbook.java @@ -2276,6 +2276,8 @@ public final class InternalWorkbook { /** * Only for internal calls - code based on this is not supported ... + * + * @return The list of records. */ @Internal public WorkbookRecordList getWorkbookRecordList() { diff --git a/src/java/org/apache/poi/hssf/record/RecordInputStream.java b/src/java/org/apache/poi/hssf/record/RecordInputStream.java index 3c9b977c79..400553721e 100644 --- a/src/java/org/apache/poi/hssf/record/RecordInputStream.java +++ b/src/java/org/apache/poi/hssf/record/RecordInputStream.java @@ -33,8 +33,9 @@ import org.apache.poi.util.LittleEndianInputStream; import org.apache.poi.util.RecordFormatException; /** - * Title: Record Input Stream

    - * Description: Wraps a stream and provides helper methods for the construction of records.

    + * Title: Record Input Stream + * + * Description: Wraps a stream and provides helper methods for the construction of records. */ public final class RecordInputStream implements LittleEndianInput { @@ -142,6 +143,15 @@ public final class RecordInputStream implements LittleEndianInput { _nextSid = readNextSid(); } + static LittleEndianInput getLEI(InputStream is) { + if (is instanceof LittleEndianInput) { + // accessing directly is an optimisation + return (LittleEndianInput) is; + } + // less optimal, but should work OK just the same. Often occurs in junit tests. + return new LittleEndianInputStream(is); + } + /** * @return the number of bytes available in the current BIFF record * @see #remaining() @@ -295,12 +305,9 @@ public final class RecordInputStream implements LittleEndianInput { return _dataInput.readUShort(); } - /** - * - * @return a double - might return NaN - */ @Override public double readDouble() { + // YK: Excel doesn't write NaN but instead converts the cell type into {@link CellType#ERROR}. return Double.longBitsToDouble(readLong()); } diff --git a/src/java/org/apache/poi/hssf/record/SSTRecord.java b/src/java/org/apache/poi/hssf/record/SSTRecord.java index 20d99319d6..1de2fe4fc2 100644 --- a/src/java/org/apache/poi/hssf/record/SSTRecord.java +++ b/src/java/org/apache/poi/hssf/record/SSTRecord.java @@ -161,7 +161,7 @@ public final class SSTRecord extends ContinuableRecord { *

    * The data consists of sets of string data. This string data is * arranged as follows: - *

    + *

    *

          * short  string_length;   // length of string data
          * byte   string_flag;     // flag specifying special string
    @@ -176,9 +176,9 @@ public final class SSTRecord extends ContinuableRecord {
          * byte[] extension;       // optional extension (length of array
          *                         // is extend_length)
          * 
    - *

    + *

    * The string_flag is bit mapped as follows: - *

    + *

    * * * @@ -232,7 +232,7 @@ public final class SSTRecord extends ContinuableRecord { * associated data. The UnicodeString class can handle the byte[] * vs short[] nature of the actual string data * - * @param in the RecordInputstream to read the record from + * @param in the RecordInputStream to read the record from */ public SSTRecord(RecordInputStream in) { // this method is ALWAYS called after construction -- using diff --git a/src/java/org/apache/poi/hssf/record/SharedFormulaRecord.java b/src/java/org/apache/poi/hssf/record/SharedFormulaRecord.java index 770f740a09..328402db80 100644 --- a/src/java/org/apache/poi/hssf/record/SharedFormulaRecord.java +++ b/src/java/org/apache/poi/hssf/record/SharedFormulaRecord.java @@ -77,7 +77,7 @@ public final class SharedFormulaRecord extends SharedValueRecordBase { public String toString() { - StringBuffer buffer = new StringBuffer(); + StringBuilder buffer = new StringBuilder(); buffer.append("[SHARED FORMULA (").append(HexDump.intToHex(sid)).append("]\n"); buffer.append(" .range = ").append(getRange()).append("\n"); @@ -99,6 +99,10 @@ public final class SharedFormulaRecord extends SharedValueRecordBase { } /** + * Convert formula into an array of {@link Ptg} tokens. + * + * @param formula The record to break into tokens, cannot be null + * * @return the equivalent {@link Ptg} array that the formula would have, were it not shared. */ public Ptg[] getFormulaTokens(FormulaRecord formula) { diff --git a/src/java/org/apache/poi/hssf/record/SharedValueRecordBase.java b/src/java/org/apache/poi/hssf/record/SharedValueRecordBase.java index a5035303a3..51fe091808 100644 --- a/src/java/org/apache/poi/hssf/record/SharedValueRecordBase.java +++ b/src/java/org/apache/poi/hssf/record/SharedValueRecordBase.java @@ -42,6 +42,8 @@ public abstract class SharedValueRecordBase extends StandardRecord { /** * reads only the range (1 {@link CellRangeAddress8Bit}) from the stream + * + * @param in The interface for reading the record data. */ public SharedValueRecordBase(LittleEndianInput in) { _range = new CellRangeAddress8Bit(in); @@ -99,14 +101,12 @@ public abstract class SharedValueRecordBase extends StandardRecord { && r.getLastColumn() >= colIx; } /** - * @return {@code true} if (rowIx, colIx) describes the first cell in this shared value - * object's range - * * @param rowIx the row index * @param colIx the column index - * - * @return {@code true} if its the first cell in this shared value object range - * + * + * @return {@code true} if (rowIx, colIx) describes the first cell in this shared value + * object's range + * * @see #getRange() */ public final boolean isFirstCell(int rowIx, int colIx) { diff --git a/src/java/org/apache/poi/poifs/crypt/ChunkedCipherOutputStream.java b/src/java/org/apache/poi/poifs/crypt/ChunkedCipherOutputStream.java index 9da03cf2e2..0aa73fd670 100644 --- a/src/java/org/apache/poi/poifs/crypt/ChunkedCipherOutputStream.java +++ b/src/java/org/apache/poi/poifs/crypt/ChunkedCipherOutputStream.java @@ -99,6 +99,13 @@ public abstract class ChunkedCipherOutputStream extends FilterOutputStream { return initCipherForBlock(cipher, block, lastChunk); } + // helper method to break a recursion loop introduced because of an IBMJCE bug, i.e. not resetting on Cipher.doFinal() + @Internal + protected Cipher initCipherForBlockNoFlush(Cipher existing, int block, boolean lastChunk) + throws IOException, GeneralSecurityException { + return initCipherForBlock(cipher, block, lastChunk); + } + protected abstract Cipher initCipherForBlock(Cipher existing, int block, boolean lastChunk) throws IOException, GeneralSecurityException; @@ -212,13 +219,30 @@ public abstract class ChunkedCipherOutputStream extends FilterOutputStream { * @throws IllegalBlockSizeException * @throws ShortBufferException */ - protected int invokeCipher(int posInChunk, boolean doFinal) throws GeneralSecurityException { + protected int invokeCipher(int posInChunk, boolean doFinal) throws GeneralSecurityException, IOException { byte plain[] = (plainByteFlags.isEmpty()) ? null : chunk.clone(); int ciLen = (doFinal) ? cipher.doFinal(chunk, 0, posInChunk, chunk) : cipher.update(chunk, 0, posInChunk, chunk); + if (doFinal && "IBMJCE".equals(cipher.getProvider().getName()) && "RC4".equals(cipher.getAlgorithm())) { + // workaround for IBMs cipher not resetting on doFinal + + int index = (int)(pos >> chunkBits); + boolean lastChunk; + if (posInChunk==0) { + index--; + posInChunk = chunk.length; + lastChunk = false; + } else { + // pad the last chunk + lastChunk = true; + } + + cipher = initCipherForBlockNoFlush(cipher, index, lastChunk); + } + if (plain != null) { int i = plainByteFlags.nextSetBit(0); while (i >= 0 && i < posInChunk) { diff --git a/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptor.java b/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptor.java index b412585261..9221d4a390 100644 --- a/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptor.java +++ b/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptor.java @@ -207,9 +207,15 @@ public class CryptoAPIEncryptor extends Encryptor implements Cloneable { protected Cipher initCipherForBlock(Cipher cipher, int block, boolean lastChunk) throws IOException, GeneralSecurityException { flush(); + return initCipherForBlockNoFlush(cipher, block, lastChunk); + } + + @Override + protected Cipher initCipherForBlockNoFlush(Cipher existing, int block, boolean lastChunk) + throws GeneralSecurityException { EncryptionInfo ei = getEncryptionInfo(); SecretKey sk = getSecretKey(); - return CryptoAPIDecryptor.initCipherForBlock(cipher, block, ei, sk, Cipher.ENCRYPT_MODE); + return CryptoAPIDecryptor.initCipherForBlock(existing, block, ei, sk, Cipher.ENCRYPT_MODE); } @Override diff --git a/src/java/org/apache/poi/poifs/filesystem/FileMagic.java b/src/java/org/apache/poi/poifs/filesystem/FileMagic.java index 4ac616082e..bab62c6437 100644 --- a/src/java/org/apache/poi/poifs/filesystem/FileMagic.java +++ b/src/java/org/apache/poi/poifs/filesystem/FileMagic.java @@ -78,7 +78,7 @@ public enum FileMagic { /** PDF document */ PDF("%PDF"), /** Some different HTML documents */ - HTML(" diff --git a/src/java/org/apache/poi/sl/draw/DrawBackground.java b/src/java/org/apache/poi/sl/draw/DrawBackground.java index 1e9d5945b3..c61f6dbb79 100644 --- a/src/java/org/apache/poi/sl/draw/DrawBackground.java +++ b/src/java/org/apache/poi/sl/draw/DrawBackground.java @@ -17,6 +17,8 @@ package org.apache.poi.sl.draw; +import static org.apache.poi.sl.draw.DrawPaint.fillPaintWorkaround; + import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.Paint; @@ -59,10 +61,10 @@ public class DrawBackground extends DrawShape { if(fill != null) { graphics.setRenderingHint(Drawable.GRADIENT_SHAPE, anchor); graphics.setPaint(fill); - graphics.fill(anchor2); + fillPaintWorkaround(graphics, anchor2); } } - + protected Background getShape() { return (Background)shape; } diff --git a/src/java/org/apache/poi/sl/draw/DrawFreeformShape.java b/src/java/org/apache/poi/sl/draw/DrawFreeformShape.java index 6eb60dfb52..be18f037ab 100644 --- a/src/java/org/apache/poi/sl/draw/DrawFreeformShape.java +++ b/src/java/org/apache/poi/sl/draw/DrawFreeformShape.java @@ -17,43 +17,11 @@ package org.apache.poi.sl.draw; -import java.awt.Graphics2D; -import java.awt.geom.AffineTransform; -import java.awt.geom.Path2D; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import org.apache.poi.sl.draw.geom.Outline; -import org.apache.poi.sl.draw.geom.Path; -import org.apache.poi.sl.usermodel.*; +import org.apache.poi.sl.usermodel.FreeformShape; +@SuppressWarnings("WeakerAccess") public class DrawFreeformShape extends DrawAutoShape { public DrawFreeformShape(FreeformShape shape) { super(shape); } - - protected Collection computeOutlines(Graphics2D graphics) { - List lst = new ArrayList<>(); - FreeformShape fsh = (FreeformShape) getShape(); - Path2D sh = fsh.getPath(); - - AffineTransform tx = (AffineTransform)graphics.getRenderingHint(Drawable.GROUP_TRANSFORM); - if (tx == null) { - tx = new AffineTransform(); - } - - java.awt.Shape canvasShape = tx.createTransformedShape(sh); - - FillStyle fs = fsh.getFillStyle(); - StrokeStyle ss = fsh.getStrokeStyle(); - Path path = new Path(fs != null, ss != null); - lst.add(new Outline(canvasShape, path)); - return lst; - } - - @Override - protected TextShape> getShape() { - return (TextShape>)shape; - } } diff --git a/src/java/org/apache/poi/sl/draw/DrawPaint.java b/src/java/org/apache/poi/sl/draw/DrawPaint.java index d6986f3023..80c5bcb00b 100644 --- a/src/java/org/apache/poi/sl/draw/DrawPaint.java +++ b/src/java/org/apache/poi/sl/draw/DrawPaint.java @@ -23,13 +23,17 @@ import java.awt.Graphics2D; import java.awt.LinearGradientPaint; import java.awt.Paint; import java.awt.RadialGradientPaint; +import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.InputStream; +import java.util.Map; import java.util.Objects; +import java.util.TreeMap; +import java.util.function.BiFunction; import org.apache.poi.sl.usermodel.AbstractColorStyle; import org.apache.poi.sl.usermodel.ColorStyle; @@ -197,28 +201,17 @@ public class DrawPaint { @Override public int getShade() { - int shade = orig.getShade(); - switch (modifier) { - case DARKEN: - return Math.min(100000, Math.max(0,shade)+40000); - case DARKEN_LESS: - return Math.min(100000, Math.max(0,shade)+20000); - default: - return shade; - } + return scale(orig.getShade(), PaintModifier.DARKEN_LESS, PaintModifier.DARKEN); } @Override public int getTint() { - int tint = orig.getTint(); - switch (modifier) { - case LIGHTEN: - return Math.min(100000, Math.max(0,tint)+40000); - case LIGHTEN_LESS: - return Math.min(100000, Math.max(0,tint)+20000); - default: - return tint; - } + return scale(orig.getTint(), PaintModifier.LIGHTEN_LESS, PaintModifier.LIGHTEN); + } + + private int scale(int value, PaintModifier lessModifier, PaintModifier moreModifier) { + int delta = (modifier == lessModifier ? 20000 : (modifier == moreModifier ? 40000 : 0)); + return Math.min(100000, Math.max(0,value)+delta); } }; @@ -300,7 +293,7 @@ public class DrawPaint { Color result = color.getColor(); double alpha = getAlpha(result, color); - double hsl[] = RGB2HSL(result); // values are in the range [0..100] (usually ...) + double[] hsl = RGB2HSL(result); // values are in the range [0..100] (usually ...) applyHslModOff(hsl, 0, color.getHueMod(), color.getHueOff()); applyHslModOff(hsl, 1, color.getSatMod(), color.getSatOff()); applyHslModOff(hsl, 2, color.getLumMod(), color.getLumOff()); @@ -344,7 +337,7 @@ public class DrawPaint { * @param mod the modulation adjustment * @param off the offset adjustment */ - private static void applyHslModOff(double hsl[], int hslPart, int mod, int off) { + private static void applyHslModOff(double[] hsl, int hslPart, int mod, int off) { if (mod == -1) { mod = 100000; } @@ -363,7 +356,7 @@ public class DrawPaint { * * For a shade, the equation is luminance * %tint. */ - private static void applyShade(double hsl[], ColorStyle fc) { + private static void applyShade(double[] hsl, ColorStyle fc) { int shade = fc.getShade(); if (shade == -1) { return; @@ -380,7 +373,7 @@ public class DrawPaint { * For a tint, the equation is luminance * %tint + (1-%tint). * (Note that 1-%tint is equal to the lumOff value in DrawingML.) */ - private static void applyTint(double hsl[], ColorStyle fc) { + private static void applyTint(double[] hsl, ColorStyle fc) { int tint = fc.getTint(); if (tint == -1) { return; @@ -403,70 +396,63 @@ public class DrawPaint { } Rectangle2D anchor = DrawShape.getAnchor(graphics, shape); - final double h = anchor.getHeight(), w = anchor.getWidth(), x = anchor.getX(), y = anchor.getY(); AffineTransform at = AffineTransform.getRotateInstance(Math.toRadians(angle), anchor.getCenterX(), anchor.getCenterY()); - double diagonal = Math.sqrt(h * h + w * w); - Point2D p1 = new Point2D.Double(x + w / 2 - diagonal / 2, y + h / 2); - p1 = at.transform(p1, null); - - Point2D p2 = new Point2D.Double(x + w, y + h / 2); - p2 = at.transform(p2, null); + double diagonal = Math.sqrt(Math.pow(anchor.getWidth(),2) + Math.pow(anchor.getHeight(),2)); + final Point2D p1 = at.transform(new Point2D.Double(anchor.getCenterX() - diagonal / 2, anchor.getCenterY()), null); + final Point2D p2 = at.transform(new Point2D.Double(anchor.getMaxX(), anchor.getCenterY()), null); // snapToAnchor(p1, anchor); // snapToAnchor(p2, anchor); - if (p1.equals(p2)) { - // gradient paint on the same point throws an exception ... and doesn't make sense - return null; - } - - float[] fractions = fill.getGradientFractions(); - Color[] colors = new Color[fractions.length]; - - int i = 0; - for (ColorStyle fc : fill.getGradientColors()) { - // if fc is null, use transparent color to get color of background - colors[i++] = (fc == null) ? TRANSPARENT : applyColorTransform(fc); - } - - return new LinearGradientPaint(p1, p2, fractions, colors); + // gradient paint on the same point throws an exception ... and doesn't make sense + return (p1.equals(p2)) ? null : safeFractions((f,c)->new LinearGradientPaint(p1,p2,f,c), fill); } + @SuppressWarnings("WeakerAccess") protected Paint createRadialGradientPaint(GradientPaint fill, Graphics2D graphics) { Rectangle2D anchor = DrawShape.getAnchor(graphics, shape); - Point2D pCenter = new Point2D.Double(anchor.getX() + anchor.getWidth()/2, - anchor.getY() + anchor.getHeight()/2); + final Point2D pCenter = new Point2D.Double(anchor.getCenterX(), anchor.getCenterY()); - float radius = (float)Math.max(anchor.getWidth(), anchor.getHeight()); + final float radius = (float)Math.max(anchor.getWidth(), anchor.getHeight()); - float[] fractions = fill.getGradientFractions(); - Color[] colors = new Color[fractions.length]; - - int i=0; - for (ColorStyle fc : fill.getGradientColors()) { - colors[i++] = applyColorTransform(fc); - } - - return new RadialGradientPaint(pCenter, radius, fractions, colors); + return safeFractions((f,c)->new RadialGradientPaint(pCenter,radius,f,c), fill); } @SuppressWarnings({"WeakerAccess", "unused"}) protected Paint createPathGradientPaint(GradientPaint fill, Graphics2D graphics) { // currently we ignore an eventually center setting - float[] fractions = fill.getGradientFractions(); - Color[] colors = new Color[fractions.length]; + return safeFractions(PathGradientPaint::new, fill); + } - int i=0; - for (ColorStyle fc : fill.getGradientColors()) { - colors[i++] = applyColorTransform(fc); + private Paint safeFractions(BiFunction init, GradientPaint fill) { + float[] fractions = fill.getGradientFractions(); + final ColorStyle[] styles = fill.getGradientColors(); + + // need to remap the fractions, because Java doesn't like repeating fraction values + Map m = new TreeMap<>(); + for (int i = 0; i me : m.entrySet()) { + fractions[i] = me.getKey(); + colors[i] = me.getValue(); + i++; + } + + return init.apply(fractions, colors); } /** @@ -620,4 +606,19 @@ public class DrawPaint { return (float)(1.055d * Math.pow(linRGB / 100000d, 1.0d/2.4d) - 0.055d); } } + + + static void fillPaintWorkaround(Graphics2D graphics, Shape shape) { + // the ibm jdk has a rendering/JIT bug, which throws an AIOOBE in + // TexturePaintContext$Int.setRaster(TexturePaintContext.java:476) + // this usually doesn't happen while debugging, because JIT doesn't jump in then. + try { + graphics.fill(shape); + } catch (ArrayIndexOutOfBoundsException e) { + LOG.log(POILogger.WARN, "IBM JDK failed with TexturePaintContext AIOOBE - try adding the following to the VM parameter:\n" + + "-Xjit:exclude={sun/java2d/pipe/AlphaPaintPipe.renderPathTile(Ljava/lang/Object;[BIIIIII)V} and " + + "search for 'JIT Problem Determination for IBM SDK using -Xjit' (http://www-01.ibm.com/support/docview.wss?uid=swg21294023) " + + "for how to add/determine further excludes", e); + } + } } diff --git a/src/java/org/apache/poi/sl/draw/DrawSimpleShape.java b/src/java/org/apache/poi/sl/draw/DrawSimpleShape.java index 7ed1d35da2..a8d6fb534e 100644 --- a/src/java/org/apache/poi/sl/draw/DrawSimpleShape.java +++ b/src/java/org/apache/poi/sl/draw/DrawSimpleShape.java @@ -17,6 +17,8 @@ package org.apache.poi.sl.draw; +import static org.apache.poi.sl.draw.DrawPaint.fillPaintWorkaround; + import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; @@ -87,7 +89,7 @@ public class DrawSimpleShape extends DrawShape { graphics.setPaint(fillMod); java.awt.Shape s = o.getOutline(); graphics.setRenderingHint(Drawable.GRADIENT_SHAPE, s); - graphics.fill(s); + fillPaintWorkaround(graphics, s); } } } @@ -327,7 +329,7 @@ public class DrawSimpleShape extends DrawShape { graphics.setPaint(shadowColor); if(fill != null && p.isFilled()){ - graphics.fill(s); + fillPaintWorkaround(graphics, s); } else if (line != null && p.isStroked()) { graphics.draw(s); } @@ -410,14 +412,20 @@ public class DrawSimpleShape extends DrawShape { } for (Path p : geom) { - double w = p.getW(), h = p.getH(), scaleX = Units.toPoints(1), scaleY = scaleX; + double w = p.getW(), h = p.getH(), scaleX, scaleY; if (w == -1) { w = Units.toEMU(anchor.getWidth()); + scaleX = Units.toPoints(1); + } else if (anchor.getWidth() == 0) { + scaleX = 1; } else { scaleX = anchor.getWidth() / w; } if (h == -1) { h = Units.toEMU(anchor.getHeight()); + scaleY = Units.toPoints(1); + } else if (anchor.getHeight() == 0) { + scaleY = 1; } else { scaleY = anchor.getHeight() / h; } diff --git a/src/java/org/apache/poi/sl/draw/DrawTableShape.java b/src/java/org/apache/poi/sl/draw/DrawTableShape.java index 905196fc90..703fb04d82 100644 --- a/src/java/org/apache/poi/sl/draw/DrawTableShape.java +++ b/src/java/org/apache/poi/sl/draw/DrawTableShape.java @@ -17,6 +17,8 @@ package org.apache.poi.sl.draw; +import static org.apache.poi.sl.draw.DrawPaint.fillPaintWorkaround; + import java.awt.Color; import java.awt.Graphics2D; import java.awt.Paint; @@ -83,8 +85,8 @@ public class DrawTableShape extends DrawShape { Paint fillPaint = drawPaint.getPaint(graphics, tc.getFillStyle().getPaint()); graphics.setPaint(fillPaint); Rectangle2D cellAnc = tc.getAnchor(); - graphics.fill(cellAnc); - + fillPaintWorkaround(graphics, cellAnc); + for (BorderEdge edge : BorderEdge.values()) { StrokeStyle stroke = tc.getBorderStyle(edge); if (stroke == null) { diff --git a/src/java/org/apache/poi/sl/draw/PathGradientPaint.java b/src/java/org/apache/poi/sl/draw/PathGradientPaint.java index d4a2a5fa5f..2281bbff83 100644 --- a/src/java/org/apache/poi/sl/draw/PathGradientPaint.java +++ b/src/java/org/apache/poi/sl/draw/PathGradientPaint.java @@ -23,21 +23,24 @@ import java.awt.MultipleGradientPaint.CycleMethod; import java.awt.geom.*; import java.awt.image.*; +import org.apache.poi.util.Internal; + +@Internal class PathGradientPaint implements Paint { // http://asserttrue.blogspot.de/2010/01/how-to-iimplement-custom-paint-in-50.html - protected final Color colors[]; - protected final float fractions[]; - protected final int capStyle; - protected final int joinStyle; - protected final int transparency; + private final Color[] colors; + private final float[] fractions; + private final int capStyle; + private final int joinStyle; + private final int transparency; - public PathGradientPaint(Color colors[], float fractions[]) { - this(colors,fractions,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND); + PathGradientPaint(float[] fractions, Color[] colors) { + this(fractions,colors,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND); } - public PathGradientPaint(Color colors[], float fractions[], int capStyle, int joinStyle) { + private PathGradientPaint(float[] fractions, Color[] colors, int capStyle, int joinStyle) { this.colors = colors.clone(); this.fractions = fractions.clone(); this.capStyle = capStyle; @@ -66,26 +69,26 @@ class PathGradientPaint implements Paint { } class PathGradientContext implements PaintContext { - protected final Rectangle deviceBounds; - protected final Rectangle2D userBounds; + final Rectangle deviceBounds; + final Rectangle2D userBounds; protected final AffineTransform xform; - protected final RenderingHints hints; + final RenderingHints hints; /** * for POI: the shape will be only known when the subclasses determines the concrete implementation * in the draw/-content method, so we need to postpone the setting/creation as long as possible **/ protected final Shape shape; - protected final PaintContext pCtx; - protected final int gradientSteps; + final PaintContext pCtx; + final int gradientSteps; WritableRaster raster; - public PathGradientContext( - ColorModel cm - , Rectangle deviceBounds - , Rectangle2D userBounds - , AffineTransform xform - , RenderingHints hints + PathGradientContext( + ColorModel cm + , Rectangle deviceBounds + , Rectangle2D userBounds + , AffineTransform xform + , RenderingHints hints ) { shape = (Shape)hints.get(Drawable.GRADIENT_SHAPE); if (shape == null) { @@ -139,7 +142,7 @@ class PathGradientPaint implements Paint { return childRaster; } - protected int getGradientSteps(Shape gradientShape) { + int getGradientSteps(Shape gradientShape) { Rectangle rect = gradientShape.getBounds(); int lower = 1; int upper = (int)(Math.max(rect.getWidth(),rect.getHeight())/2.0); @@ -158,7 +161,7 @@ class PathGradientPaint implements Paint { - protected void createRaster() { + void createRaster() { ColorModel cm = getColorModel(); raster = cm.createCompatibleWritableRaster((int)deviceBounds.getWidth(), (int)deviceBounds.getHeight()); BufferedImage img = new BufferedImage(cm, raster, false, null); @@ -168,7 +171,7 @@ class PathGradientPaint implements Paint { graphics.transform(xform); Raster img2 = pCtx.getRaster(0, 0, gradientSteps, 1); - int rgb[] = new int[cm.getNumComponents()]; + int[] rgb = new int[cm.getNumComponents()]; for (int i = gradientSteps-1; i>=0; i--) { img2.getPixel(i, 0, rgb); diff --git a/src/java/org/apache/poi/sl/draw/geom/Context.java b/src/java/org/apache/poi/sl/draw/geom/Context.java index 31847ceeff..3ed84b3d18 100644 --- a/src/java/org/apache/poi/sl/draw/geom/Context.java +++ b/src/java/org/apache/poi/sl/draw/geom/Context.java @@ -22,11 +22,18 @@ package org.apache.poi.sl.draw.geom; import java.awt.geom.Rectangle2D; import java.util.HashMap; import java.util.Map; +import java.util.regex.Pattern; public class Context { - final Map _ctx = new HashMap<>(); - final IAdjustableShape _props; - final Rectangle2D _anchor; + private static final Pattern DOUBLE_PATTERN = Pattern.compile( + "[\\x00-\\x20]*[+-]?(NaN|Infinity|((((\\p{Digit}+)(\\.)?((\\p{Digit}+)?)" + + "([eE][+-]?(\\p{Digit}+))?)|(\\.(\\p{Digit}+)([eE][+-]?(\\p{Digit}+))?)|" + + "(((0[xX](\\p{XDigit}+)(\\.)?)|(0[xX](\\p{XDigit}+)?(\\.)(\\p{XDigit}+)))" + + "[pP][+-]?(\\p{Digit}+)))[fFdD]?))[\\x00-\\x20]*"); + + private final Map _ctx = new HashMap<>(); + private final IAdjustableShape _props; + private final Rectangle2D _anchor; public Context(CustomGeometry geom, Rectangle2D anchor, IAdjustableShape props){ _props = props; @@ -39,23 +46,22 @@ public class Context { } } - public Rectangle2D getShapeAnchor(){ + Rectangle2D getShapeAnchor(){ return _anchor; } - public Guide getAdjustValue(String name){ + Guide getAdjustValue(String name){ // ignore HSLF props for now ... the results with default value are usually better - see #59004 return (_props.getClass().getName().contains("hslf")) ? null : _props.getAdjustValue(name); } public double getValue(String key){ - if(key.matches("(\\+|-)?\\d+")){ + if(DOUBLE_PATTERN.matcher(key).matches()){ return Double.parseDouble(key); } - Double val = _ctx.get(key); // BuiltInGuide throws IllegalArgumentException if key is not defined - return (val != null) ? val : evaluate(BuiltInGuide.valueOf("_"+key)); + return _ctx.containsKey(key) ? _ctx.get(key) : evaluate(BuiltInGuide.valueOf("_"+key)); } public double evaluate(Formula fmla){ diff --git a/src/java/org/apache/poi/sl/draw/geom/PresetGeometries.java b/src/java/org/apache/poi/sl/draw/geom/PresetGeometries.java index a188e6e255..f8b461f929 100644 --- a/src/java/org/apache/poi/sl/draw/geom/PresetGeometries.java +++ b/src/java/org/apache/poi/sl/draw/geom/PresetGeometries.java @@ -27,12 +27,12 @@ import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.bind.Unmarshaller; import javax.xml.stream.EventFilter; -import javax.xml.stream.XMLEventReader; import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamConstants; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; -import javax.xml.stream.events.StartElement; import javax.xml.stream.events.XMLEvent; +import javax.xml.transform.stream.StreamSource; import org.apache.poi.sl.draw.binding.CTCustomGeometry2D; import org.apache.poi.util.POILogFactory; @@ -61,27 +61,30 @@ public class PresetGeometries extends LinkedHashMap { }; XMLInputFactory staxFactory = StaxHelper.newXMLInputFactory(); - XMLEventReader staxReader = staxFactory.createXMLEventReader(is); - XMLEventReader staxFiltRd = staxFactory.createFilteredReader(staxReader, startElementFilter); - // ignore StartElement: - /* XMLEvent evDoc = */ staxFiltRd.nextEvent(); - // JAXB: - JAXBContext jaxbContext = JAXBContext.newInstance(BINDING_PACKAGE); - Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); + XMLStreamReader streamReader = staxFactory.createXMLStreamReader(new StreamSource(is)); + try { + // ignore StartElement: + streamReader.nextTag(); - long cntElem = 0; - while (staxFiltRd.peek() != null) { - StartElement evRoot = (StartElement)staxFiltRd.peek(); - String name = evRoot.getName().getLocalPart(); - JAXBElement el = unmarshaller.unmarshal(staxReader, CTCustomGeometry2D.class); - CTCustomGeometry2D cus = el.getValue(); - cntElem++; - - if(containsKey(name)) { - LOG.log(POILogger.WARN, "Duplicate definition of " + name); + // JAXB: + JAXBContext jaxbContext = JAXBContext.newInstance(BINDING_PACKAGE); + Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); + + long cntElem = 0; + while (streamReader.hasNext() && streamReader.nextTag() == XMLStreamConstants.START_ELEMENT) { + String name = streamReader.getLocalName(); + JAXBElement el = unmarshaller.unmarshal(streamReader, CTCustomGeometry2D.class); + CTCustomGeometry2D cus = el.getValue(); + cntElem++; + + if (containsKey(name)) { + LOG.log(POILogger.WARN, "Duplicate definition of " + name); + } + put(name, new CustomGeometry(cus)); } - put(name, new CustomGeometry(cus)); - } + } finally { + streamReader.close(); + } } /** diff --git a/src/java/org/apache/poi/sl/usermodel/FreeformShape.java b/src/java/org/apache/poi/sl/usermodel/FreeformShape.java index 2a1580a7da..42ae10ae9a 100644 --- a/src/java/org/apache/poi/sl/usermodel/FreeformShape.java +++ b/src/java/org/apache/poi/sl/usermodel/FreeformShape.java @@ -24,16 +24,15 @@ public interface FreeformShape< P extends TextParagraph > extends AutoShape { /** - * Gets the shape path. - *

    - * The path is translated in the shape's coordinate system, i.e. - * freeform.getPath().getBounds2D() equals to freeform.getAnchor() - * (small discrepancies are possible due to rounding errors) - *

    + * Gets the shape path.

    + * + * The path is translated in the shape's coordinate system, i.e. + * freeform.getPath2D().getBounds2D() equals to freeform.getAnchor() + * (small discrepancies are possible due to rounding errors) * * @return the path */ - Path2D.Double getPath(); + Path2D getPath(); /** * Set the shape path @@ -41,5 +40,5 @@ public interface FreeformShape< * @param path shape outline * @return the number of points written */ - int setPath(Path2D.Double path); + int setPath(Path2D path); } diff --git a/src/java/org/apache/poi/ss/formula/SheetNameFormatter.java b/src/java/org/apache/poi/ss/formula/SheetNameFormatter.java index c6e13c3110..6e10e67c58 100644 --- a/src/java/org/apache/poi/ss/formula/SheetNameFormatter.java +++ b/src/java/org/apache/poi/ss/formula/SheetNameFormatter.java @@ -59,6 +59,7 @@ public final class SheetNameFormatter { * @param rawSheetName - sheet name * @deprecated use appendFormat(StringBuilder out, String rawSheetName) instead */ + @Deprecated public static void appendFormat(StringBuffer out, String rawSheetName) { boolean needsQuotes = needsDelimiting(rawSheetName); if(needsQuotes) { @@ -73,6 +74,7 @@ public final class SheetNameFormatter { /** * @deprecated use appendFormat(StringBuilder out, String workbookName, String rawSheetName) instead */ + @Deprecated public static void appendFormat(StringBuffer out, String workbookName, String rawSheetName) { boolean needsQuotes = needsDelimiting(workbookName) || needsDelimiting(rawSheetName); if(needsQuotes) { @@ -123,7 +125,7 @@ public final class SheetNameFormatter { } } - private static void appendAndEscape(Appendable sb, String rawSheetName) { + static void appendAndEscape(Appendable sb, String rawSheetName) { int len = rawSheetName.length(); for(int i=0; i= 0) { + sb.append('['); + sb.append(workbookIndex); + sb.append(']'); + } + + SheetNameFormatter.appendAndEscape(sb, firstSheetName); + + if (lastSheetName != null) { + sb.append(':'); + SheetNameFormatter.appendAndEscape(sb, lastSheetName); + } + + sb.append('\''); + return sb.toString(); + } + + private static String formatWithoutDelimiting(StringBuilder sb, int workbookIndex, String firstSheetName, String lastSheetName) { + if (workbookIndex >= 0) { + sb.append('['); + sb.append(workbookIndex); + sb.append(']'); + } + + sb.append(firstSheetName); + + if (lastSheetName != null) { + sb.append(':'); + sb.append(lastSheetName); + } + + return sb.toString(); + } + + private static boolean anySheetNameNeedsEscaping(String firstSheetName, String lastSheetName) { + boolean anySheetNameNeedsDelimiting = firstSheetName != null && SheetNameFormatter.needsDelimiting(firstSheetName); + anySheetNameNeedsDelimiting |= lastSheetName != null && SheetNameFormatter.needsDelimiting(lastSheetName); + return anySheetNameNeedsDelimiting; + } +} diff --git a/src/java/org/apache/poi/ss/formula/functions/MatrixFunction.java b/src/java/org/apache/poi/ss/formula/functions/MatrixFunction.java index 6b111cfc1a..04e7f4cd77 100644 --- a/src/java/org/apache/poi/ss/formula/functions/MatrixFunction.java +++ b/src/java/org/apache/poi/ss/formula/functions/MatrixFunction.java @@ -63,7 +63,7 @@ public abstract class MatrixFunction implements Function{ i = 0; j++; } - matrix[j][i++] = aVector; + if (j < matrix.length) matrix[j][i++] = aVector; } } diff --git a/src/java/org/apache/poi/ss/formula/ptg/Area3DPxg.java b/src/java/org/apache/poi/ss/formula/ptg/Area3DPxg.java index 65f59e83e1..41bea0c950 100644 --- a/src/java/org/apache/poi/ss/formula/ptg/Area3DPxg.java +++ b/src/java/org/apache/poi/ss/formula/ptg/Area3DPxg.java @@ -20,6 +20,7 @@ package org.apache.poi.ss.formula.ptg; import org.apache.poi.ss.SpreadsheetVersion; import org.apache.poi.ss.formula.SheetIdentifier; import org.apache.poi.ss.formula.SheetNameFormatter; +import org.apache.poi.ss.formula.SheetRangeAndWorkbookIndexFormatter; import org.apache.poi.ss.formula.SheetRangeIdentifier; import org.apache.poi.ss.util.AreaReference; import org.apache.poi.util.LittleEndianOutput; @@ -102,16 +103,8 @@ public final class Area3DPxg extends AreaPtgBase implements Pxg3D { public String toFormulaString() { StringBuilder sb = new StringBuilder(64); - if (externalWorkbookNumber >= 0) { - sb.append('['); - sb.append(externalWorkbookNumber); - sb.append(']'); - } - SheetNameFormatter.appendFormat(sb, firstSheetName); - if (lastSheetName != null) { - sb.append(':'); - SheetNameFormatter.appendFormat(sb, lastSheetName); - } + + SheetRangeAndWorkbookIndexFormatter.format(sb, externalWorkbookNumber, firstSheetName, lastSheetName); sb.append('!'); sb.append(formatReferenceAsString()); return sb.toString(); diff --git a/src/java/org/apache/poi/ss/formula/ptg/Ref3DPxg.java b/src/java/org/apache/poi/ss/formula/ptg/Ref3DPxg.java index 67f73b360d..12e7e54eda 100644 --- a/src/java/org/apache/poi/ss/formula/ptg/Ref3DPxg.java +++ b/src/java/org/apache/poi/ss/formula/ptg/Ref3DPxg.java @@ -18,7 +18,7 @@ package org.apache.poi.ss.formula.ptg; import org.apache.poi.ss.formula.SheetIdentifier; -import org.apache.poi.ss.formula.SheetNameFormatter; +import org.apache.poi.ss.formula.SheetRangeAndWorkbookIndexFormatter; import org.apache.poi.ss.formula.SheetRangeIdentifier; import org.apache.poi.ss.util.CellReference; import org.apache.poi.util.LittleEndianOutput; @@ -101,18 +101,8 @@ public final class Ref3DPxg extends RefPtgBase implements Pxg3D { public String toFormulaString() { StringBuilder sb = new StringBuilder(64); - if (externalWorkbookNumber >= 0) { - sb.append('['); - sb.append(externalWorkbookNumber); - sb.append(']'); - } - if (firstSheetName != null) { - SheetNameFormatter.appendFormat(sb, firstSheetName); - } - if (lastSheetName != null) { - sb.append(':'); - SheetNameFormatter.appendFormat(sb, lastSheetName); - } + + SheetRangeAndWorkbookIndexFormatter.format(sb, externalWorkbookNumber, firstSheetName, lastSheetName); sb.append('!'); sb.append(formatReferenceAsString()); return sb.toString(); diff --git a/src/java/org/apache/poi/util/RecordFormatException.java b/src/java/org/apache/poi/util/RecordFormatException.java index 2bc4ba3bcb..65bea581ee 100644 --- a/src/java/org/apache/poi/util/RecordFormatException.java +++ b/src/java/org/apache/poi/util/RecordFormatException.java @@ -45,8 +45,8 @@ public class RecordFormatException * be thrown. If assertTrue is false, this will throw this * exception with the message. * - * @param assertTrue - * @param message + * @param assertTrue If false, the exception is thrown, if true, no action is performed + * @param message The message to include in the thrown exception */ public static void check(boolean assertTrue, String message) { if (! assertTrue) { diff --git a/src/java/org/apache/poi/util/StringUtil.java b/src/java/org/apache/poi/util/StringUtil.java index 302e532570..2017abf125 100644 --- a/src/java/org/apache/poi/util/StringUtil.java +++ b/src/java/org/apache/poi/util/StringUtil.java @@ -698,7 +698,7 @@ public class StringUtil { final String prefix; // #61881 - for now we only check the first char - if (len > 0 && string[offset] == 0 && string[offset+1] == 0) { + if (len > 0 && offset < (string.length - 1) && string[offset] == 0 && string[offset+1] == 0) { newOffset = offset+2; prefix = "?"; diff --git a/src/java/org/apache/poi/util/Units.java b/src/java/org/apache/poi/util/Units.java index da0a877cf4..709e3f10cc 100644 --- a/src/java/org/apache/poi/util/Units.java +++ b/src/java/org/apache/poi/util/Units.java @@ -127,7 +127,7 @@ public class Units { points /= MASTER_DPI; return points; } - + public static int pointsToMaster(double points) { points *= MASTER_DPI; points /= POINT_DPI; diff --git a/src/ooxml/java/org/apache/poi/ooxml/POIXMLDocumentPart.java b/src/ooxml/java/org/apache/poi/ooxml/POIXMLDocumentPart.java index 54fa790eca..c8e1a277a4 100644 --- a/src/ooxml/java/org/apache/poi/ooxml/POIXMLDocumentPart.java +++ b/src/ooxml/java/org/apache/poi/ooxml/POIXMLDocumentPart.java @@ -615,7 +615,7 @@ public class POIXMLDocumentPart { protected void read(POIXMLFactory factory, Map context) throws OpenXML4JException { PackagePart pp = getPackagePart(); - if (pp.getContentType().equals(XWPFRelation.TEMPLATE.getContentType())) { + if (pp.getContentType().equals(XWPFRelation.GLOSSARY_DOCUMENT.getContentType())) { logger.log(POILogger.WARN, "POI does not currently support template.main+xml (glossary) parts. " + "Skipping this part for now."); diff --git a/src/ooxml/java/org/apache/poi/ooxml/extractor/CommandLineTextExtractor.java b/src/ooxml/java/org/apache/poi/ooxml/extractor/CommandLineTextExtractor.java index 999abd46ee..c3d429b3c7 100644 --- a/src/ooxml/java/org/apache/poi/ooxml/extractor/CommandLineTextExtractor.java +++ b/src/ooxml/java/org/apache/poi/ooxml/extractor/CommandLineTextExtractor.java @@ -22,41 +22,37 @@ import org.apache.poi.extractor.POITextExtractor; /** * A command line wrapper around {@link ExtractorFactory}, useful - * for when debugging. + * for when debugging. */ public class CommandLineTextExtractor { - public static final String DIVIDER = "======================="; - - public static void main(String[] args) throws Exception { - if(args.length < 1) { - System.err.println("Use:"); - System.err.println(" CommandLineTextExtractor [filename] [filename]"); - System.exit(1); - } + public static final String DIVIDER = "======================="; - for (String arg : args) { - System.out.println(DIVIDER); + public static void main(String[] args) throws Exception { + if (args.length < 1) { + System.err.println("Use:"); + System.err.println(" CommandLineTextExtractor [filename] [filename]"); + System.exit(1); + } - File f = new File(arg); - System.out.println(f); + for (String arg : args) { + System.out.println(DIVIDER); - POITextExtractor extractor = - ExtractorFactory.createExtractor(f); - try { - POITextExtractor metadataExtractor = - extractor.getMetadataTextExtractor(); + File f = new File(arg); + System.out.println(f); - System.out.println(" " + DIVIDER); - String metaData = metadataExtractor.getText(); - System.out.println(metaData); - System.out.println(" " + DIVIDER); - String text = extractor.getText(); - System.out.println(text); - System.out.println(DIVIDER); - System.out.println("Had " + metaData.length() + " characters of metadata and " + text.length() + " characters of text"); - } finally { - extractor.close(); - } - } - } + try (POITextExtractor extractor = ExtractorFactory.createExtractor(f)) { + POITextExtractor metadataExtractor = + extractor.getMetadataTextExtractor(); + + System.out.println(" " + DIVIDER); + String metaData = metadataExtractor.getText(); + System.out.println(metaData); + System.out.println(" " + DIVIDER); + String text = extractor.getText(); + System.out.println(text); + System.out.println(DIVIDER); + System.out.println("Had " + metaData.length() + " characters of metadata and " + text.length() + " characters of text"); + } + } + } } diff --git a/src/ooxml/java/org/apache/poi/ooxml/util/POIXMLConstants.java b/src/ooxml/java/org/apache/poi/ooxml/util/POIXMLConstants.java index ab58e35833..c6d7935fda 100644 --- a/src/ooxml/java/org/apache/poi/ooxml/util/POIXMLConstants.java +++ b/src/ooxml/java/org/apache/poi/ooxml/util/POIXMLConstants.java @@ -20,6 +20,7 @@ package org.apache.poi.ooxml.util; public class POIXMLConstants { public static final String FEATURE_LOAD_DTD_GRAMMAR = "http://apache.org/xml/features/nonvalidating/load-dtd-grammar"; public static final String FEATURE_LOAD_EXTERNAL_DTD = "http://apache.org/xml/features/nonvalidating/load-external-dtd"; + public static final String FEATURE_DISALLOW_DOCTYPE_DECL = "http://apache.org/xml/features/disallow-doctype-decl"; public static final String PROPERTY_ENTITY_EXPANSION_LIMIT = "http://www.oracle.com/xml/jaxp/properties/entityExpansionLimit"; public static final String PROPERTY_SECURITY_MANAGER = "http://apache.org/xml/properties/security-manager"; } diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/ZipHelper.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/ZipHelper.java index 094e89c6d7..0169909c9b 100644 --- a/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/ZipHelper.java +++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/ZipHelper.java @@ -154,8 +154,6 @@ public final class ZipHelper { "The supplied data appears to be a raw XML file. " + "Formats such as Office 2003 XML are not supported"); default: - case OOXML: - case UNKNOWN: // Don't check for a Zip header, as to maintain backwards // compatibility we need to let them seek over junk at the // start before beginning processing. diff --git a/src/ooxml/java/org/apache/poi/openxml4j/util/ZipInputStreamZipEntrySource.java b/src/ooxml/java/org/apache/poi/openxml4j/util/ZipInputStreamZipEntrySource.java index 048b34e251..0a56966d4f 100644 --- a/src/ooxml/java/org/apache/poi/openxml4j/util/ZipInputStreamZipEntrySource.java +++ b/src/ooxml/java/org/apache/poi/openxml4j/util/ZipInputStreamZipEntrySource.java @@ -38,7 +38,7 @@ public class ZipInputStreamZipEntrySource implements ZipEntrySource { /** * Reads all the entries from the ZipInputStream - * into memory, and closes the source stream. + * into memory, and don't close (since POI 4.0.1) the source stream. * We'll then eat lots of memory, but be able to * work with the entries at-will. */ diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureOutputStream.java b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureOutputStream.java index 0a00e29f1a..0ba11fbd34 100644 --- a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureOutputStream.java +++ b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureOutputStream.java @@ -20,6 +20,7 @@ package org.apache.poi.poifs.crypt.dsig; import java.io.IOException; import java.security.GeneralSecurityException; import java.security.PrivateKey; +import java.security.Security; import java.security.Signature; import java.security.SignatureException; @@ -35,7 +36,12 @@ import org.apache.poi.poifs.crypt.HashAlgorithm; @Override public void init() throws GeneralSecurityException { final String provider = isMSCapi(key) ? "SunMSCAPI" : "SunRsaSign"; - signature = Signature.getInstance(algo.ecmaString+"withRSA", provider); + if (Security.getProvider(provider) != null) { + signature = Signature.getInstance(algo.ecmaString + "withRSA", provider); + } else { + signature = Signature.getInstance(algo.ecmaString + "withRSA"); + } + signature.initSign(key); } diff --git a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFChart.java b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFChart.java index 321626d71f..4267898309 100644 --- a/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFChart.java +++ b/src/ooxml/java/org/apache/poi/xddf/usermodel/chart/XDDFChart.java @@ -53,6 +53,7 @@ import org.apache.poi.xddf.usermodel.text.XDDFTextBody; import org.apache.poi.xssf.usermodel.XSSFCell; import org.apache.poi.xssf.usermodel.XSSFRow; import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFTable; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlOptions; @@ -81,6 +82,27 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTTableColumns; @Beta public abstract class XDDFChart extends POIXMLDocumentPart implements TextContainer { + + /** + * default width of chart in emu + */ + public static final int DEFAULT_WIDTH = 500000; + + /** + * default height of chart in emu + */ + public static final int DEFAULT_HEIGHT = 500000; + + /** + * default x-coordinate of chart in emu + */ + public static final int DEFAULT_X = 10; + + /** + * default y-coordinate value of chart in emu + */ + public static final int DEFAULT_Y = 10; + /** * Underlying workbook */ @@ -712,10 +734,29 @@ public abstract class XDDFChart extends POIXMLDocumentPart implements TextContai XSSFRow row = this.getRow(sheet, 0); XSSFCell cell = this.getCell(row, column); cell.setCellValue(title); - this.updateSheetTable(sheet.getTables().get(0).getCTTable(), title, column); + + CTTable ctTable = this.getSheetTable(sheet); + + this.updateSheetTable(ctTable, title, column); return new CellReference(sheet.getSheetName(), 0, column, true, true); } + /** + * this method will check whether sheet have table + * in case table size zero then create new table and add table columns element + * @param sheet + * @return table object + */ + private CTTable getSheetTable(XSSFSheet sheet) { + if(sheet.getTables().size() == 0) + { + XSSFTable newTable = sheet.createTable(null); + newTable.getCTTable().addNewTableColumns(); + sheet.getTables().add(newTable); + } + return sheet.getTables().get(0).getCTTable(); + } + /** * this method update column header of sheet into table * @@ -729,7 +770,8 @@ public abstract class XDDFChart extends POIXMLDocumentPart implements TextContai private void updateSheetTable(CTTable ctTable, String title, int index) { CTTableColumns tableColumnList = ctTable.getTableColumns(); CTTableColumn column = null; - for( int i = 0; tableColumnList.getCount() < index; i++) { + int columnCount = tableColumnList.getTableColumnList().size()-1; + for( int i = columnCount; i < index; i++) { column = tableColumnList.addNewTableColumn(); column.setId(i); } diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java index 687ddc5f5b..b0cbc59dd1 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java @@ -302,14 +302,23 @@ public class XMLSlideShow extends POIXMLDocument * Create a blank chart on the given slide. */ public XSLFChart createChart(XSLFSlide slide) { + XSLFChart chart = createChart(); + slide.addRelation(null, XSLFRelation.CHART, chart); + return chart; + } + + /** + * This method is used to create template for chart XML. + * @return Xslf chart object + * @since POI 4.0.2 + */ + public XSLFChart createChart() { int chartIdx = findNextAvailableFileNameIndex(XSLFRelation.CHART, _charts.size() + 1); XSLFChart chart = (XSLFChart) createRelationship(XSLFRelation.CHART, XSLFFactory.getInstance(), chartIdx, true).getDocumentPart(); - slide.addRelation(null, XSLFRelation.CHART, chart); chart.setChartIndex(chartIdx); _charts.add(chart); return chart; } - /** * Return notes slide for the specified slide or create new if it does not exist yet. */ @@ -416,7 +425,7 @@ public class XMLSlideShow extends POIXMLDocument * Return all the charts in the slideshow */ public List getCharts() { - return _charts; + return Collections.unmodifiableList(_charts); } /** diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFChart.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFChart.java index 8e6612f875..5e8d73cd06 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFChart.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFChart.java @@ -19,16 +19,28 @@ package org.apache.poi.xslf.usermodel; +import java.awt.geom.Rectangle2D; import java.io.IOException; +import javax.xml.namespace.QName; + import org.apache.poi.ooxml.POIXMLFactory; import org.apache.poi.ooxml.POIXMLRelation; import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.openxml4j.opc.PackageRelationshipTypes; import org.apache.poi.util.Beta; import org.apache.poi.xddf.usermodel.chart.XDDFChart; +import org.apache.xmlbeans.XmlCursor; import org.apache.xmlbeans.XmlException; import org.openxmlformats.schemas.drawingml.x2006.chart.CTTitle; +import org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObjectData; +import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps; +import org.openxmlformats.schemas.drawingml.x2006.main.CTPoint2D; +import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D; import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBody; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTransform2D; +import org.openxmlformats.schemas.presentationml.x2006.main.CTGraphicalObjectFrame; +import org.openxmlformats.schemas.presentationml.x2006.main.CTGraphicalObjectFrameNonVisual; /** * Represents a Chart in a .pptx presentation @@ -36,6 +48,8 @@ import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBody; @Beta public final class XSLFChart extends XDDFChart { + private static String CHART_URI = "http://schemas.openxmlformats.org/drawingml/2006/chart"; + /** * Construct a PresentationML chart. */ @@ -90,4 +104,47 @@ public final class XSLFChart extends XDDFChart { }; } } + + /** + * method to add graphic frame for XSLF chart + * + * @param shapeId shape id + * @param rID relation id + * @param anchor size and location of chart + * @return graphic frame object + * @since POI 4.0.2 + */ + static CTGraphicalObjectFrame prototype(int shapeId, String rID, Rectangle2D anchor) { + CTGraphicalObjectFrame frame = CTGraphicalObjectFrame.Factory.newInstance(); + CTGraphicalObjectFrameNonVisual nvGr = frame.addNewNvGraphicFramePr(); + + CTNonVisualDrawingProps cnv = nvGr.addNewCNvPr(); + cnv.setName("Chart " + shapeId); + cnv.setId(shapeId); + nvGr.addNewCNvGraphicFramePr().addNewGraphicFrameLocks().setNoGrp(true); + nvGr.addNewNvPr(); + + CTTransform2D xfrm = frame.addNewXfrm(); + + CTPoint2D off = xfrm.addNewOff(); + off.setX((int)anchor.getX()); + off.setY((int)anchor.getY()); + + CTPositiveSize2D ext = xfrm.addNewExt(); + ext.setCx((int)anchor.getWidth()); + ext.setCy((int)anchor.getHeight()); + + xfrm.setExt(ext); + xfrm.setOff(off); + + CTGraphicalObjectData gr = frame.addNewGraphic().addNewGraphicData(); + XmlCursor grCur = gr.newCursor(); + grCur.toNextToken(); + grCur.beginElement(new QName(CHART_URI, "chart")); + grCur.insertAttributeWithValue("id", PackageRelationshipTypes.CORE_PROPERTIES_ECMA376_NS, rID); + grCur.dispose(); + + gr.setUri(CHART_URI); + return frame; + } } diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFDrawing.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFDrawing.java index f4cd0d8e1c..ba93988d0b 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFDrawing.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFDrawing.java @@ -107,6 +107,19 @@ public class XSLFDrawing { return shape; } + /** + * This method will add chart into slide's graphic frame + * + * @param rID relation id of chart + * @param rect2D Chart Bounding values + * @since POI 4.0.2 + */ + public void addChart(String rID, Rectangle2D rect2D) { + CTGraphicalObjectFrame sp = _spTree.addNewGraphicFrame(); + sp.set(XSLFChart.prototype(_sheet.allocateShapeId(), rID, rect2D)); + } + + public XSLFObjectShape createOleShape(String pictureRel) { CTGraphicalObjectFrame sp = _spTree.addNewGraphicFrame(); sp.set(XSLFObjectShape.prototype(_sheet.allocateShapeId(), pictureRel)); diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFFreeformShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFFreeformShape.java index 54acd2b7ba..8c41fe1b93 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFFreeformShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFFreeformShape.java @@ -24,6 +24,12 @@ import java.awt.geom.Path2D; import java.awt.geom.PathIterator; import java.awt.geom.Rectangle2D; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; + +import org.apache.poi.ooxml.POIXMLTypeLoader; +import org.apache.poi.sl.draw.geom.CustomGeometry; +import org.apache.poi.sl.draw.geom.PresetGeometries; import org.apache.poi.sl.usermodel.FreeformShape; import org.apache.poi.util.Beta; import org.apache.poi.util.POILogFactory; @@ -31,6 +37,7 @@ import org.apache.poi.util.POILogger; import org.apache.poi.util.Units; import org.apache.xmlbeans.XmlCursor; import org.apache.xmlbeans.XmlObject; +import org.apache.xmlbeans.XmlOptions; import org.openxmlformats.schemas.drawingml.x2006.main.CTAdjPoint2D; import org.openxmlformats.schemas.drawingml.x2006.main.CTCustomGeometry2D; import org.openxmlformats.schemas.drawingml.x2006.main.CTGeomRect; @@ -61,7 +68,7 @@ public class XSLFFreeformShape extends XSLFAutoShape } @Override - public int setPath(final Path2D.Double path) { + public int setPath(final Path2D path) { final CTPath2D ctPath = CTPath2D.Factory.newInstance(); final Rectangle2D bounds = path.getBounds2D(); @@ -117,6 +124,30 @@ public class XSLFFreeformShape extends XSLFAutoShape return numPoints; } + /** + * @return definition of the shape geometry + */ + @Override + public CustomGeometry getGeometry() { + final XmlObject xo = getShapeProperties(); + if (!(xo instanceof CTShapeProperties)) { + return null; + } + + XmlOptions xop = new XmlOptions(POIXMLTypeLoader.DEFAULT_XML_OPTIONS); + xop.setSaveOuter(); + + XMLStreamReader staxReader = ((CTShapeProperties)xo).getCustGeom().newXMLStreamReader(xop); + CustomGeometry custGeo = PresetGeometries.convertCustomGeometry(staxReader); + try { + staxReader.close(); + } catch (XMLStreamException e) { + LOG.log(POILogger.WARN, + "An error occurred while closing a Custom Geometry XML Stream Reader: " + e.getMessage()); + } + + return custGeo; + } @Override public Path2D.Double getPath() { diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSheet.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSheet.java index b67264ba0f..2bcf063990 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSheet.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSheet.java @@ -20,6 +20,7 @@ import static org.apache.poi.ooxml.POIXMLTypeLoader.DEFAULT_XML_OPTIONS; import java.awt.Dimension; import java.awt.Graphics2D; +import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -52,6 +53,7 @@ import org.apache.poi.util.IOUtils; import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; import org.apache.poi.util.Units; +import org.apache.poi.xddf.usermodel.chart.XDDFChart; import org.apache.xmlbeans.XmlCursor; import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlObject; @@ -306,14 +308,13 @@ implements XSLFShapeContainer, Sheet { throw new IllegalArgumentException("pictureData needs to be of type XSLFPictureData"); } RelationPart rp = addRelation(null, XSLFRelation.IMAGES, (XSLFPictureData)pictureData); - + XSLFObjectShape sh = getDrawing().createOleShape(rp.getRelationship().getId()); CTOleObject oleObj = sh.getCTOleObject(); Dimension dim = pictureData.getImageDimension(); oleObj.setImgW(Units.toEMU(dim.getWidth())); oleObj.setImgH(Units.toEMU(dim.getHeight())); - - + getShapes().add(sh); sh.setParent(this); return sh; @@ -719,4 +720,28 @@ implements XSLFShapeContainer, Sheet { return (ph == null) ? null : new XSLFPlaceholderDetails(ph); } + /** + * this method will add chart into slide + * with default height, width, x and y + * @param chart xslf chart object + * @since POI 4.0.2 + */ + public void addChart(XSLFChart chart) { + Rectangle2D rect2D = new java.awt.Rectangle(XDDFChart.DEFAULT_X, XDDFChart.DEFAULT_Y, + XDDFChart.DEFAULT_WIDTH, XDDFChart.DEFAULT_HEIGHT); + + this.addChart(chart, rect2D); + } + + /** + * this method will add chart into slide + * with given height, width, x and y + * @param chart xslf chart object + * @since POI 4.0.2 + */ + public void addChart(XSLFChart chart, Rectangle2D rect2D) { + RelationPart rp = addRelation(null, XSLFRelation.CHART, chart); + getDrawing().addChart(rp.getRelationship().getId(), rect2D); + } + } diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java index 4e54712a77..89f312327b 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java @@ -716,7 +716,6 @@ public abstract class XSLFSimpleShape extends XSLFShape } /** - * * @return definition of the shape geometry */ @Override diff --git a/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFWorkbook.java b/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFWorkbook.java index ed52e0e16b..16d7903a37 100644 --- a/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFWorkbook.java +++ b/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFWorkbook.java @@ -32,6 +32,7 @@ import java.util.List; import java.util.Map; import java.util.NoSuchElementException; +import org.apache.commons.compress.archivers.zip.Zip64Mode; import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; import org.apache.poi.openxml4j.opc.OPCPackage; @@ -51,13 +52,7 @@ import org.apache.poi.ss.usermodel.Row.MissingCellPolicy; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.SheetVisibility; import org.apache.poi.ss.usermodel.Workbook; -import org.apache.poi.util.IOUtils; -import org.apache.poi.util.Internal; -import org.apache.poi.util.NotImplemented; -import org.apache.poi.util.POILogFactory; -import org.apache.poi.util.POILogger; -import org.apache.poi.util.Removal; -import org.apache.poi.util.TempFile; +import org.apache.poi.util.*; import org.apache.poi.xssf.model.SharedStringsTable; import org.apache.poi.xssf.usermodel.XSSFChartSheet; import org.apache.poi.xssf.usermodel.XSSFSheet; @@ -117,6 +112,8 @@ public class SXSSFWorkbook implements Workbook { */ private final SharedStringsTable _sharedStringSource; + private Zip64Mode zip64Mode = Zip64Mode.AsNeeded; + /** * Construct a new workbook with default row window size */ @@ -250,6 +247,7 @@ public class SXSSFWorkbook implements Workbook { } } } + /** * Construct an empty workbook and specify the window for row access. *

    @@ -290,6 +288,16 @@ public class SXSSFWorkbook implements Workbook { _randomAccessWindowSize = rowAccessWindowSize; } + /** + * @param zip64Mode {@link Zip64Mode} + * + * @since 4.0.3 + */ + @Beta + public void setZip64Mode(Zip64Mode zip64Mode) { + this.zip64Mode = zip64Mode; + } + /** * Get whether temp files should be compressed. * @@ -298,6 +306,7 @@ public class SXSSFWorkbook implements Workbook { public boolean isCompressTempFiles() { return _compressTmpFiles; } + /** * Set whether temp files should be compressed. *

    @@ -377,6 +386,7 @@ public class SXSSFWorkbook implements Workbook { protected void injectData(ZipEntrySource zipEntrySource, OutputStream out) throws IOException { ZipArchiveOutputStream zos = new ZipArchiveOutputStream(out); + zos.setUseZip64(zip64Mode); try { Enumeration en = zipEntrySource.getEntries(); while (en.hasMoreElements()) { diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFChart.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFChart.java index 44820a5871..495cde1bf9 100644 --- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFChart.java +++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFChart.java @@ -39,12 +39,12 @@ public class XWPFChart extends XDDFChart { /** * default width of chart in emu */ - public static final int DEFAULT_WIDTH = 500000; + public static final int DEFAULT_WIDTH = XDDFChart.DEFAULT_WIDTH; /** * default height of chart in emu */ - public static final int DEFAULT_HEIGHT = 500000; + public static final int DEFAULT_HEIGHT = XDDFChart.DEFAULT_HEIGHT; // lazy initialization private Long checksum; diff --git a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java index e3cc314a4a..8e7e383d45 100644 --- a/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java +++ b/src/ooxml/java/org/apache/poi/xwpf/usermodel/XWPFDocument.java @@ -58,6 +58,7 @@ import org.apache.poi.util.Internal; import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; import org.apache.poi.wp.usermodel.HeaderFooterType; +import org.apache.poi.xddf.usermodel.chart.XDDFChart; import org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy; import org.apache.xmlbeans.XmlCursor; import org.apache.xmlbeans.XmlException; @@ -1672,7 +1673,7 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { * @since POI 4.0.0 */ public XWPFChart createChart() throws InvalidFormatException, IOException { - return createChart(XWPFChart.DEFAULT_WIDTH, XWPFChart.DEFAULT_HEIGHT); + return createChart(XDDFChart.DEFAULT_WIDTH, XDDFChart.DEFAULT_HEIGHT); } /** diff --git a/src/ooxml/testcases/org/apache/poi/ooxml/TestPOIXMLProperties.java b/src/ooxml/testcases/org/apache/poi/ooxml/TestPOIXMLProperties.java index d91b8191eb..1ad2a633f9 100644 --- a/src/ooxml/testcases/org/apache/poi/ooxml/TestPOIXMLProperties.java +++ b/src/ooxml/testcases/org/apache/poi/ooxml/TestPOIXMLProperties.java @@ -19,6 +19,7 @@ package org.apache.poi.ooxml; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -89,7 +90,7 @@ public final class TestPOIXMLProperties { XSSFWorkbook newWorkbook = XSSFTestDataSamples.writeOutAndReadBack(workbook); workbook.close(); - assertTrue(workbook != newWorkbook); + assertNotSame(workbook, newWorkbook); POIXMLProperties newProps = newWorkbook.getProperties(); @@ -158,7 +159,7 @@ public final class TestPOIXMLProperties { p = ctProps.getPropertyArray(3); assertEquals("{D5CDD505-2E9C-101B-9397-08002B2CF9AE}", p.getFmtid()); assertEquals("test-4", p.getName()); - assertEquals(true, p.getBool()); + assertTrue(p.getBool()); assertEquals(5, p.getPid()); wb2.close(); diff --git a/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java b/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java index dd2bc1705f..4b9cff9b3c 100644 --- a/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java +++ b/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java @@ -60,6 +60,7 @@ import javax.xml.crypto.dsig.CanonicalizationMethod; import javax.xml.crypto.dsig.dom.DOMSignContext; import org.apache.jcp.xml.dsig.internal.dom.DOMSignedInfo; +import org.apache.poi.EncryptedDocumentException; import org.apache.poi.POIDataSamples; import org.apache.poi.POITestCase; import org.apache.poi.ooxml.util.DocumentHelper; @@ -682,6 +683,8 @@ public class TestSignatureInfo { si.confirmSignature(); boolean b = si.verifySignature(); assertTrue("Signature not correctly calculated for " + ha, b); + } catch (EncryptedDocumentException e) { + Assume.assumeTrue(e.getMessage().startsWith("Export Restrictions")); } finally { if (pkg != null) { pkg.close(); diff --git a/src/ooxml/testcases/org/apache/poi/xddf/usermodel/TestNecessaryOOXMLClasses.java b/src/ooxml/testcases/org/apache/poi/xddf/usermodel/TestNecessaryOOXMLClasses.java index 74ec1bb6cf..6cbf24ab6f 100644 --- a/src/ooxml/testcases/org/apache/poi/xddf/usermodel/TestNecessaryOOXMLClasses.java +++ b/src/ooxml/testcases/org/apache/poi/xddf/usermodel/TestNecessaryOOXMLClasses.java @@ -87,6 +87,10 @@ public class TestNecessaryOOXMLClasses { Assert.assertNotNull(ctLblAlgn); CTDashStopList ctDashStopList = CTDashStopList.Factory.newInstance(); Assert.assertNotNull(ctDashStopList); + STDispBlanksAs stDashBlanksAs = STDispBlanksAs.Factory.newInstance(); + Assert.assertNotNull(stDashBlanksAs); + CTDispBlanksAs ctDashBlanksAs = CTDispBlanksAs.Factory.newInstance(); + Assert.assertNotNull(ctDashBlanksAs); STLblAlgn.Enum e1 = STLblAlgn.Enum.forString("ctr"); Assert.assertNotNull(e1); @@ -100,6 +104,8 @@ public class TestNecessaryOOXMLClasses { Assert.assertNotNull(e5); STMarkerStyle.Enum e6 = STMarkerStyle.Enum.forString("circle"); Assert.assertNotNull(e6); + STDispBlanksAs.Enum e7 = STDispBlanksAs.Enum.forString("span"); + Assert.assertNotNull(e7); CTTextBulletTypefaceFollowText ctTextBulletTypefaceFollowText = CTTextBulletTypefaceFollowText.Factory.newInstance(); Assert.assertNotNull(ctTextBulletTypefaceFollowText); diff --git a/src/ooxml/testcases/org/apache/poi/xdgf/extractor/TestXDGFVisioExtractor.java b/src/ooxml/testcases/org/apache/poi/xdgf/extractor/TestXDGFVisioExtractor.java index 6a3369ed90..c261e2c91b 100644 --- a/src/ooxml/testcases/org/apache/poi/xdgf/extractor/TestXDGFVisioExtractor.java +++ b/src/ooxml/testcases/org/apache/poi/xdgf/extractor/TestXDGFVisioExtractor.java @@ -42,7 +42,7 @@ public class TestXDGFVisioExtractor { } @After - public void closeResoures() throws IOException { + public void closeResources() throws IOException { if(xml != null) { xml.close(); } diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java index 94205da144..9a206a48ca 100644 --- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java @@ -47,7 +47,9 @@ public class TestPPTX2PNG { private static final POIDataSamples samples = POIDataSamples.getSlideShowInstance(); private static final File basedir = null; private static final String files = - "53446.ppt, alterman_security.ppt, alterman_security.pptx, KEY02.pptx, themes.pptx, backgrounds.pptx, layouts.pptx, sample.pptx, shapes.pptx, 54880_chinese.ppt"; + "53446.ppt, alterman_security.ppt, alterman_security.pptx, KEY02.pptx, themes.pptx, " + + "backgrounds.pptx, layouts.pptx, sample.pptx, shapes.pptx, 54880_chinese.ppt, keyframes.pptx," + + "customGeo.pptx, customGeo.ppt"; diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFSlide.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFSlide.java index fbf4589765..b9cda3fd21 100644 --- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFSlide.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFSlide.java @@ -20,6 +20,7 @@ import static org.apache.poi.sl.TestCommonSL.sameColor; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -34,7 +35,7 @@ import org.junit.Test; * @author Yegor Kozlov */ public class TestXSLFSlide { - + @Test public void testReadShapes() throws IOException { XMLSlideShow ppt = XSLFTestDataSamples.openSampleDocument("shapes.pptx"); @@ -101,7 +102,7 @@ public class TestXSLFSlide { XSLFTable tbl = (XSLFTable)shapes4.get(0); assertEquals(3, tbl.getNumberOfColumns()); assertEquals(6, tbl.getNumberOfRows()); - + ppt.close(); } @@ -116,7 +117,7 @@ public class TestXSLFSlide { assertFalse(slide.getFollowMasterGraphics()); slide.setFollowMasterGraphics(true); assertTrue(slide.getFollowMasterGraphics()); - + ppt.close(); } @@ -174,7 +175,7 @@ public class TestXSLFSlide { XSLFPictureShape sh4 = (XSLFPictureShape)shapes2.get(1); XSLFPictureShape srcPic = (XSLFPictureShape)src.getSlides().get(4).getShapes().get(1); assertArrayEquals(sh4.getPictureData().getData(), srcPic.getPictureData().getData()); - + ppt.close(); } @@ -191,7 +192,23 @@ public class TestXSLFSlide { } } assertEquals(30, ppt.getSlides().size()); - + ppt.close(); - } -} \ No newline at end of file + } + + @Test + public void testCreateChart() throws IOException { + XMLSlideShow ppt = new XMLSlideShow(); + XSLFSlide slide = ppt.createSlide(); + XSLFChart chart = ppt.createChart(); + assertNotNull(chart); + + slide.addChart(chart); + assertEquals(XSLFRelation.CHART.getContentType(), chart.getPackagePart().getContentType()); + + String partName = slide.getRelationPartById("rId2").getDocumentPart().getPackagePart().getPartName().getName(); + assertEquals(partName, chart.getPackagePart().getPartName().getName()); + + ppt.close(); + } +} diff --git a/src/ooxml/testcases/org/apache/poi/xssf/XSSFTestDataSamples.java b/src/ooxml/testcases/org/apache/poi/xssf/XSSFTestDataSamples.java index 0f03af7453..b981b9994e 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/XSSFTestDataSamples.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/XSSFTestDataSamples.java @@ -70,7 +70,7 @@ public class XSSFTestDataSamples { * @param wb the workbook to write * @param testName a fragment of the filename * @return the location where the workbook was saved - * @throws IOException + * @throws IOException If writing the file fails */ public static File writeOut(R wb, String testName) throws IOException { final File file = getOutputFile(testName); @@ -104,7 +104,9 @@ public class XSSFTestDataSamples { file = TempFile.createTempFile(testName, ".xlsx"); } if (file.exists()) { - file.delete(); + if(!file.delete()) { + throw new IOException("Could not delete file " + file); + } } return file; } @@ -114,7 +116,7 @@ public class XSSFTestDataSamples { * * @param wb the workbook to write * @return the memory buffer - * @throws IOException + * @throws IOException If writing the file fails */ public static ByteArrayOutputStream writeOut(R wb) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(8192); @@ -137,7 +139,7 @@ public class XSSFTestDataSamples { * to avoid creating a temporary file. However, this may complicate the calling * code to avoid having the workbook, BAOS, and BAIS open at the same time. * - * @param wb + * @param wb The workbook to write out, it is closed after the call. * @param testName file name to be used to write to a file. This file will be cleaned up by a call to readBack(String) * @return workbook location * @throws RuntimeException if {@link #TEST_OUTPUT_DIR} System property is not set @@ -161,18 +163,13 @@ public class XSSFTestDataSamples { * * @param wb the workbook to write * @return the memory buffer - * @throws IOException + * @throws RuntimeException If writing the file fails */ - public static ByteArrayOutputStream writeOutAndClose(R wb) { - try { - ByteArrayOutputStream out = writeOut(wb); - // Do not close the workbook if there was a problem writing the workbook - wb.close(); - return out; - } - catch (final IOException e) { - throw new RuntimeException(e); - } + public static ByteArrayOutputStream writeOutAndClose(R wb) throws IOException { + ByteArrayOutputStream out = writeOut(wb); + // Do not close the workbook if there was a problem writing the workbook + wb.close(); + return out; } /** @@ -183,12 +180,14 @@ public class XSSFTestDataSamples { * * @param file the workbook file to read and delete * @return the read back workbook - * @throws IOException + * @throws IOException If reading or deleting the file fails */ public static XSSFWorkbook readBackAndDelete(File file) throws IOException { XSSFWorkbook wb = readBack(file); // do not delete the file if there's an error--might be helpful for debugging - file.delete(); + if(!file.delete()) { + throw new IOException("Could not delete file " + file + " after reading"); + } return wb; } @@ -198,16 +197,12 @@ public class XSSFTestDataSamples { * * @param file the workbook file to read * @return the read back workbook - * @throws IOException + * @throws IOException If reading the file fails */ public static XSSFWorkbook readBack(File file) throws IOException { - InputStream in = new FileInputStream(file); - try { + try (InputStream in = new FileInputStream(file)) { return new XSSFWorkbook(in); } - finally { - in.close(); - } } /** @@ -216,17 +211,13 @@ public class XSSFTestDataSamples { * * @param out the output stream to read back from * @return the read back workbook - * @throws IOException + * @throws IOException If reading the file fails */ public static XSSFWorkbook readBack(ByteArrayOutputStream out) throws IOException { - InputStream is = new ByteArrayInputStream(out.toByteArray()); - out.close(); - try { + try (InputStream is = new ByteArrayInputStream(out.toByteArray())) { + out.close(); return new XSSFWorkbook(is); } - finally { - is.close(); - } } /** diff --git a/src/ooxml/testcases/org/apache/poi/xwpf/extractor/TestXWPFWordExtractor.java b/src/ooxml/testcases/org/apache/poi/xwpf/extractor/TestXWPFWordExtractor.java index 0fcccd1838..30d943051f 100644 --- a/src/ooxml/testcases/org/apache/poi/xwpf/extractor/TestXWPFWordExtractor.java +++ b/src/ooxml/testcases/org/apache/poi/xwpf/extractor/TestXWPFWordExtractor.java @@ -452,4 +452,12 @@ public class TestXWPFWordExtractor extends TestCase { //once we add processing for this, we can change this to contains assertNotContained(txt, "table rows"); } + + public void testPartsInTemplate() throws IOException { + XWPFDocument doc = XWPFTestDataSamples.openSampleDocument("60316b.dotx"); + XWPFWordExtractor extractor = new XWPFWordExtractor(doc); + String txt = extractor.getText(); + assertContains(txt, "header 2"); + assertContains(txt, "footer 1"); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/CurrentUserAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/CurrentUserAtom.java index cedfbb0470..26ba59d2c6 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/CurrentUserAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/CurrentUserAtom.java @@ -20,6 +20,8 @@ package org.apache.poi.hslf.record; +import static org.apache.poi.hslf.usermodel.HSLFSlideShow.PP95_DOCUMENT; + import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -143,7 +145,7 @@ public class CurrentUserAtom // 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("PP40"); + boolean isPP95 = dir.hasEntry(PP95_DOCUMENT); // PPT95 has 4 byte size, then data if (!isPP95 && _contents.length >= 4) { int size = LittleEndian.getInt(_contents); diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFAutoShape.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFAutoShape.java index 910c8a0adc..3a3fa7afb3 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFAutoShape.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFAutoShape.java @@ -17,21 +17,122 @@ package org.apache.poi.hslf.usermodel; +import java.awt.geom.Arc2D; +import java.awt.geom.Path2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.Iterator; +import java.util.List; + +import org.apache.poi.ddf.AbstractEscherOptRecord; +import org.apache.poi.ddf.EscherArrayProperty; import org.apache.poi.ddf.EscherContainerRecord; import org.apache.poi.ddf.EscherProperties; -import org.apache.poi.sl.usermodel.*; +import org.apache.poi.ddf.EscherProperty; +import org.apache.poi.ddf.EscherSimpleProperty; +import org.apache.poi.sl.draw.binding.CTAdjPoint2D; +import org.apache.poi.sl.draw.binding.CTCustomGeometry2D; +import org.apache.poi.sl.draw.binding.CTPath2D; +import org.apache.poi.sl.draw.binding.CTPath2DArcTo; +import org.apache.poi.sl.draw.binding.CTPath2DCubicBezierTo; +import org.apache.poi.sl.draw.binding.CTPath2DLineTo; +import org.apache.poi.sl.draw.binding.CTPath2DList; +import org.apache.poi.sl.draw.binding.CTPath2DMoveTo; +import org.apache.poi.sl.draw.binding.ObjectFactory; +import org.apache.poi.sl.draw.geom.CustomGeometry; +import org.apache.poi.sl.usermodel.AutoShape; +import org.apache.poi.sl.usermodel.ShapeContainer; +import org.apache.poi.sl.usermodel.ShapeType; +import org.apache.poi.sl.usermodel.VerticalAlignment; import org.apache.poi.ss.usermodel.ShapeTypes; +import org.apache.poi.util.BitField; +import org.apache.poi.util.BitFieldFactory; +import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.POILogFactory; +import org.apache.poi.util.POILogger; /** - * Represents an AutoShape. - *

    + * Represents an AutoShape.

    + * * AutoShapes are drawing objects with a particular shape that may be customized through smart resizing and adjustments. * See {@link ShapeTypes} - *

    - * - * @author Yegor Kozlov */ public class HSLFAutoShape extends HSLFTextShape implements AutoShape { + private static final POILogger LOG = POILogFactory.getLogger(HSLFAutoShape.class); + + static final byte[] SEGMENTINFO_MOVETO = new byte[]{0x00, 0x40}; + static final byte[] SEGMENTINFO_LINETO = new byte[]{0x00, (byte)0xAC}; + static final byte[] SEGMENTINFO_ESCAPE = new byte[]{0x01, 0x00}; + static final byte[] SEGMENTINFO_ESCAPE2 = new byte[]{0x01, 0x20}; + static final byte[] SEGMENTINFO_CUBICTO = new byte[]{0x00, (byte)0xAD}; + // OpenOffice inserts 0xB3 instead of 0xAD. + // protected static final byte[] SEGMENTINFO_CUBICTO2 = new byte[]{0x00, (byte)0xB3}; + static final byte[] SEGMENTINFO_CLOSE = new byte[]{0x01, (byte)0x60}; + static final byte[] SEGMENTINFO_END = new byte[]{0x00, (byte)0x80}; + + private static final BitField PATH_INFO = BitFieldFactory.getInstance(0xE000); + private static final BitField ESCAPE_INFO = BitFieldFactory.getInstance(0x1F00); + + enum PathInfo { + lineTo(0),curveTo(1),moveTo(2),close(3),end(4),escape(5),clientEscape(6); + private final int flag; + PathInfo(int flag) { + this.flag = flag; + } + public int getFlag() { + return flag; + } + static PathInfo valueOf(int flag) { + for (PathInfo v : values()) { + if (v.flag == flag) { + return v; + } + } + return null; + } + } + + enum EscapeInfo { + EXTENSION(0x0000), + ANGLE_ELLIPSE_TO(0x0001), + ANGLE_ELLIPSE(0x0002), + ARC_TO(0x0003), + ARC(0x0004), + CLOCKWISE_ARC_TO(0x0005), + CLOCKWISE_ARC(0x0006), + ELLIPTICAL_QUADRANT_X(0x0007), + ELLIPTICAL_QUADRANT_Y(0x0008), + QUADRATIC_BEZIER(0x0009), + NO_FILL(0X000A), + NO_LINE(0X000B), + AUTO_LINE(0X000C), + AUTO_CURVE(0X000D), + CORNER_LINE(0X000E), + CORNER_CURVE(0X000F), + SMOOTH_LINE(0X0010), + SMOOTH_CURVE(0X0011), + SYMMETRIC_LINE(0X0012), + SYMMETRIC_CURVE(0X0013), + FREEFORM(0X0014), + FILL_COLOR(0X0015), + LINE_COLOR(0X0016); + + private final int flag; + EscapeInfo(int flag) { + this.flag = flag; + } + public int getFlag() { + return flag; + } + static EscapeInfo valueOf(int flag) { + for (EscapeInfo v : values()) { + if (v.flag == flag) { + return v; + } + } + return null; + } + } protected HSLFAutoShape(EscherContainerRecord escherRecord, ShapeContainer parent){ super(escherRecord, parent); @@ -72,13 +173,11 @@ public class HSLFAutoShape extends HSLFTextShape implements AutoShape * - *

    * The adjustment values are given in shape coordinates: * the origin is at the top-left, positive-x is to the right, positive-y is down. * The region from (0,0) to (S,S) maps to the geometry box of the shape (S=21600 is a constant). - *

    * * @param idx the adjust index in the [0, 9] range * @return the adjustment value @@ -90,13 +189,11 @@ public class HSLFAutoShape extends HSLFTextShape implements AutoShape * - *

    * The adjustment values are given in shape coordinates: * the origin is at the top-left, positive-x is to the right, positive-y is down. * The region from (0,0) to (S,S) maps to the geometry box of the shape (S=21600 is a constant). - *

    * * @param idx the adjust index in the [0, 9] range * @param val the adjustment value @@ -106,4 +203,278 @@ public class HSLFAutoShape extends HSLFTextShape implements AutoShape vertIter = verticesProp.iterator(); + final Iterator segIter = segmentsProp.iterator(); + final int[] xyPoints = new int[2]; + boolean isClosed = false; + + final CTPath2DList pathLst = of.createCTPath2DList(); + final CTPath2D pathCT = of.createCTPath2D(); + final List moveLst = pathCT.getCloseOrMoveToOrLnTo(); + pathLst.getPath().add(pathCT); + cusGeo.setPathLst(pathLst); + + while (segIter.hasNext()) { + byte[] segElem = segIter.next(); + HSLFFreeformShape.PathInfo pi = getPathInfo(segElem); + if (pi == null) { + continue; + } + switch (pi) { + case escape: { + handleEscapeInfo(pathCT, path2D, segElem, vertIter); + break; + } + case moveTo: + if (vertIter.hasNext()) { + final CTPath2DMoveTo m = of.createCTPath2DMoveTo(); + m.setPt(fillPoint(vertIter.next(), xyPoints)); + moveLst.add(m); + path2D.moveTo(xyPoints[0], xyPoints[1]); + } + break; + case lineTo: + if (vertIter.hasNext()) { + final CTPath2DLineTo m = of.createCTPath2DLineTo(); + m.setPt(fillPoint(vertIter.next(), xyPoints)); + moveLst.add(m); + path2D.lineTo(xyPoints[0], xyPoints[1]); + } + break; + case curveTo: { + final CTPath2DCubicBezierTo m = of.createCTPath2DCubicBezierTo(); + List mLst = m.getPt(); + + int[] pts = new int[6]; + + for (int i=0; vertIter.hasNext() && i<3; i++) { + mLst.add(fillPoint(vertIter.next(), xyPoints)); + pts[i*2] = xyPoints[0]; + pts[i*2+1] = xyPoints[1]; + if (i == 2) { + moveLst.add(m); + path2D.curveTo(pts[0], pts[1], pts[2], pts[3], pts[4], pts[5]); + } + } + break; + } + case close: + moveLst.add(of.createCTPath2DClose()); + path2D.closePath(); + isClosed = true; + break; + default: + break; + } + } + + EscherSimpleProperty shapePath = getShapeProp(opt, EscherProperties.GEOMETRY__SHAPEPATH); + HSLFFreeformShape.ShapePath sp = HSLFFreeformShape.ShapePath.valueOf(shapePath == null ? 1 : shapePath.getPropertyValue()); + if ((sp == HSLFFreeformShape.ShapePath.LINES_CLOSED || sp == HSLFFreeformShape.ShapePath.CURVES_CLOSED) && !isClosed) { + moveLst.add(of.createCTPath2DClose()); + path2D.closePath(); + } + + EscherSimpleProperty geoLeft = getShapeProp(opt, EscherProperties.GEOMETRY__LEFT); + EscherSimpleProperty geoRight = getShapeProp(opt, EscherProperties.GEOMETRY__RIGHT); + EscherSimpleProperty geoTop = getShapeProp(opt, EscherProperties.GEOMETRY__TOP); + EscherSimpleProperty geoBottom = getShapeProp(opt, EscherProperties.GEOMETRY__BOTTOM); + + final Rectangle2D bounds; + if (geoLeft != null && geoRight != null && geoTop != null && geoBottom != null) { + bounds = new Rectangle2D.Double(); + bounds.setFrameFromDiagonal( + new Point2D.Double(geoLeft.getPropertyValue(), geoTop.getPropertyValue()), + new Point2D.Double(geoRight.getPropertyValue(), geoBottom.getPropertyValue()) + ); + } else { + bounds = path2D.getBounds2D(); + } + + pathCT.setW((int)Math.rint(bounds.getWidth())); + pathCT.setH((int)Math.rint(bounds.getHeight())); + + return new CustomGeometry(cusGeo); + } + + private void handleEscapeInfo(CTPath2D pathCT, Path2D path2D, byte[] segElem, Iterator vertIter) { + final ObjectFactory of = new ObjectFactory(); + HSLFFreeformShape.EscapeInfo ei = getEscapeInfo(segElem); + switch (ei) { + case EXTENSION: + break; + case ANGLE_ELLIPSE_TO: + break; + case ANGLE_ELLIPSE: + break; + case ARC_TO: { + // The first two POINT values specify the bounding rectangle of the ellipse. + // The second two POINT values specify the radial vectors for the ellipse. + // The radial vectors are cast from the center of the bounding rectangle. + // The path starts at the POINT where the first radial vector intersects the + // bounding rectangle and goes to the POINT where the second radial vector + // intersects the bounding rectangle. The drawing direction is always counterclockwise. + // If the path has already been started, a line is drawn from the last POINT to + // the starting POINT of the arc; otherwise, a new path is started. + // The number of arc segments drawn equals the number of segments divided by four. + + int[] r1 = new int[2], r2 = new int[2], start = new int[2], end = new int[2]; + fillPoint(vertIter.next(), r1); + fillPoint(vertIter.next(), r2); + fillPoint(vertIter.next(), start); + fillPoint(vertIter.next(), end); + + Arc2D arc2D = new Arc2D.Double(); + Rectangle2D.Double bounds = new Rectangle2D.Double(); + bounds.setFrameFromDiagonal(xy2p(r1), xy2p(r2)); + arc2D.setFrame(bounds); + arc2D.setAngles(xy2p(start), xy2p(end)); + path2D.append(arc2D, true); + + + CTPath2DArcTo arcTo = of.createCTPath2DArcTo(); + arcTo.setHR(d2s(bounds.getHeight()/2.0)); + arcTo.setWR(d2s(bounds.getWidth()/2.0)); + + arcTo.setStAng(d2s(-arc2D.getAngleStart()*60000.)); + arcTo.setSwAng(d2s(-arc2D.getAngleExtent()*60000.)); + + pathCT.getCloseOrMoveToOrLnTo().add(arcTo); + + break; + } + case ARC: + break; + case CLOCKWISE_ARC_TO: + break; + case CLOCKWISE_ARC: + break; + case ELLIPTICAL_QUADRANT_X: + break; + case ELLIPTICAL_QUADRANT_Y: + break; + case QUADRATIC_BEZIER: + break; + case NO_FILL: + break; + case NO_LINE: + break; + case AUTO_LINE: + break; + case AUTO_CURVE: + break; + case CORNER_LINE: + break; + case CORNER_CURVE: + break; + case SMOOTH_LINE: + break; + case SMOOTH_CURVE: + break; + case SYMMETRIC_LINE: + break; + case SYMMETRIC_CURVE: + break; + case FREEFORM: + break; + case FILL_COLOR: + break; + case LINE_COLOR: + break; + default: + break; + } + } + + private static String d2s(double d) { + return Integer.toString((int)Math.rint(d)); + } + + private static Point2D xy2p(int[] xyPoints) { + return new Point2D.Double(xyPoints[0],xyPoints[1]); + } + + private static HSLFFreeformShape.PathInfo getPathInfo(byte[] elem) { + int elemUS = LittleEndian.getUShort(elem, 0); + int pathInfo = PATH_INFO.getValue(elemUS); + return HSLFFreeformShape.PathInfo.valueOf(pathInfo); + } + + private static HSLFFreeformShape.EscapeInfo getEscapeInfo(byte[] elem) { + int elemUS = LittleEndian.getUShort(elem, 0); + int escInfo = ESCAPE_INFO.getValue(elemUS); + return HSLFFreeformShape.EscapeInfo.valueOf(escInfo); + } + + + private static T getShapeProp(AbstractEscherOptRecord opt, int propId) { + T prop = getEscherProperty(opt, (short)(propId + 0x4000)); + if (prop == null) { + prop = getEscherProperty(opt, propId); + } + return prop; + } + + private CTAdjPoint2D fillPoint(byte[] xyMaster, int[] xyPoints) { + if (xyMaster == null || xyPoints == null) { + LOG.log(POILogger.WARN, "Master bytes or points not set - ignore point"); + return null; + } + if ((xyMaster.length != 4 && xyMaster.length != 8) || xyPoints.length != 2) { + LOG.log(POILogger.WARN, "Invalid number of master bytes for a single point - ignore point"); + return null; + } + + int x, y; + if (xyMaster.length == 4) { + x = LittleEndian.getShort(xyMaster, 0); + y = LittleEndian.getShort(xyMaster, 2); + } else { + x = LittleEndian.getInt(xyMaster, 0); + y = LittleEndian.getInt(xyMaster, 4); + } + + xyPoints[0] = x; + xyPoints[1] = y; + + return toPoint(xyPoints); + } + + private static CTAdjPoint2D toPoint(int[] xyPoints) { + CTAdjPoint2D pt = new CTAdjPoint2D(); + pt.setX(Integer.toString(xyPoints[0])); + pt.setY(Integer.toString(xyPoints[1])); + return pt; + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFill.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFill.java index ee0a2d0731..63885af3f3 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFill.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFill.java @@ -49,60 +49,61 @@ import org.apache.poi.util.Units; /** * Represents functionality provided by the 'Fill Effects' dialog in PowerPoint. */ +@SuppressWarnings("WeakerAccess") public final class HSLFFill { private static final POILogger LOG = POILogFactory.getLogger(HSLFFill.class); /** * Fill with a solid color */ - public static final int FILL_SOLID = 0; + static final int FILL_SOLID = 0; /** * Fill with a pattern (bitmap) */ - public static final int FILL_PATTERN = 1; + static final int FILL_PATTERN = 1; /** * A texture (pattern with its own color map) */ - public static final int FILL_TEXTURE = 2; + static final int FILL_TEXTURE = 2; /** * Center a picture in the shape */ - public static final int FILL_PICTURE = 3; + static final int FILL_PICTURE = 3; /** * Shade from start to end points */ - public static final int FILL_SHADE = 4; + static final int FILL_SHADE = 4; /** * Shade from bounding rectangle to end point */ - public static final int FILL_SHADE_CENTER = 5; + static final int FILL_SHADE_CENTER = 5; /** * Shade from shape outline to end point */ - public static final int FILL_SHADE_SHAPE = 6; + static final int FILL_SHADE_SHAPE = 6; /** * Similar to FILL_SHADE, but the fill angle * is additionally scaled by the aspect ratio of * the shape. If shape is square, it is the same as FILL_SHADE */ - public static final int FILL_SHADE_SCALE = 7; + static final int FILL_SHADE_SCALE = 7; /** * shade to title */ - public static final int FILL_SHADE_TITLE = 8; + static final int FILL_SHADE_TITLE = 8; /** * Use the background fill color/pattern */ - public static final int FILL_BACKGROUND = 9; + static final int FILL_BACKGROUND = 9; /** * A bit that specifies whether the RecolorFillAsPicture bit is set. @@ -214,7 +215,7 @@ public final class HSLFFill { private HSLFShape shape; /** - * Construct a Fill object for a shape. + * Construct a {@code Fill} object for a shape. * Fill information will be read from shape's escher properties. * * @param shape the shape this background applies to @@ -279,7 +280,7 @@ public final class HSLFFill { @Override public ColorStyle[] getGradientColors() { - ColorStyle cs[]; + ColorStyle[] cs; if (colorCnt == 0) { cs = new ColorStyle[2]; cs[0] = wrapColor(getBackgroundColor()); @@ -288,7 +289,7 @@ public final class HSLFFill { cs = new ColorStyle[colorCnt]; int idx = 0; // TODO: handle palette colors and alpha(?) value - for (byte data[] : ep) { + for (byte[] data : ep) { EscherColorRef ecr = new EscherColorRef(data, 0, 4); cs[idx++] = wrapColor(shape.getColor(ecr)); } @@ -302,13 +303,13 @@ public final class HSLFFill { @Override public float[] getGradientFractions() { - float frc[]; + float[] frc; if (colorCnt == 0) { frc = new float[]{0, 1}; } else { frc = new float[colorCnt]; int idx = 0; - for (byte data[] : ep) { + for (byte[] data : ep) { double pos = Units.fixedPointToDouble(LittleEndian.getInt(data, 4)); frc[idx++] = (float)pos; } @@ -354,7 +355,7 @@ public final class HSLFFill { /** * Returns fill type. - * Must be one of the FILL_* constants defined in this class. + * Must be one of the {@code FILL_*} constants defined in this class. * * @return type of fill */ @@ -364,9 +365,7 @@ public final class HSLFFill { return prop == null ? FILL_SOLID : prop.getPropertyValue(); } - /** - */ - protected void afterInsert(HSLFSheet sh){ + void afterInsert(HSLFSheet sh){ AbstractEscherOptRecord opt = shape.getEscherOptRecord(); EscherSimpleProperty p = HSLFShape.getEscherProperty(opt, EscherProperties.FILL__PATTERNTEXTURE); if(p != null) { @@ -379,7 +378,7 @@ public final class HSLFFill { } @SuppressWarnings("resource") - protected EscherBSERecord getEscherBSERecord(int idx){ + EscherBSERecord getEscherBSERecord(int idx){ HSLFSheet sheet = shape.getSheet(); if(sheet == null) { LOG.log(POILogger.DEBUG, "Fill has not yet been assigned to a sheet"); @@ -399,7 +398,7 @@ public final class HSLFFill { /** * Sets fill type. - * Must be one of the FILL_* constants defined in this class. + * Must be one of the {@code FILL_*} constants defined in this class. * * @param type type of the fill */ @@ -415,10 +414,10 @@ public final class HSLFFill { AbstractEscherOptRecord opt = shape.getEscherOptRecord(); EscherSimpleProperty p = HSLFShape.getEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST); int propVal = (p == null) ? 0 : p.getPropertyValue(); - + return (FILL_USE_FILLED.isSet(propVal) && !FILL_FILLED.isSet(propVal)) ? null - : shape.getColor(EscherProperties.FILL__FILLCOLOR, EscherProperties.FILL__FILLOPACITY, -1); + : shape.getColor(EscherProperties.FILL__FILLCOLOR, EscherProperties.FILL__FILLOPACITY); } /** @@ -462,7 +461,7 @@ public final class HSLFFill { return (FILL_USE_FILLED.isSet(propVal) && !FILL_FILLED.isSet(propVal)) ? null - : shape.getColor(EscherProperties.FILL__FILLBACKCOLOR, EscherProperties.FILL__FILLOPACITY, -1); + : shape.getColor(EscherProperties.FILL__FILLBACKCOLOR, EscherProperties.FILL__FILLOPACITY); } /** @@ -480,7 +479,7 @@ public final class HSLFFill { } /** - * PictureData object used in a texture, pattern of picture fill. + * {@code PictureData} object used in a texture, pattern of picture fill. */ @SuppressWarnings("resource") public HSLFPictureData getPictureData(){ diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFreeformShape.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFreeformShape.java index 0b2d322b63..10eeb7b19c 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFreeformShape.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFreeformShape.java @@ -23,20 +23,16 @@ import java.awt.geom.PathIterator; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; import org.apache.poi.ddf.AbstractEscherOptRecord; import org.apache.poi.ddf.EscherArrayProperty; import org.apache.poi.ddf.EscherContainerRecord; import org.apache.poi.ddf.EscherProperties; -import org.apache.poi.ddf.EscherProperty; import org.apache.poi.ddf.EscherSimpleProperty; import org.apache.poi.sl.usermodel.FreeformShape; import org.apache.poi.sl.usermodel.ShapeContainer; import org.apache.poi.sl.usermodel.ShapeType; -import org.apache.poi.util.BitField; -import org.apache.poi.util.BitFieldFactory; import org.apache.poi.util.LittleEndian; import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; @@ -53,79 +49,6 @@ import org.apache.poi.util.Units; public final class HSLFFreeformShape extends HSLFAutoShape implements FreeformShape { private static final POILogger LOG = POILogFactory.getLogger(HSLFFreeformShape.class); - private static final byte[] SEGMENTINFO_MOVETO = new byte[]{0x00, 0x40}; - private static final byte[] SEGMENTINFO_LINETO = new byte[]{0x00, (byte)0xAC}; - private static final byte[] SEGMENTINFO_ESCAPE = new byte[]{0x01, 0x00}; - private static final byte[] SEGMENTINFO_ESCAPE2 = new byte[]{0x01, 0x20}; - private static final byte[] SEGMENTINFO_CUBICTO = new byte[]{0x00, (byte)0xAD}; - // OpenOffice inserts 0xB3 instead of 0xAD. - // private static final byte[] SEGMENTINFO_CUBICTO2 = new byte[]{0x00, (byte)0xB3}; - private static final byte[] SEGMENTINFO_CLOSE = new byte[]{0x01, (byte)0x60}; - private static final byte[] SEGMENTINFO_END = new byte[]{0x00, (byte)0x80}; - - private static final BitField PATH_INFO = BitFieldFactory.getInstance(0xE000); - // private static final BitField ESCAPE_INFO = BitFieldFactory.getInstance(0x1F00); - - enum PathInfo { - lineTo(0),curveTo(1),moveTo(2),close(3),end(4),escape(5),clientEscape(6); - private final int flag; - PathInfo(int flag) { - this.flag = flag; - } - public int getFlag() { - return flag; - } - static PathInfo valueOf(int flag) { - for (PathInfo v : values()) { - if (v.flag == flag) { - return v; - } - } - return null; - } - } - - enum EscapeInfo { - EXTENSION(0x0000), - ANGLE_ELLIPSE_TO(0x0001), - ANGLE_ELLIPSE(0x0002), - ARC_TO(0x0003), - ARC(0x0004), - CLOCKWISE_ARC_TO(0x0005), - CLOCKWISE_ARC(0x0006), - ELLIPTICAL_QUADRANT_X(0x0007), - ELLIPTICAL_QUADRANT_Y(0x0008), - QUADRATIC_BEZIER(0x0009), - NO_FILL(0X000A), - NO_LINE(0X000B), - AUTO_LINE(0X000C), - AUTO_CURVE(0X000D), - CORNER_LINE(0X000E), - CORNER_CURVE(0X000F), - SMOOTH_LINE(0X0010), - SMOOTH_CURVE(0X0011), - SYMMETRIC_LINE(0X0012), - SYMMETRIC_CURVE(0X0013), - FREEFORM(0X0014), - FILL_COLOR(0X0015), - LINE_COLOR(0X0016); - - private final int flag; - EscapeInfo(int flag) { - this.flag = flag; - } - public int getFlag() { - return flag; - } - static EscapeInfo valueOf(int flag) { - for (EscapeInfo v : values()) { - if (v.flag == flag) { - return v; - } - } - return null; - } - } enum ShapePath { LINES(0), @@ -182,9 +105,9 @@ public final class HSLFFreeformShape extends HSLFAutoShape implements FreeformSh } @Override - public int setPath(Path2D.Double path) { + public int setPath(Path2D path) { Rectangle2D bounds = path.getBounds2D(); - PathIterator it = path.getPathIterator(new AffineTransform()); + PathIterator it = path.getPathIterator(null); List segInfo = new ArrayList<>(); List pntInfo = new ArrayList<>(); @@ -275,187 +198,24 @@ public final class HSLFFreeformShape extends HSLFAutoShape implements FreeformSh } @Override - public Path2D.Double getPath(){ - AbstractEscherOptRecord opt = getEscherOptRecord(); + public Path2D getPath(){ + Path2D path2D = new Path2D.Double(); + getGeometry(path2D); - EscherArrayProperty verticesProp = getShapeProp(opt, EscherProperties.GEOMETRY__VERTICES); - EscherArrayProperty segmentsProp = getShapeProp(opt, EscherProperties.GEOMETRY__SEGMENTINFO); - - // return empty path if either GEOMETRY__VERTICES or GEOMETRY__SEGMENTINFO is missing, see Bugzilla 54188 - Path2D.Double path = new Path2D.Double(); - - //sanity check - if(verticesProp == null) { - LOG.log(POILogger.WARN, "Freeform is missing GEOMETRY__VERTICES "); - return path; - } - if(segmentsProp == null) { - LOG.log(POILogger.WARN, "Freeform is missing GEOMETRY__SEGMENTINFO "); - return path; - } - - Iterator vertIter = verticesProp.iterator(); - Iterator segIter = segmentsProp.iterator(); - double xyPoints[] = new double[2]; - - while (vertIter.hasNext() && segIter.hasNext()) { - byte[] segElem = segIter.next(); - PathInfo pi = getPathInfo(segElem); - if (pi != null) { - switch (pi) { - case escape: { - // handleEscapeInfo(path, segElem, vertIter); - break; - } - case moveTo: { - fillPoint(vertIter.next(), xyPoints); - double x = xyPoints[0]; - double y = xyPoints[1]; - path.moveTo(x, y); - break; - } - case curveTo: { - fillPoint(vertIter.next(), xyPoints); - double x1 = xyPoints[0]; - double y1 = xyPoints[1]; - fillPoint(vertIter.next(), xyPoints); - double x2 = xyPoints[0]; - double y2 = xyPoints[1]; - fillPoint(vertIter.next(), xyPoints); - double x3 = xyPoints[0]; - double y3 = xyPoints[1]; - path.curveTo(x1, y1, x2, y2, x3, y3); - break; - } - case lineTo: - if (vertIter.hasNext()) { - fillPoint(vertIter.next(), xyPoints); - double x = xyPoints[0]; - double y = xyPoints[1]; - path.lineTo(x, y); - } - break; - case close: - path.closePath(); - break; - default: - break; - } - } - } - - EscherSimpleProperty shapePath = getShapeProp(opt, EscherProperties.GEOMETRY__SHAPEPATH); - ShapePath sp = ShapePath.valueOf(shapePath == null ? 1 : shapePath.getPropertyValue()); - if (sp == ShapePath.LINES_CLOSED || sp == ShapePath.CURVES_CLOSED) { - path.closePath(); - } - + Rectangle2D bounds = path2D.getBounds2D(); Rectangle2D anchor = getAnchor(); - Rectangle2D bounds = path.getBounds2D(); AffineTransform at = new AffineTransform(); at.translate(anchor.getX(), anchor.getY()); at.scale( anchor.getWidth()/bounds.getWidth(), anchor.getHeight()/bounds.getHeight() ); - return new Path2D.Double(at.createTransformedShape(path)); - } - - private void fillPoint(byte xyMaster[], double xyPoints[]) { - if (xyMaster == null || xyPoints == null) { - LOG.log(POILogger.WARN, "Master bytes or points not set - ignore point"); - return; - } - if ((xyMaster.length != 4 && xyMaster.length != 8) || xyPoints.length != 2) { - LOG.log(POILogger.WARN, "Invalid number of master bytes for a single point - ignore point"); - return; - } - - int x, y; - if (xyMaster.length == 4) { - x = LittleEndian.getShort(xyMaster, 0); - y = LittleEndian.getShort(xyMaster, 2); - } else { - x = LittleEndian.getInt(xyMaster, 0); - y = LittleEndian.getInt(xyMaster, 4); - } - - xyPoints[0] = Units.masterToPoints(x); - xyPoints[1] = Units.masterToPoints(y); - } - - private static T getShapeProp(AbstractEscherOptRecord opt, int propId) { - T prop = getEscherProperty(opt, (short)(propId + 0x4000)); - if (prop == null) { - prop = getEscherProperty(opt, propId); - } - return prop; - } - -// private void handleEscapeInfo(Path2D path, byte segElem[], Iterator vertIter) { -// EscapeInfo ei = getEscapeInfo(segElem); -// switch (ei) { -// case EXTENSION: -// break; -// case ANGLE_ELLIPSE_TO: -// break; -// case ANGLE_ELLIPSE: -// break; -// case ARC_TO: -// break; -// case ARC: -// break; -// case CLOCKWISE_ARC_TO: -// break; -// case CLOCKWISE_ARC: -// break; -// case ELLIPTICAL_QUADRANT_X: -// break; -// case ELLIPTICAL_QUADRANT_Y: -// break; -// case QUADRATIC_BEZIER: -// break; -// case NO_FILL: -// break; -// case NO_LINE: -// break; -// case AUTO_LINE: -// break; -// case AUTO_CURVE: -// break; -// case CORNER_LINE: -// break; -// case CORNER_CURVE: -// break; -// case SMOOTH_LINE: -// break; -// case SMOOTH_CURVE: -// break; -// case SYMMETRIC_LINE: -// break; -// case SYMMETRIC_CURVE: -// break; -// case FREEFORM: -// break; -// case FILL_COLOR: -// break; -// case LINE_COLOR: -// break; -// default: -// break; -// } -// } - - private static PathInfo getPathInfo(byte elem[]) { - int elemUS = LittleEndian.getUShort(elem, 0); - int pathInfo = PATH_INFO.getValue(elemUS); - return PathInfo.valueOf(pathInfo); + path2D.transform(at); + + + return path2D; } - -// private static EscapeInfo getEscapeInfo(byte elem[]) { -// int elemUS = LittleEndian.getUShort(elem, 0); -// int escInfo = ESCAPE_INFO.getValue(elemUS); -// return EscapeInfo.valueOf(escInfo); -// } + + } diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShape.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShape.java index 085d617eb8..f1f2c22406 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShape.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShape.java @@ -358,17 +358,18 @@ public abstract class HSLFShape implements Shape { _sheet = sheet; } - Color getColor(short colorProperty, short opacityProperty, int defaultColor){ - AbstractEscherOptRecord opt = getEscherOptRecord(); - EscherSimpleProperty p = getEscherProperty(opt, colorProperty); - if(p == null && defaultColor == -1) return null; - - int val = (p == null) ? defaultColor : p.getPropertyValue(); - - EscherColorRef ecr = new EscherColorRef(val); - Color col = getColor(ecr); - if (col == null) { - return null; + Color getColor(short colorProperty, short opacityProperty){ + final AbstractEscherOptRecord opt = getEscherOptRecord(); + final EscherSimpleProperty colProp = getEscherProperty(opt, colorProperty); + final Color col; + if (colProp == null) { + col = Color.WHITE; + } else { + EscherColorRef ecr = new EscherColorRef(colProp.getPropertyValue()); + col = getColor(ecr); + if (col == null) { + return null; + } } double alpha = getAlpha(opacityProperty); diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSimpleShape.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSimpleShape.java index f9cf74c263..acabc68715 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSimpleShape.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSimpleShape.java @@ -164,7 +164,7 @@ public abstract class HSLFSimpleShape extends HSLFShape implements SimpleShapejava.awt.Color.black */ public Color getShadowColor(){ - Color clr = getColor(EscherProperties.SHADOWSTYLE__COLOR, EscherProperties.SHADOWSTYLE__OPACITY, -1); + Color clr = getColor(EscherProperties.SHADOWSTYLE__COLOR, EscherProperties.SHADOWSTYLE__OPACITY); return clr == null ? Color.black : clr; } diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShow.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShow.java index ecc360e135..2f0a116d68 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShow.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShow.java @@ -71,6 +71,7 @@ public final class HSLFSlideShow implements SlideShow
    Bit number