All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.odftoolkit.odfdom.doc.OdfDocument Maven / Gradle / Ivy

/**
 * **********************************************************************
 *
 * 

DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER * *

Copyright 2008, 2010 Oracle and/or its affiliates. All rights reserved. * *

Use is subject to license terms. * *

Licensed 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. You can also obtain a copy of the License at * http://odftoolkit.org/docs/license.txt * *

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.odftoolkit.odfdom.doc; import java.io.File; import java.io.InputStream; import java.io.OutputStream; import java.net.URI; import java.util.ArrayList; import java.util.Calendar; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.xml.datatype.DatatypeConfigurationException; import javax.xml.datatype.DatatypeFactory; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import org.odftoolkit.odfdom.doc.table.OdfTable; import org.odftoolkit.odfdom.dom.OdfContentDom; import org.odftoolkit.odfdom.dom.OdfDocumentNamespace; import org.odftoolkit.odfdom.dom.OdfMetaDom; import org.odftoolkit.odfdom.dom.OdfSchemaConstraint; import org.odftoolkit.odfdom.dom.OdfSchemaDocument; import org.odftoolkit.odfdom.dom.attribute.text.TextAnchorTypeAttribute; import org.odftoolkit.odfdom.dom.element.draw.DrawPageElement; import org.odftoolkit.odfdom.dom.element.office.OfficeAnnotationElement; import org.odftoolkit.odfdom.dom.element.office.OfficeBodyElement; import org.odftoolkit.odfdom.dom.element.table.TableTableCellElement; import org.odftoolkit.odfdom.dom.element.table.TableTableElement; import org.odftoolkit.odfdom.dom.element.text.TextPElement; import org.odftoolkit.odfdom.dom.style.OdfStyleFamily; import org.odftoolkit.odfdom.dom.style.props.OdfStyleProperty; import org.odftoolkit.odfdom.dom.style.props.OdfTextProperties; import org.odftoolkit.odfdom.incubator.doc.draw.OdfDrawFrame; import org.odftoolkit.odfdom.incubator.doc.draw.OdfDrawImage; import org.odftoolkit.odfdom.incubator.doc.office.OdfOfficeStyles; import org.odftoolkit.odfdom.incubator.doc.style.OdfDefaultStyle; import org.odftoolkit.odfdom.incubator.meta.OdfOfficeMeta; import org.odftoolkit.odfdom.incubator.search.SelectionManager; import org.odftoolkit.odfdom.pkg.MediaType; import org.odftoolkit.odfdom.pkg.OdfElement; import org.odftoolkit.odfdom.pkg.OdfName; import org.odftoolkit.odfdom.pkg.OdfPackage; import org.odftoolkit.odfdom.pkg.OdfValidationException; import org.odftoolkit.odfdom.type.Duration; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.ErrorHandler; import org.xml.sax.SAXException; /** This abstract class is representing one of the possible ODF documents. */ public abstract class OdfDocument extends OdfSchemaDocument { // Static parts of file references private static final String SLASH = "/"; private OdfMediaType mMediaType; private OdfOfficeMeta mOfficeMeta; private long documentOpeningTime; private static final Pattern CONTROL_CHAR_PATTERN = Pattern.compile("\\p{Cntrl}"); private static final String EMPTY_STRING = ""; private Calendar mCreationDate; private static final String FORMER_OPEN_OFFICE_VERSION = "StarOffice/8$Win32 OpenOffice.org_project/680m18$Build-9161"; protected Boolean mHasCollaboration = null; // Using static factory instead of constructor protected OdfDocument(OdfPackage pkg, String internalPath, OdfMediaType mediaType) throws SAXException { super(pkg, internalPath, mediaType.getMediaTypeString()); mMediaType = mediaType; // set document opening time. documentOpeningTime = System.currentTimeMillis(); } /** This enum contains all possible media types of OpenDocument documents. */ public enum OdfMediaType implements MediaType { CHART("application/vnd.oasis.opendocument.chart", "odc"), CHART_TEMPLATE("application/vnd.oasis.opendocument.chart-template", "otc"), FORMULA("application/vnd.oasis.opendocument.formula", "odf"), FORMULA_TEMPLATE("application/vnd.oasis.opendocument.formula-template", "otf"), DATABASE_FRONT_END("application/vnd.oasis.opendocument.base", "odb"), GRAPHICS("application/vnd.oasis.opendocument.graphics", "odg"), GRAPHICS_TEMPLATE("application/vnd.oasis.opendocument.graphics-template", "otg"), IMAGE("application/vnd.oasis.opendocument.image", "odi"), IMAGE_TEMPLATE("application/vnd.oasis.opendocument.image-template", "oti"), PRESENTATION("application/vnd.oasis.opendocument.presentation", "odp"), PRESENTATION_TEMPLATE("application/vnd.oasis.opendocument.presentation-template", "otp"), SPREADSHEET("application/vnd.oasis.opendocument.spreadsheet", "ods"), SPREADSHEET_TEMPLATE("application/vnd.oasis.opendocument.spreadsheet-template", "ots"), TEXT("application/vnd.oasis.opendocument.text", "odt"), TEXT_MASTER("application/vnd.oasis.opendocument.text-master", "odm"), TEXT_TEMPLATE("application/vnd.oasis.opendocument.text-template", "ott"), TEXT_MASTER_TEMPLATE("application/vnd.oasis.opendocument.text-master-template", "otm"), TEXT_WEB("application/vnd.oasis.opendocument.text-web", "oth"); private final String mMediaType; private final String mSuffix; OdfMediaType(String mediaType, String suffix) { this.mMediaType = mediaType; this.mSuffix = suffix; } /** @return the mediatype String of this document */ @Override public String getMediaTypeString() { return mMediaType; } /** @return the ODF filesuffix of this document */ @Override public String getSuffix() { return mSuffix; } /** * @param mediaType string defining an ODF document * @return the according OdfMediatype encapsulating the given string and the suffix */ public static OdfMediaType getOdfMediaType(String mediaType) { OdfMediaType odfMediaType = null; if (mediaType != null) { String mediaTypeShort = mediaType.substring(mediaType.lastIndexOf(".") + 1, mediaType.length()); mediaTypeShort = mediaTypeShort.replace('-', '_').toUpperCase(); try { odfMediaType = OdfMediaType.valueOf(mediaTypeShort); } catch (IllegalArgumentException e) { throw new IllegalArgumentException( "Given mediaType '" + mediaType + "' is not an ODF mediatype!"); } } return odfMediaType; } } /** * Loads the ODF root document from the given Resource. * *

NOTE: Initial meta data (like the document creation time) will be added in this method. * * @param res a resource containing a package with a root document * @param odfMediaType the media type of the root document * @return the OpenDocument document or NULL if the media type is not supported by ODFDOM. * @throws java.lang.Exception - if the document could not be created. */ protected static OdfDocument loadTemplate(Resource res, OdfMediaType odfMediaType) throws Exception { InputStream in = res.createInputStream(); OdfPackage pkg = null; try { pkg = OdfPackage.loadPackage(in); } finally { in.close(); } OdfDocument newDocument = newDocument(pkg, ROOT_DOCUMENT_PATH, odfMediaType, Boolean.FALSE); // add creation time, the metadata have to be explicitly set newDocument.mCreationDate = Calendar.getInstance(); return newDocument; } /** * Loads the ODF root document from the given Resource. * *

NOTE: Initial meta data (like the document creation time) will be added in this method. * * @param res a resource containing a package with a root document * @param odfMediaType the media type of the root document * @param enableCollaboration - user changes equivalent for creating this document are gathered * @return the OpenDocument document or NULL if the media type is not supported by ODFDOM. * @throws java.lang.Exception - if the document could not be created. */ protected static OdfDocument loadTemplate( Resource res, OdfMediaType odfMediaType, Boolean enableCollaboration) throws Exception { InputStream in = res.createInputStream(); OdfPackage pkg = null; try { pkg = OdfPackage.loadPackage(in); } finally { in.close(); } OdfDocument newDocument = newDocument(pkg, ROOT_DOCUMENT_PATH, odfMediaType, enableCollaboration); // add creation time, the metadata have to be explicitly set newDocument.mCreationDate = Calendar.getInstance(); return newDocument; } /** * Loads the ODF root document from the ODF package provided by its path. * *

OdfDocument relies on the file being available for read access over the whole life-cycle of * OdfDocument. * * @param documentPath - the path from where the document can be loaded * @return the OpenDocument from the given path or NULL if the media type is not supported by * ODFDOM. * @throws java.lang.Exception - if the document could not be created. */ public static OdfDocument loadDocument(String documentPath) throws Exception { return loadDocument(OdfPackage.loadPackage(documentPath)); } /** * Loads the ODF root document from the ODF package provided by a Stream. * *

Since an InputStream does not provide the arbitrary (non sequential) read access needed by * OdfDocument, the InputStream is cached. This usually takes more time compared to the other * createInternalDocument methods. An advantage of caching is that there are no problems * overwriting an input file. * * @param inStream - the InputStream of the ODF document. * @param configuration - key/value pairs of user given run-time settings (configuration) * @return the document created from the given InputStream * @throws java.lang.Exception - if the document could not be created. */ public static OdfDocument loadDocument(InputStream inStream, Map configuration) throws Exception { return loadDocument(OdfPackage.loadPackage(inStream, configuration)); } /** * Loads the ODF root document from the ODF package provided by a Stream. * *

Since an InputStream does not provide the arbitrary (non sequential) read access needed by * OdfDocument, the InputStream is cached. This usually takes more time compared to the other * createInternalDocument methods. An advantage of caching is that there are no problems * overwriting an input file. * * @param inStream - the InputStream of the ODF document. * @param configuration - key/value pairs of user given run-time settings (configuration) * @param enableCollaboration - user changes equivalent for creating this document are gathered * @return the document created from the given InputStream * @throws java.lang.Exception - if the document could not be created. */ public static OdfDocument loadDocument( InputStream inStream, Map configuration, Boolean enableCollaboration) throws Exception { return loadDocument(OdfPackage.loadPackage(inStream, configuration), enableCollaboration); } /** * Loads the ODF root document from the ODF package provided by a Stream. * *

Since an InputStream does not provide the arbitrary (non sequential) read access needed by * OdfDocument, the InputStream is cached. This usually takes more time compared to the other * createInternalDocument methods. An advantage of caching is that there are no problems * overwriting an input file. * * @param inStream - the InputStream of the ODF document. * @return the document created from the given InputStream * @throws java.lang.Exception - if the document could not be created. */ public static OdfDocument loadDocument(InputStream inStream) throws Exception { return loadDocument(OdfPackage.loadPackage(inStream)); } /** * Loads the ODF root document from the ODF package provided by a Stream. * *

Since an InputStream does not provide the arbitrary (non sequential) read access needed by * OdfDocument, the InputStream is cached. This usually takes more time compared to the other * createInternalDocument methods. An advantage of caching is that there are no problems * overwriting an input file. * * @param inStream - the InputStream of the ODF document. * @param enableCollaboration - user changes equivalent for creating this document are gathered * @return the document created from the given InputStream * @throws java.lang.Exception - if the document could not be created. */ public static OdfDocument loadDocument(InputStream inStream, Boolean enableCollaboration) throws Exception { return loadDocument(OdfPackage.loadPackage(inStream), enableCollaboration); } /** * Loads the ODF root document from the ODF package provided as a File. * * @param file - a file representing the ODF document. * @return the document created from the given File * @throws java.lang.Exception - if the document could not be created. */ public static OdfDocument loadDocument(File file) throws Exception { return loadDocument(OdfPackage.loadPackage(file)); } /** * Loads the ODF root document from the ODF package. * * @param odfPackage - the ODF package containing the ODF document. * @return the root document of the given OdfPackage * @throws java.lang.Exception - if the ODF document could not be created. */ public static OdfDocument loadDocument(OdfPackage odfPackage) throws Exception { return loadDocument(odfPackage, ROOT_DOCUMENT_PATH); } /** * Loads the ODF root document from the ODF package. * * @param odfPackage - the ODF package containing the ODF document. * @param enableCollaboration - user changes equivalent for creating this document are gathered * @return the root document of the given OdfPackage * @throws java.lang.Exception - if the ODF document could not be created. */ public static OdfDocument loadDocument(OdfPackage odfPackage, Boolean enableCollaboration) throws Exception { return loadDocument(odfPackage, ROOT_DOCUMENT_PATH, enableCollaboration); } /** * Creates an OdfDocument from the OpenDocument provided by an ODF package. * * @param odfPackage - the ODF package containing the ODF document. * @param internalPath - the path to the ODF document relative to the package root, or an empty * String for the root document. * @return the root document of the given OdfPackage * @throws java.lang.Exception - if the ODF document could not be created. */ public static OdfDocument loadDocument(OdfPackage odfPackage, String internalPath) throws Exception { return loadDocument(odfPackage, internalPath, Boolean.FALSE); } /** * Creates an OdfDocument from the OpenDocument provided by an ODF package. * * @param odfPackage - the ODF package containing the ODF document. * @param internalPath - the path to the ODF document relative to the package root, or an empty * String for the root document. * @param enableCollaboration - user changes equivalent for creating this document are gathered * @return the root document of the given OdfPackage * @throws java.lang.Exception - if the ODF document could not be created. */ public static OdfDocument loadDocument( OdfPackage odfPackage, String internalPath, Boolean enableCollaboration) throws Exception { String documentMediaType = odfPackage.getMediaTypeString(internalPath); OdfMediaType odfMediaType = null; try { odfMediaType = OdfMediaType.getOdfMediaType(documentMediaType); } catch (IllegalArgumentException e) { // the returned NULL will be taking care of afterwards } if (odfMediaType == null) { ErrorHandler errorHandler = odfPackage.getErrorHandler(); if (documentMediaType != null) { Matcher matcherCTRL = CONTROL_CHAR_PATTERN.matcher(documentMediaType); if (matcherCTRL.find()) { documentMediaType = matcherCTRL.replaceAll(EMPTY_STRING); } } OdfValidationException ve = new OdfValidationException( OdfSchemaConstraint.DOCUMENT_WITHOUT_ODF_MIMETYPE, internalPath, documentMediaType); if (errorHandler != null) { errorHandler.fatalError(ve); } throw ve; } return newDocument(odfPackage, internalPath, odfMediaType, enableCollaboration); } // return null if the media type can not be recognized. private static OdfDocument loadDocumentFromTemplate(OdfMediaType odfMediaType) throws Exception { final Resource documentTemplate; switch (odfMediaType) { case TEXT: case TEXT_TEMPLATE: case TEXT_MASTER: case TEXT_MASTER_TEMPLATE: case TEXT_WEB: documentTemplate = OdfTextDocument.EMPTY_TEXT_DOCUMENT_RESOURCE; break; case SPREADSHEET: case SPREADSHEET_TEMPLATE: documentTemplate = OdfSpreadsheetDocument.EMPTY_SPREADSHEET_DOCUMENT_RESOURCE; break; case PRESENTATION: case PRESENTATION_TEMPLATE: documentTemplate = OdfPresentationDocument.EMPTY_PRESENTATION_DOCUMENT_RESOURCE; break; case GRAPHICS: case GRAPHICS_TEMPLATE: documentTemplate = OdfGraphicsDocument.EMPTY_GRAPHICS_DOCUMENT_RESOURCE; break; case CHART: case CHART_TEMPLATE: documentTemplate = OdfChartDocument.EMPTY_CHART_DOCUMENT_RESOURCE; break; case IMAGE: case IMAGE_TEMPLATE: documentTemplate = OdfImageDocument.EMPTY_IMAGE_DOCUMENT_RESOURCE; break; default: documentTemplate = null; throw new IllegalArgumentException( "Given mediaType '" + odfMediaType.mMediaType + "' is not yet supported!"); } return loadTemplate(documentTemplate, odfMediaType); } /** * Creates one of the ODF documents based a given mediatype. * * @param odfMediaType The ODF Mediatype of the ODF document to be created. * @param enableCollaboration - user changes equivalent for creating this document are gathered * @return The ODF document, which mediatype dependends on the parameter or NULL if media type * were not supported. */ private static OdfDocument newDocument( OdfPackage pkg, String internalPath, OdfMediaType odfMediaType, Boolean enableCollaboration) throws SAXException { OdfDocument newDoc = null; switch (odfMediaType) { case TEXT: newDoc = new OdfTextDocument( pkg, internalPath, OdfTextDocument.OdfMediaType.TEXT, enableCollaboration); break; case TEXT_TEMPLATE: newDoc = new OdfTextDocument( pkg, internalPath, OdfTextDocument.OdfMediaType.TEXT_TEMPLATE, enableCollaboration); break; case TEXT_MASTER: newDoc = new OdfTextDocument( pkg, internalPath, OdfTextDocument.OdfMediaType.TEXT_MASTER, enableCollaboration); break; case TEXT_MASTER_TEMPLATE: newDoc = new OdfTextDocument( pkg, internalPath, OdfTextDocument.OdfMediaType.TEXT_MASTER_TEMPLATE, enableCollaboration); break; case TEXT_WEB: newDoc = new OdfTextDocument( pkg, internalPath, OdfTextDocument.OdfMediaType.TEXT_WEB, enableCollaboration); break; case SPREADSHEET: newDoc = new OdfSpreadsheetDocument( pkg, internalPath, OdfSpreadsheetDocument.OdfMediaType.SPREADSHEET); break; case SPREADSHEET_TEMPLATE: newDoc = new OdfSpreadsheetDocument( pkg, internalPath, OdfSpreadsheetDocument.OdfMediaType.SPREADSHEET_TEMPLATE); break; case PRESENTATION: newDoc = new OdfPresentationDocument( pkg, internalPath, OdfPresentationDocument.OdfMediaType.PRESENTATION); break; case PRESENTATION_TEMPLATE: newDoc = new OdfPresentationDocument( pkg, internalPath, OdfPresentationDocument.OdfMediaType.PRESENTATION_TEMPLATE); break; case GRAPHICS: newDoc = new OdfGraphicsDocument(pkg, internalPath, OdfGraphicsDocument.OdfMediaType.GRAPHICS); break; case GRAPHICS_TEMPLATE: newDoc = new OdfGraphicsDocument( pkg, internalPath, OdfGraphicsDocument.OdfMediaType.GRAPHICS_TEMPLATE); break; case CHART: newDoc = new OdfChartDocument(pkg, internalPath, OdfChartDocument.OdfMediaType.CHART); break; case CHART_TEMPLATE: newDoc = new OdfChartDocument(pkg, internalPath, OdfChartDocument.OdfMediaType.CHART_TEMPLATE); break; case IMAGE: newDoc = new OdfImageDocument(pkg, internalPath, OdfImageDocument.OdfMediaType.IMAGE); break; case IMAGE_TEMPLATE: newDoc = new OdfImageDocument(pkg, internalPath, OdfImageDocument.OdfMediaType.IMAGE_TEMPLATE); break; default: newDoc = null; throw new IllegalArgumentException( "Given mediaType '" + odfMediaType.mMediaType + "' is not yet supported!"); } // returning null if MediaType is not supported return newDoc; } /** * Returns an embedded OdfPackageDocument from the given package path. * * @param documentPath to the ODF document within the package. The path is relative the current * ODF document path. * @return an embedded OdfPackageDocument */ @Override public OdfDocument loadSubDocument(String documentPath) { return (OdfDocument) super.loadSubDocument(documentPath); } /** * Method returns all embedded OdfPackageDocuments, which match a valid OdfMediaType, of the * current OdfPackageDocument. Note: The root document is not part of the returned collection. * * @return a map with all embedded documents and their paths of the current OdfPackageDocument */ public Map loadSubDocuments() { return loadSubDocuments(null); } /** * Method returns all embedded OdfPackageDocuments of sthe current OdfPackageDocument matching the * according MediaType. This is done by matching the subfolder entries of the manifest file with * the given OdfMediaType. * * @param desiredMediaType media type of the documents to be returned (used as a filter). * @return embedded documents of the current OdfPackageDocument matching the given media type */ public Map loadSubDocuments(OdfMediaType desiredMediaType) { String wantedMediaString = null; if (desiredMediaType != null) { wantedMediaString = desiredMediaType.getMediaTypeString(); } Map embeddedObjectsMap = new HashMap(); // check manifest for current embedded OdfPackageDocuments Set manifestEntries = mPackage.getFilePaths(); for (String path : manifestEntries) { // any directory that is not the root document "/" if (path.length() > 1 && path.endsWith(SLASH)) { String entryMediaType = mPackage.getFileEntry(path).getMediaTypeString(); // if the entry is a document (directory has mediaType) if (entryMediaType != null) { // if a specific ODF mediatype was requested if (wantedMediaString != null) { // test if the desired mediatype matches the current if (entryMediaType.equals(wantedMediaString)) { path = normalizeDocumentPath(path); embeddedObjectsMap.put(path, (OdfDocument) mPackage.loadDocument(path)); } } else { // test if any ODF mediatype matches the current for (OdfMediaType mediaType : OdfMediaType.values()) { if (entryMediaType.equals(mediaType.getMediaTypeString())) { embeddedObjectsMap.put(path, (OdfDocument) mPackage.loadDocument(path)); } } } } } } return embeddedObjectsMap; } /** * Sets the media type of the OdfDocument * * @param odfMediaType media type to be set */ protected void setOdfMediaType(OdfMediaType odfMediaType) { mMediaType = odfMediaType; super.setMediaTypeString(odfMediaType.getMediaTypeString()); } /** Gets the media type of the OdfDocument */ protected OdfMediaType getOdfMediaType() { return mMediaType; } /** * Get the meta data feature instance of the current document * * @return the meta data feature instance which represent office:meta in the meta.xml */ public OdfOfficeMeta getOfficeMetadata() { if (mOfficeMeta == null) { try { OdfMetaDom metaDom = getMetaDom(); mOfficeMeta = new OdfOfficeMeta(metaDom); } catch (Exception ex) { Logger.getLogger(OdfDocument.class.getName()).log(Level.SEVERE, null, ex); } } return mOfficeMeta; } /** * Save the document to an OutputStream. Delegate to the root document and save possible embedded * OdfDocuments. * *

If the input file has been cached (this is the case when loading from an InputStream), the * input file can be overwritten. * *

If not, the OutputStream may not point to the input file! Otherwise this will result in * unwanted behaviour and broken files. * *

When save the embedded document to a stand alone document, all the file entries of the * embedded document will be copied to a new document package. If the embedded document is outside * of the current document directory, you have to embed it to the sub directory and refresh the * link of the embedded document. you should reload it from the stream to get the saved embedded * document. * * @param out - the OutputStream to write the file to * @throws java.lang.Exception if the document could not be saved */ public void save(OutputStream out) throws Exception { updateMetaData(); if (!isRootDocument()) { OdfDocument newDoc = loadDocumentFromTemplate(getOdfMediaType()); newDoc.insertDocument(this, ROOT_DOCUMENT_PATH); newDoc.updateMetaData(); newDoc.mPackage.save(out); // ToDo: (Issue 219 - PackageRefactoring) - Return the document, when not closing! // Should we close the sources now? User will never receive the open package! } else { mPackage.save(out); } } /** * Save the document to a given file. * *

If the input file has been cached (this is the case when loading from an InputStream), the * input file can be overwritten. * *

Otherwise it's allowed to overwrite the input file as long as the same path name is used * that was used for loading (no symbolic link foo2.odt pointing to the loaded file foo1.odt, no * network path X:\foo.odt pointing to the loaded file D:\foo.odt). * *

When saving the embedded document to a stand alone document, all files of the embedded * document will be copied to a new document package. If the embedded document is outside of the * current document directory, you have to embed it to the sub directory and refresh the link of * the embedded document. You should reload it from the given file to get the saved embedded * document. * * @param file - the file to save the document * @throws java.lang.Exception if the document could not be saved */ @Override public void save(File file) throws Exception { updateMetaData(); if (!isRootDocument()) { OdfDocument newDoc = loadDocumentFromTemplate(getOdfMediaType()); newDoc.insertDocument(this, ROOT_DOCUMENT_PATH); newDoc.updateMetaData(); newDoc.mPackage.save(file); // ToDo: (Issue 219 - PackageRefactoring) - Return the document, when not closing! // Should we close the sources now? User will never receive the open package! } else { this.mPackage.save(file); } } /** * Close the document and release all temporary created data. After execution of this method, this * class is no longer usable. Do this as the last action to free resources. Closing an already * closed document has no effect. */ @Override public void close() { // set all member variables explicit to null mMediaType = null; mOfficeMeta = null; super.close(); } /** * Get the content root of a document. * *

You may prefer to use the getContentRoot methods of subclasses of OdfDocument. Their return * parameters are already casted to respective subclasses of OdfElement. * * @param the type of the content root, depend on the document type * @return the child element of office:body, e.g. office:text for text docs * @throws Exception if the file DOM could not be created. */ @SuppressWarnings("unchecked") T getContentRoot(Class clazz) throws Exception { OdfElement contentRoot = getContentDom().getRootElement(); OfficeBodyElement contentBody = OdfElement.findFirstChildNode(OfficeBodyElement.class, contentRoot); NodeList childs = contentBody.getChildNodes(); for (int i = 0; i < childs.getLength(); i++) { Node cur = childs.item(i); if ((cur != null) && clazz.isInstance(cur)) { return (T) cur; } } return null; } /** * Get the content root of a document. * *

You may prefer to use the getContentRoot methods of subclasses of OdfDocument. * * @return the child element of office:body, e.g. office:text for text docs * @throws Exception if the file DOM could not be created. */ public OdfElement getContentRoot() throws Exception { return getContentRoot(OdfElement.class); } @Override public String toString() { return "\n" + getMediaTypeString() + " - ID: " + this.hashCode() + " " + getPackage().getBaseURI(); } /** * Insert an Image from the specified uri to the end of the OdfDocument. * * @param imageUri The URI of the image that will be added to the document, add image stream to * the package, in the 'Pictures/' graphic directory with the same image file name as in the * URI. If the imageURI is relative first the user.dir is taken to make it absolute. * @return Returns the internal package path of the image, which was created based on the given * URI. */ public String newImage(URI imageUri) { try { OdfContentDom contentDom = this.getContentDom(); OdfDrawFrame drawFrame = contentDom.newOdfElement(OdfDrawFrame.class); XPath xpath = contentDom.getXPath(); if (this instanceof OdfSpreadsheetDocument) { TableTableCellElement lastCell = (TableTableCellElement) xpath.evaluate("//table:table-cell[last()]", contentDom, XPathConstants.NODE); lastCell.appendChild(drawFrame); drawFrame.removeAttribute("text:anchor-type"); } else if (this instanceof OdfTextDocument) { TextPElement lastPara = (TextPElement) xpath.evaluate("//text:p[last()]", contentDom, XPathConstants.NODE); if (lastPara == null) { lastPara = ((OdfTextDocument) this).newParagraph(); } lastPara.appendChild(drawFrame); drawFrame.setTextAnchorTypeAttribute(TextAnchorTypeAttribute.Value.PARAGRAPH.toString()); } else if (this instanceof OdfPresentationDocument) { DrawPageElement lastPage = (DrawPageElement) xpath.evaluate("//draw:page[last()]", contentDom, XPathConstants.NODE); lastPage.appendChild(drawFrame); } OdfDrawImage image = (OdfDrawImage) drawFrame.newDrawImageElement(); String imagePath = image.newImage(imageUri); return imagePath; } catch (Exception ex) { Logger.getLogger(OdfDocument.class.getName()).log(Level.SEVERE, null, ex); } return null; } /** * Return an instance of table feature with the specific table name. * * @param name of the table being searched for. * @return an instance of table feature with the specific table name. */ public OdfTable getTableByName(String name) { try { OdfElement root = getContentDom().getRootElement(); OfficeBodyElement officeBody = OdfElement.findFirstChildNode(OfficeBodyElement.class, root); OdfElement typedContent = OdfElement.findFirstChildNode(OdfElement.class, officeBody); NodeList childList = typedContent.getChildNodes(); for (int i = 0; i < childList.getLength(); i++) { if (childList.item(i) instanceof TableTableElement) { TableTableElement table = (TableTableElement) childList.item(i); if (table .getOdfAttributeValue(OdfName.newName(OdfDocumentNamespace.TABLE, "name")) .equals(name)) { return OdfTable.getInstance(table); } } } } catch (Exception e) { Logger.getLogger(OdfDocument.class.getName()).log(Level.SEVERE, null, e); } return null; } /** * Return a list of table features in this document. For general ODF documents it searches for * them recursively through the document. For ODF documents, there is a getOdsTableList * * @see OdfSpreadsheetDocument:getSpreadsheetTables * @return a list of table features in this document. */ @Deprecated( since = "It was not clear that this is searching recursively, especialy in OdfSpreadsheetDocuments") public List getTableList() { return getTableList(false); } /** * Return a list of table features in this document. For general ODF documents it searches for * tables recursively through the document. * * @see OdfSpreadsheetDocument:getSpreadsheetTables * @param doRecursiveSearch In spreadsheet documents you do not need a recursive search. * @return a list of table features in this document. */ public List getTableList(boolean doRecursiveSearch) { List tableList = null; try { List tableElementList = getTables(doRecursiveSearch); tableList = new ArrayList(tableElementList.size()); for (int i = 0; i < tableElementList.size(); i++) { tableList.add(OdfTable.getInstance(tableElementList.get(i))); } } catch (Exception e) { Logger.getLogger(OdfDocument.class.getName()).log(Level.SEVERE, null, e); } return tableList; } /** * Update document meta data in the ODF document. Following metadata data is being updated: * *

    *
  • The name of the person who last modified this document will be the Java user.name System * property *
  • The date and time when the document was last modified using current data *
  • The number of times this document has been edited is incremented by 1 *
  • The total time spent editing this document *
* * TODO:This method will be moved to OdfMetadata class. see * http://odftoolkit.org/bugzilla/show_bug.cgi?id=204 */ public void updateMetaData() { if (getOfficeMetadata().hasAutomaticUpdate()) { OdfOfficeMeta metaData = getOfficeMetadata(); // OpenOffice 3.4.1 needs this metadata to continue list numbering correctly metaData.setGenerator(FORMER_OPEN_OFFICE_VERSION); // set creation date if (mCreationDate != null) { getOfficeMetadata().setCreationDate(mCreationDate); } // update late modfied date Calendar calendar = Calendar.getInstance(); metaData.setDate(calendar); // update editing-cycles Integer cycle = metaData.getEditingCycles(); if (cycle != null) { metaData.setEditingCycles(++cycle); } else { metaData.setEditingCycles(1); } // update editing-duration long editingDuration = calendar.getTimeInMillis() - documentOpeningTime; editingDuration = (editingDuration < 1) ? 1 : editingDuration; try { DatatypeFactory aFactory = DatatypeFactory.newInstance(); metaData.setEditingDuration(new Duration(aFactory.newDurationDayTime(editingDuration))); } catch (DatatypeConfigurationException e) { Logger.getLogger(OdfDocument.class.getName()) .log( Level.SEVERE, "editing duration update fail as DatatypeFactory can not be instanced", e); } } } // ///////////////// // Following is the implementation of locale settings // //////////////// /** * Unicode characters are in general divided by office applications into three different groups. * *

1) There is CJK: the Chinese, Japanese and Korean script (also old Vietnamese belong to this * group). See http://en.wikipedia.org/wiki/CJK_characters * *

2) There is CTL: Complex Text Layout, which uses BIDI algorithms and/or glyph modules for * instance Arabic, Hebrew, Indic and Thai. See http://en.wikipedia.org/wiki/Complex_Text_Layout * *

3) And there is all the rest, which was once called by MS Western. */ public enum UnicodeGroup { /** Western language */ WESTERN, /** Chinese, Japanese and Korean */ CJK, /** Complex Text Layout language */ CTL; } private static final Set CJKLanguage = Set.of( "zh", // LANGUAGE_CHINES "ja", // LANGUAGE_JAPANESE "ko"); // LANGUAGE_KOREANE private static final Set CTLLanguage = Set.of( "am", // LANGUAGE_AMHARIC_ETHIOPIA "ar", // LANGUAGE_ARABIC_SAUDI_ARABIA "as", // LANGUAGE_ASSAMESE "bn", // LANGUAGE_BENGALI "bo", // LANGUAGE_TIBETAN "brx", // LANGUAGE_USER_BODO_INDIA "dgo", // LANGUAGE_USER_DOGRI_INDIA "dv", // LANGUAGE_DHIVEHI "dz", // LANGUAGE_DZONGKHA "fa", // LANGUAGE_FARSI "gu", // LANGUAGE_GUJARATI "he", // LANGUAGE_HEBREW "hi", // LANGUAGE_HINDI "km", // LANGUAGE_KHMER "kn", // LANGUAGE_KANNADA "ks", // LANGUAGE_KASHMIRI "ku", // LANGUAGE_USER_KURDISH_IRAQ "lo", // LANGUAGE_LAO "mai", // LANGUAGE_USER_MAITHILI_INDIA "ml", // LANGUAGE_MALAYALAM "mn", // LANGUAGE_MONGOLIAN_MONGOLIAN "mni", // LANGUAGE_MANIPURI "mr", // LANGUAGE_MARATHI "my", // LANGUAGE_BURMESE "ne", // LANGUAGE_NEPALI "or", // LANGUAGE_ORIYA "pa", // LANGUAGE_PUNJABI "sa", // LANGUAGE_SANSKRIT "sd", // LANGUAGE_SINDHI "si", // LANGUAGE_SINHALESE_SRI_LANKA "syr", // LANGUAGE_SYRIAC "ta", // LANGUAGE_TAMIL "te", // LANGUAGE_TELUGU "th", // LANGUAGE_THAI "ug", // LANGUAGE_UIGHUR_CHINA "ur", // LANGUAGE_URDU "yi"); // LANGUAGE_YIDDISH /** * Set a locale information. * *

The locale information will affect the language and country setting of the document. Thus * the font settings, the spell checkings and etc will be affected. * * @param locale - an instance of Locale */ public void setLocale(Locale locale) { setLocale(locale, getUnicodeGroup(locale)); } /** * This method will return Locale, which presents the default language and country information * settings in this document. * * @return an instance of Locale that the default language and country is set to. */ /** * Similar to OpenOffice.org, ODFDOM assumes that every Locale is related to one of the three * Unicodes Groups, either CJK, CTL or Western. * * @param locale the UnicodeGroup is requested for * @return the related UnicodeGroup */ public static UnicodeGroup getUnicodeGroup(Locale locale) { String language = locale.getLanguage(); if (CJKLanguage.contains(language)) { return UnicodeGroup.CJK; } if (CTLLanguage.contains(language)) { return UnicodeGroup.CTL; } return UnicodeGroup.WESTERN; } /** * Set a locale of a specific script type. * *

If the locale does not belong to the script type, it will not be set. * * @param locale - Locale information * @param unicodeGroup - The script type */ private void setLocale(Locale locale, UnicodeGroup unicodeGroup) { try { switch (unicodeGroup) { case WESTERN: setDefaultWesternLanguage(locale); break; case CJK: setDefaultAsianLanguage(locale); break; case CTL: setDefaultComplexLanguage(locale); break; } } catch (Exception e) { Logger.getLogger(OdfDocument.class.getName()).log(Level.SEVERE, "Failed to set locale", e); } } /** * This method will return Locale, which presents the default language and country information * settings in this document * *

ODF allows to set a Locale for each of the three UnicodeGroups. Therefore there might be * three different Locale for the document. * * @param unicodeGroup - One of the three (CJK, CTL or Western). * @return the Locale for the given UnicodeGroup */ public Locale getLocale(UnicodeGroup unicodeGroup) { try { switch (unicodeGroup) { case WESTERN: return getDefaultLanguageByProperty( OdfTextProperties.Country, OdfTextProperties.Language); case CJK: return getDefaultLanguageByProperty( OdfTextProperties.CountryAsian, OdfTextProperties.LanguageAsian); case CTL: return getDefaultLanguageByProperty( OdfTextProperties.CountryComplex, OdfTextProperties.LanguageComplex); } } catch (Exception e) { Logger.getLogger(OdfDocument.class.getName()).log(Level.SEVERE, "Failed to get locale", e); } return null; } /** Returns the current Locale for the OdfStyleProperty of the corresponding UnicodeGroup */ private Locale getDefaultLanguageByProperty( OdfStyleProperty countryProp, OdfStyleProperty languageProp) throws Exception { String lang = null, ctry = null; OdfOfficeStyles styles = getStylesDom().getOfficeStyles(); // get language and country setting from default style setting for // paragraph OdfDefaultStyle defaultStyle = styles.getDefaultStyle(OdfStyleFamily.Paragraph); if (defaultStyle != null) { if (defaultStyle.hasProperty(countryProp) && defaultStyle.hasProperty(languageProp)) { ctry = defaultStyle.getProperty(countryProp); lang = defaultStyle.getProperty(languageProp); return new Locale(lang, ctry); } } // if no default style setting for paragraph // get language and country setting from other default style settings Iterable defaultStyles = styles.getDefaultStyles(); Iterator itera = defaultStyles.iterator(); while (itera.hasNext()) { OdfDefaultStyle style = itera.next(); if (style.hasProperty(countryProp) && style.hasProperty(languageProp)) { ctry = style.getProperty(countryProp); lang = style.getProperty(languageProp); return new Locale(lang, ctry); } } return null; } /** * This method will set the default language and country information of the document, based on the * parameter of the Locale information. * * @param locale - an instance of Locale that the default language and country will be set to. * @throws Exception */ private void setDefaultWesternLanguage(Locale locale) throws Exception { OdfOfficeStyles styles = getStylesDom().getOfficeStyles(); Iterable defaultStyles = styles.getDefaultStyles(); if (defaultStyles != null) { Iterator itera = defaultStyles.iterator(); while (itera.hasNext()) { OdfDefaultStyle style = itera.next(); if (style.getFamily().getProperties().contains(OdfTextProperties.Language)) { style.setProperty(OdfTextProperties.Language, locale.getLanguage()); style.setProperty(OdfTextProperties.Country, locale.getCountry()); } } } } /** * This method will set the default Asian language and country information of the document, based * on the parameter of the Locale information. If the Locale instance is not set a Asian language * (Chinese, Traditional Chinese, Japanese and Korean, nothing will take effect. * * @param locale - an instance of Locale that the default Asian language and country will be set * to. * @throws Exception */ private void setDefaultAsianLanguage(Locale locale) throws Exception { OdfOfficeStyles styles = getStylesDom().getOfficeStyles(); Iterable defaultStyles = styles.getDefaultStyles(); if (defaultStyles != null) { Iterator itera = defaultStyles.iterator(); while (itera.hasNext()) { OdfDefaultStyle style = itera.next(); if (style.getFamily().getProperties().contains(OdfTextProperties.LanguageAsian)) { style.setProperty(OdfTextProperties.LanguageAsian, locale.getLanguage()); style.setProperty(OdfTextProperties.CountryAsian, locale.getCountry()); } } } } /** * This method will set the default complex language and country information of the document, * based on the parameter of the Locale information. * * @param locale - an instance of Locale that the default complex language and country will be set * to. * @throws Exception */ private void setDefaultComplexLanguage(Locale locale) throws Exception { OdfOfficeStyles styles = getStylesDom().getOfficeStyles(); Iterable defaultStyles = styles.getDefaultStyles(); if (defaultStyles != null) { Iterator itera = defaultStyles.iterator(); while (itera.hasNext()) { OdfDefaultStyle style = itera.next(); if (style.getFamily().getProperties().contains(OdfTextProperties.LanguageComplex)) { style.setProperty(OdfTextProperties.LanguageComplex, locale.getLanguage()); style.setProperty(OdfTextProperties.CountryComplex, locale.getCountry()); } } } } private Set mFontNames = null; private Map annotations = null; /** * This is a temporary solution to know about the fonts within the documents. Project client does * not provide font information, therefore the 16 fonts are hard coded */ public Set getFontNames() { if (mFontNames == null) { mFontNames = new HashSet(); } return mFontNames; } public void addAnnotation(String name, OfficeAnnotationElement element) { if (annotations == null) { annotations = new HashMap(); } annotations.put(name, element); } public OfficeAnnotationElement getAnnotation(String name) { if (annotations == null || !annotations.containsKey(name)) { return null; } return annotations.get(name); } public void removeAnnotation(String name) { if (annotations != null && annotations.containsKey(name)) { annotations.remove(name); } } public String getUniqueAnnotationName() { String prefix = "CmtId"; int freeIndex = 0; if (annotations != null) { while (annotations.containsKey(prefix + freeIndex)) { ++freeIndex; } } return prefix + freeIndex; } protected void removeCachedView() { mPackage = getPackage(); // removes the LO/AO view caching mPackage.remove("Thumbnails/thumbnail.png"); } /** * @return TRUE if the document was created by CollabTextDocument and thereby supports user * changes, otherwise FALSE */ public Boolean hasCollaboration() { return mHasCollaboration != null && mHasCollaboration; } private SelectionManager mSelectionManager = null; /** * * All text selections within this document are cached and have to be updated when the document * is changed.The SelectionManager takes care of those updates across each * TextNavigator * * @return the single selection manager for an OdfDocument instance */ public SelectionManager getSelectionManager() { if (mSelectionManager == null) { mSelectionManager = new SelectionManager(); } return mSelectionManager; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy