From: Yegor Kozlov Date: Sun, 19 Jul 2009 18:26:36 +0000 (+0000) Subject: Fix for saving custom and extended OOXML properties X-Git-Tag: REL_3_5-FINAL~74 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=5aba781802dddfde5ac42c84e6fbdb998428fcce;p=poi.git Fix for saving custom and extended OOXML properties git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@795587 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index 2af0bd1e9f..d9045f2be5 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -33,7 +33,8 @@ - 47535 - fixed WordExtractor to tolerate files with empty footnote block + 47540 - Fix for saving custom and extended OOXML properties + 47535 - Fixed WordExtractor to tolerate files with empty footnote block 47517 - Fixed ExtractorFactory to support .xltx and .dotx files 45556 - Support for extraction of footnotes from docx files 45555 - Support for extraction of endnotes from docx files diff --git a/src/examples/src/org/apache/poi/xssf/usermodel/examples/WorkbookProperties.java b/src/examples/src/org/apache/poi/xssf/usermodel/examples/WorkbookProperties.java new file mode 100755 index 0000000000..0e00c7d94b --- /dev/null +++ b/src/examples/src/org/apache/poi/xssf/usermodel/examples/WorkbookProperties.java @@ -0,0 +1,75 @@ +/* ==================================================================== + 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.xssf.usermodel.examples; + +import java.io.FileOutputStream; + +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.apache.poi.POIXMLProperties; + +/** + * How to set extended and custom properties + * + * @author Yegor Kozlov + */ +public class WorkbookProperties { + + public static void main(String[]args) throws Exception { + + XSSFWorkbook workbook = new XSSFWorkbook(); + workbook.createSheet("Workbook Properties"); + + POIXMLProperties props = workbook.getProperties(); + + /** + * Extended properties are a predefined set of metadata properties + * that are specifically applicable to Office Open XML documents. + * Extended properties consist of 24 simple properties and 3 complex properties stored in the + * part targeted by the relationship of type + */ + POIXMLProperties.ExtendedProperties ext = props.getExtendedProperties(); + ext.getUnderlyingProperties().setCompany("Apache Software Foundation"); + ext.getUnderlyingProperties().setTemplate("XSSF"); + + /** + * Custom properties enable users to define custom metadata properties + * through a set of well-defined data types. For example, a custom + * OLE Editor property of type string can be defined as follows: + * + * + * John Smith + * + */ + + POIXMLProperties.CustomProperties cust = props.getCustomProperties(); + org.openxmlformats.schemas.officeDocument.x2006.customProperties.CTProperty + property = cust.getUnderlyingProperties().addNewProperty(); + + property.setFmtid("{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"); + property.setPid(2); + property.setName("Editor"); + property.setLpwstr("John Smith"); + + FileOutputStream out = new FileOutputStream("workbook.xlsx"); + workbook.write(out); + out.close(); + + } + + +} \ No newline at end of file diff --git a/src/ooxml/java/org/apache/poi/POIXMLDocument.java b/src/ooxml/java/org/apache/poi/POIXMLDocument.java index 03a9466d9d..ace5d01986 100644 --- a/src/ooxml/java/org/apache/poi/POIXMLDocument.java +++ b/src/ooxml/java/org/apache/poi/POIXMLDocument.java @@ -157,9 +157,13 @@ public abstract class POIXMLDocument extends POIXMLDocumentPart{ * Get the document properties. This gives you access to the * core ooxml properties, and the extended ooxml properties. */ - public POIXMLProperties getProperties() throws OpenXML4JException, IOException, XmlException { + public POIXMLProperties getProperties() { if(properties == null) { - properties = new POIXMLProperties(pkg); + try { + properties = new POIXMLProperties(pkg); + } catch (Exception e){ + throw new POIXMLException(e); + } } return properties; } @@ -197,6 +201,9 @@ public abstract class POIXMLDocument extends POIXMLDocumentPart{ //force all children to commit their changes into the underlying OOXML Package onSave(); + //save extended and custom properties + getProperties().commit(); + getPackage().save(stream); } diff --git a/src/ooxml/java/org/apache/poi/POIXMLProperties.java b/src/ooxml/java/org/apache/poi/POIXMLProperties.java index 9729309bcd..6966149668 100644 --- a/src/ooxml/java/org/apache/poi/POIXMLProperties.java +++ b/src/ooxml/java/org/apache/poi/POIXMLProperties.java @@ -17,12 +17,17 @@ package org.apache.poi; import java.io.IOException; +import java.io.OutputStream; +import java.util.Map; +import java.util.HashMap; import org.apache.poi.openxml4j.exceptions.OpenXML4JException; -import org.apache.poi.openxml4j.opc.OPCPackage; -import org.apache.poi.openxml4j.opc.PackageRelationshipCollection; +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; +import org.apache.poi.openxml4j.opc.*; import org.apache.poi.openxml4j.opc.internal.PackagePropertiesPart; import org.apache.xmlbeans.XmlException; +import org.apache.xmlbeans.XmlOptions; +import org.openxmlformats.schemas.officeDocument.x2006.extendedProperties.PropertiesDocument; /** * Wrapper around the two different kinds of OOXML properties @@ -33,8 +38,22 @@ public class POIXMLProperties { private CoreProperties core; private ExtendedProperties ext; private CustomProperties cust; - - public POIXMLProperties(OPCPackage docPackage) throws IOException, OpenXML4JException, XmlException { + + private PackagePart extPart; + private PackagePart custPart; + + + private static final org.openxmlformats.schemas.officeDocument.x2006.extendedProperties.PropertiesDocument NEW_EXT_INSTANCE; + private static final org.openxmlformats.schemas.officeDocument.x2006.customProperties.PropertiesDocument NEW_CUST_INSTANCE; + static { + NEW_EXT_INSTANCE = org.openxmlformats.schemas.officeDocument.x2006.extendedProperties.PropertiesDocument.Factory.newInstance(); + NEW_EXT_INSTANCE.addNewProperties(); + + NEW_CUST_INSTANCE = org.openxmlformats.schemas.officeDocument.x2006.customProperties.PropertiesDocument.Factory.newInstance(); + NEW_CUST_INSTANCE.addNewProperties(); + } + + public POIXMLProperties(OPCPackage docPackage) throws IOException, OpenXML4JException, XmlException { this.pkg = docPackage; // Core properties @@ -44,24 +63,28 @@ public class POIXMLProperties { PackageRelationshipCollection extRel = pkg.getRelationshipsByType(POIXMLDocument.EXTENDED_PROPERTIES_REL_TYPE); if(extRel.size() == 1) { - org.openxmlformats.schemas.officeDocument.x2006.extendedProperties.PropertiesDocument props = org.openxmlformats.schemas.officeDocument.x2006.extendedProperties.PropertiesDocument.Factory.parse( - pkg.getPart( extRel.getRelationship(0) ).getInputStream() + extPart = pkg.getPart( extRel.getRelationship(0)); + org.openxmlformats.schemas.officeDocument.x2006.extendedProperties.PropertiesDocument props = org.openxmlformats.schemas.officeDocument.x2006.extendedProperties.PropertiesDocument.Factory.parse( + extPart.getInputStream() ); ext = new ExtendedProperties(props); } else { - ext = new ExtendedProperties(org.openxmlformats.schemas.officeDocument.x2006.extendedProperties.PropertiesDocument.Factory.newInstance()); - } + extPart = null; + ext = new ExtendedProperties((org.openxmlformats.schemas.officeDocument.x2006.extendedProperties.PropertiesDocument)NEW_EXT_INSTANCE.copy()); + } // Custom properties PackageRelationshipCollection custRel = pkg.getRelationshipsByType(POIXMLDocument.CUSTOM_PROPERTIES_REL_TYPE); if(custRel.size() == 1) { - org.openxmlformats.schemas.officeDocument.x2006.customProperties.PropertiesDocument props = org.openxmlformats.schemas.officeDocument.x2006.customProperties.PropertiesDocument.Factory.parse( - pkg.getPart( custRel.getRelationship(0) ).getInputStream() + custPart = pkg.getPart( custRel.getRelationship(0)); + org.openxmlformats.schemas.officeDocument.x2006.customProperties.PropertiesDocument props = org.openxmlformats.schemas.officeDocument.x2006.customProperties.PropertiesDocument.Factory.parse( + custPart.getInputStream() ); cust = new CustomProperties(props); } else { - cust = new CustomProperties(org.openxmlformats.schemas.officeDocument.x2006.customProperties.PropertiesDocument.Factory.newInstance()); + custPart = null; + cust = new CustomProperties((org.openxmlformats.schemas.officeDocument.x2006.customProperties.PropertiesDocument)NEW_CUST_INSTANCE.copy()); } } @@ -87,11 +110,44 @@ public class POIXMLProperties { } /** - * Writes out the ooxml properties into the supplied, - * new Package + * Commit changes to the underlying OPC package */ - public void write(OPCPackage pkg) { - // TODO + public void commit() throws IOException{ + + if(extPart == null && !NEW_EXT_INSTANCE.toString().equals(ext.props.toString())){ + try { + PackagePartName prtname = PackagingURIHelper.createPartName("/docProps/app.xml"); + pkg.addRelationship(prtname, TargetMode.INTERNAL, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties"); + extPart = pkg.createPart(prtname, "application/vnd.openxmlformats-officedocument.extended-properties+xml"); + } catch (InvalidFormatException e){ + throw new POIXMLException(e); + } + } + if(custPart == null && !NEW_CUST_INSTANCE.toString().equals(cust.props.toString())){ + try { + PackagePartName prtname = PackagingURIHelper.createPartName("/docProps/custom.xml"); + pkg.addRelationship(prtname, TargetMode.INTERNAL, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties"); + custPart = pkg.createPart(prtname, "application/vnd.openxmlformats-officedocument.custom-properties+xml"); + } catch (InvalidFormatException e){ + throw new POIXMLException(e); + } + } + if(extPart != null){ + XmlOptions xmlOptions = new XmlOptions(POIXMLDocumentPart.DEFAULT_XML_OPTIONS); + + Map map = new HashMap(); + map.put("http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes", "vt"); + xmlOptions.setSaveSuggestedPrefixes(map); + + OutputStream out = extPart.getOutputStream(); + ext.props.save(out, xmlOptions); + out.close(); + } + if(custPart != null){ + OutputStream out = custPart.getOutputStream(); + cust.props.save(out, POIXMLDocumentPart.DEFAULT_XML_OPTIONS); + out.close(); + } } /** @@ -122,10 +178,6 @@ public class POIXMLProperties { private org.openxmlformats.schemas.officeDocument.x2006.extendedProperties.PropertiesDocument props; private ExtendedProperties(org.openxmlformats.schemas.officeDocument.x2006.extendedProperties.PropertiesDocument props) { this.props = props; - - if(props.getProperties() == null) { - props.addNewProperties(); - } } public org.openxmlformats.schemas.officeDocument.x2006.extendedProperties.CTProperties getUnderlyingProperties() { @@ -140,10 +192,6 @@ public class POIXMLProperties { private org.openxmlformats.schemas.officeDocument.x2006.customProperties.PropertiesDocument props; private CustomProperties(org.openxmlformats.schemas.officeDocument.x2006.customProperties.PropertiesDocument props) { this.props = props; - - if(props.getProperties() == null) { - props.addNewProperties(); - } } public org.openxmlformats.schemas.officeDocument.x2006.customProperties.CTProperties getUnderlyingProperties() { diff --git a/src/ooxml/testcases/org/apache/poi/TestPOIXMLProperties.java b/src/ooxml/testcases/org/apache/poi/TestPOIXMLProperties.java new file mode 100755 index 0000000000..4349600ad6 --- /dev/null +++ b/src/ooxml/testcases/org/apache/poi/TestPOIXMLProperties.java @@ -0,0 +1,134 @@ +/* ==================================================================== + 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; + +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.apache.poi.xssf.XSSFTestDataSamples; + +import junit.framework.TestCase; + +/** + * Test setting extended and custom OOXML properties + */ +public class TestPOIXMLProperties extends TestCase { + public void testWorkbookExtendedProperties() throws Exception { + XSSFWorkbook workbook = new XSSFWorkbook(); + POIXMLProperties props = workbook.getProperties(); + assertNotNull(props); + + org.apache.poi.POIXMLProperties.ExtendedProperties properties = + props.getExtendedProperties(); + + org.openxmlformats.schemas.officeDocument.x2006.extendedProperties.CTProperties + ctProps = properties.getUnderlyingProperties(); + + + String appVersion = "3.5 beta"; + String application = "POI"; + + ctProps.setApplication(application); + ctProps.setAppVersion(appVersion); + + ctProps = null; + properties = null; + props = null; + + XSSFWorkbook newWorkbook = + XSSFTestDataSamples.writeOutAndReadBack(workbook); + + assertTrue(workbook != newWorkbook); + + + POIXMLProperties newProps = newWorkbook.getProperties(); + assertNotNull(newProps); + org.apache.poi.POIXMLProperties.ExtendedProperties newProperties = + newProps.getExtendedProperties(); + + org.openxmlformats.schemas.officeDocument.x2006.extendedProperties.CTProperties + newCtProps = newProperties.getUnderlyingProperties(); + + assertEquals(application, newCtProps.getApplication()); + assertEquals(appVersion, newCtProps.getAppVersion()); + + + } + + + public void testWorkbookCustomProperties() throws Exception { + XSSFWorkbook workbook = new XSSFWorkbook(); + POIXMLProperties props = workbook.getProperties(); + assertNotNull(props); + + org.apache.poi.POIXMLProperties.CustomProperties properties = + props.getCustomProperties(); + + org.openxmlformats.schemas.officeDocument.x2006.customProperties.CTProperties + ctProps = properties.getUnderlyingProperties(); + + + org.openxmlformats.schemas.officeDocument.x2006.customProperties.CTProperty + property = ctProps.addNewProperty(); + + + String fmtid = + "{A1A1A1A1A1A1A1A1-A1A1A1A1-A1A1A1A1-A1A1A1A1-A1A1A1A1A1A1A1A1}"; + int pId = 1; + String name = "testProperty"; + String stringValue = "testValue"; + + + property.setFmtid(fmtid); + property.setPid(pId); + property.setName(name); + property.setBstr(stringValue); + + + property = null; + ctProps = null; + properties = null; + props = null; + + XSSFWorkbook newWorkbook = + XSSFTestDataSamples.writeOutAndReadBack(workbook); + + assertTrue(workbook != newWorkbook); + + + POIXMLProperties newProps = newWorkbook.getProperties(); + assertNotNull(newProps); + org.apache.poi.POIXMLProperties.CustomProperties newProperties = + newProps.getCustomProperties(); + + org.openxmlformats.schemas.officeDocument.x2006.customProperties.CTProperties + newCtProps = newProperties.getUnderlyingProperties(); + + assertEquals(1, newCtProps.getPropertyArray().length); + + + org.openxmlformats.schemas.officeDocument.x2006.customProperties.CTProperty + newpProperty = newCtProps.getPropertyArray()[0]; + + assertEquals(fmtid, newpProperty.getFmtid()); + assertEquals(pId, newpProperty.getPid()); + assertEquals(name, newpProperty.getName()); + assertEquals(stringValue, newpProperty.getBstr()); + + + } +} \ No newline at end of file