blob: 9546620e17bb69de56740852343990d443c343fe [file] [log] [blame]
/************************************************************************
*
* 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.simple;
import java.awt.Rectangle;
import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.xpath.XPathConstants;
import org.odftoolkit.odfdom.dom.OdfContentDom;
import org.odftoolkit.odfdom.dom.OdfDocumentNamespace;
import org.odftoolkit.odfdom.dom.attribute.text.TextAnchorTypeAttribute;
import org.odftoolkit.odfdom.dom.element.draw.DrawFrameElement;
import org.odftoolkit.odfdom.dom.element.draw.DrawPageElement;
import org.odftoolkit.odfdom.dom.element.draw.DrawPageThumbnailElement;
import org.odftoolkit.odfdom.dom.element.office.OfficePresentationElement;
import org.odftoolkit.odfdom.dom.element.presentation.PresentationNotesElement;
import org.odftoolkit.odfdom.pkg.MediaType;
import org.odftoolkit.odfdom.pkg.OdfElement;
import org.odftoolkit.odfdom.pkg.OdfFileDom;
import org.odftoolkit.odfdom.pkg.OdfPackage;
import org.odftoolkit.odfdom.type.CellRangeAddressList;
import org.odftoolkit.simple.chart.AbstractChartContainer;
import org.odftoolkit.simple.chart.Chart;
import org.odftoolkit.simple.chart.ChartContainer;
import org.odftoolkit.simple.chart.DataSet;
import org.odftoolkit.simple.presentation.Slide;
import org.odftoolkit.simple.presentation.Notes.NotesBuilder;
import org.odftoolkit.simple.presentation.Slide.SlideBuilder;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* This class represents an empty ODF presentation.
*/
public class PresentationDocument extends Document implements ChartContainer{
private static final String EMPTY_PRESENTATION_DOCUMENT_PATH = "/OdfPresentationDocument.odp";
static final Resource EMPTY_PRESENTATION_DOCUMENT_RESOURCE = new Resource(EMPTY_PRESENTATION_DOCUMENT_PATH);
private final SlideBuilder slideBuilder;
private final NotesBuilder notesBuilder;
private ChartContainerImpl chartContainerImpl;
/**
* It represents the defined values of presentation:class. The
* presentation:class attribute classifies presentation shapes by their
* usage within a draw page.
*
* @since 0.5
*/
public static enum PresentationClass {
/**
* presentation charts are standard object shapes.
*/
CHAT("chart"),
/**
* presentation graphics are standard graphic shapes.
*/
GRAPHIC("graphic"),
/**
* presentation handouts are placeholder for the drawing page in a
* handout page.
*/
HANDOUT("handout"),
/**
* presentation notes are used on notes pages.
*/
NOTES("notes"),
/**
* presentation objects are standard object shapes.
*/
OBJECTS("object"),
/**
* presentation organization charts are standard object shapes.
*/
ORGCHART("orgchart"),
/**
* outlines are standard text shapes
*/
OUTLINE("outline"),
/**
* presentation pages are used on notes pages
*/
PAGE("page"),
/**
* subtitles are standard text shapes
*/
SUBTITLE("subtitle"),
/**
* presentation tables are standard object shapes
*/
TABLE("table"),
/**
* presentation texts are standard text shapes
*/
TEXT("text"),
/**
* titles are standard text shapes
*/
TITLE("title"),
/**
* drawing shape is used as a date and/or time shape. Date and Time
* shapes are standard text shapes.
*/
DATETIME("date-time"),
/**
* drawing shape is used as a footer. Footer shapes are standard text
* shapes.
*/
FOOTER("footer"),
/**
* drawing shape is used as a header. Header shapes are standard text
* shapes.
*/
HEADER("header"),
/**
* drawing shape is used as a page number shape. Page Number shapes are
* standard text shapes.
*/
PAGENUMBER("page-number");
private String value;
PresentationClass(String aClass) {
value = aClass;
}
@Override
public String toString() {
return value;
}
public static PresentationClass enumValueOf(String aValue) {
if ((aValue == null) || (aValue.length() == 0))
return null;
for (PresentationClass aIter : values()) {
if (aValue.equals(aIter.toString())) {
return aIter;
}
}
throw new RuntimeException("Unsupported Presentation Class!");
}
}
/**
* This enum contains all possible media types of PresentationDocument
* documents.
*/
public enum OdfMediaType implements MediaType {
PRESENTATION(Document.OdfMediaType.PRESENTATION), PRESENTATION_TEMPLATE(
Document.OdfMediaType.PRESENTATION_TEMPLATE);
private final Document.OdfMediaType mMediaType;
OdfMediaType(Document.OdfMediaType mediaType) {
this.mMediaType = mediaType;
}
/**
* @return the ODF mediatype of this document
*/
public Document.OdfMediaType getOdfMediaType() {
return mMediaType;
}
/**
* @return the mediatype of this document
*/
public String getMediaTypeString() {
return mMediaType.getMediaTypeString();
}
/**
* @return the ODF filesuffix of this document
*/
public String getSuffix() {
return mMediaType.getSuffix();
}
/**
*
* @param mediaType
* string defining an ODF document
* @return the according OdfMediatype encapuslating the given string and
* the suffix
*/
public static Document.OdfMediaType getOdfMediaType(String mediaType) {
return Document.OdfMediaType.getOdfMediaType(mediaType);
}
}
/**
* Creates an empty presentation document.
*
* @return ODF presentation document based on a default template
* @throws java.lang.Exception
* - if the document could not be created
*/
public static PresentationDocument newPresentationDocument() throws Exception {
return (PresentationDocument) Document.loadTemplate(EMPTY_PRESENTATION_DOCUMENT_RESOURCE,
Document.OdfMediaType.PRESENTATION);
}
/**
* Creates an empty presentation template.
*
* @return ODF presentation template based on a default
* @throws Exception
* - if the template could not be created
*/
public static PresentationDocument newPresentationTemplateDocument() throws Exception {
PresentationDocument doc = (PresentationDocument) Document.loadTemplate(EMPTY_PRESENTATION_DOCUMENT_RESOURCE,
Document.OdfMediaType.PRESENTATION_TEMPLATE);
doc.changeMode(OdfMediaType.PRESENTATION_TEMPLATE);
return doc;
}
/**
* To avoid data duplication a new document is only created, if not already
* opened. A document is cached by this constructor using the internalpath
* as key.
*/
protected PresentationDocument(OdfPackage pkg, String internalPath, PresentationDocument.OdfMediaType odfMediaType) {
super(pkg, internalPath, odfMediaType.mMediaType);
slideBuilder = new SlideBuilder(this);
notesBuilder = new NotesBuilder(this);
}
/**
* Creates an PresentationDocument from the OpenDocument provided by a
* resource Stream.
*
* <p>
* Since an InputStream does not provide the arbitrary (non sequentiell)
* read access needed by PresentationDocument, 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.
* </p>
*
* <p>
* If the resource stream is not a ODF presentation document,
* ClassCastException might be thrown.
* </p>
*
* @param inputStream
* - the InputStream of the ODF presentation document.
* @return the presentation document created from the given InputStream
* @throws java.lang.Exception
* - if the document could not be created.
*/
public static PresentationDocument loadDocument(InputStream inputStream) throws Exception {
return (PresentationDocument) Document.loadDocument(inputStream);
}
/**
* Loads an PresentationDocument from the provided path.
*
* <p>
* PresentationDocument relies on the file being available for read access
* over the whole lifecycle of PresentationDocument.
* </p>
*
* <p>
* If the resource stream is not a ODF presentation document,
* ClassCastException might be thrown.
* </p>
*
* @param documentPath
* - the path from where the document can be loaded
* @return the presentation document from the given path or NULL if the
* media type is not supported by SIMPLE.
* @throws java.lang.Exception
* - if the document could not be created.
*/
public static PresentationDocument loadDocument(String documentPath) throws Exception {
return (PresentationDocument) Document.loadDocument(documentPath);
}
/**
* Creates an PresentationDocument from the OpenDocument provided by a File.
*
* <p>
* PresentationDocument relies on the file being available for read access
* over the whole lifecycle of PresentationDocument.
* </p>
*
* <p>
* If the resource stream is not a ODF presentation document,
* ClassCastException might be thrown.
* </p>
*
* @param file
* - a file representing the ODF presentation document.
* @return the presentation document created from the given File
* @throws java.lang.Exception
* - if the document could not be created.
*/
public static PresentationDocument loadDocument(File file) throws Exception {
return (PresentationDocument) Document.loadDocument(file);
}
/**
* Get the content root of a presentation document.
*
* @return content root, representing the office:presentation tag
* @throws Exception
* if the file DOM could not be created.
*/
@Override
public OfficePresentationElement getContentRoot() throws Exception {
return super.getContentRoot(OfficePresentationElement.class);
}
/**
* Switches this instance to the given type. This method can be used to e.g.
* convert a document instance to a template and vice versa. Changes take
* affect in the package when saving the document.
*
* @param type
* the compatible ODF mediatype.
*/
public void changeMode(OdfMediaType type) {
setOdfMediaType(type.mMediaType);
}
private boolean hasCheckSlideName = false;
/**
* Return the slide builder of this document. Every presentation document
* has a slide builder.
*
* @return the slide builder of this document.
* @since 0.3.5
*/
public SlideBuilder getSlideBuilder() {
return slideBuilder;
}
/**
* Return the notes builder of this document. Every presentation document
* has a notes builder.
*
* @return the notes builder of this document.
* @since 0.3.5
*/
public NotesBuilder getNotesBuilder() {
return notesBuilder;
}
/**
* Return the slide at a specified position in this presentation. Return
* null if the index is out of range.
*
* @param index
* the index of the slide to be returned
* @return a draw slide at the specified position
*/
public Slide getSlideByIndex(int index) {
checkAllSlideName();
OfficePresentationElement contentRoot = null;
try {
contentRoot = getContentRoot();
} catch (Exception e) {
Logger.getLogger(PresentationDocument.class.getName()).log(Level.SEVERE, null, e);
return null;
}
NodeList slideNodes = contentRoot.getElementsByTagNameNS(OdfDocumentNamespace.DRAW.getUri(), "page");
if ((index >= slideNodes.getLength()) || (index < 0)) {
return null;
}
DrawPageElement slideElement = (DrawPageElement) slideNodes.item(index);
return Slide.getInstance(slideElement);
}
/**
* Get the number of the slides in this presentation.
*
* @return the number of slides
*/
public int getSlideCount() {
checkAllSlideName();
OfficePresentationElement contentRoot = null;
try {
contentRoot = getContentRoot();
} catch (Exception e) {
Logger.getLogger(PresentationDocument.class.getName()).log(Level.SEVERE, null, e);
return 0;
}
NodeList slideNodes = contentRoot.getElementsByTagNameNS(OdfDocumentNamespace.DRAW.getUri(), "page");
return slideNodes.getLength();
}
/**
* Return the slide which have a specified slide name in this presentation.
* <p>
* According to the odf specification "The draw:name attribute specifies a
* name by which this element can be referenced. It is optional but if
* present, must be unique within the document instance. If not present, an
* application may generate a unique name."
* <p>
* If the name is null, then return null because all the slide must has its
* own unique name.
*
* @param name
* the specified slide name
* @return the slide whose name equals to the specified name
*/
public Slide getSlideByName(String name) {
checkAllSlideName();
if (name == null) {
return null;
}
OfficePresentationElement contentRoot = null;
try {
contentRoot = getContentRoot();
} catch (Exception e) {
Logger.getLogger(PresentationDocument.class.getName()).log(Level.SEVERE, null, e);
return null;
}
NodeList slideNodes = contentRoot.getElementsByTagNameNS(OdfDocumentNamespace.DRAW.getUri(), "page");
for (int i = 0; i < slideNodes.getLength(); i++) {
DrawPageElement slideElement = (DrawPageElement) slideNodes.item(i);
Slide slide = Slide.getInstance(slideElement);
String slideName = slide.getSlideName();
if (slideName.equals(name)) {
return slide;
}
}
return null;
}
// when access slide related method, this function should be called
private void checkAllSlideName() {
// check if this function is called or not
if (hasCheckSlideName) {
return;
}
List<String> slideNameList = new ArrayList<String>();
OfficePresentationElement contentRoot = null;
try {
contentRoot = getContentRoot();
} catch (Exception e) {
Logger.getLogger(PresentationDocument.class.getName()).log(Level.SEVERE, null, e);
return;
}
NodeList slideNodes = contentRoot.getElementsByTagNameNS(OdfDocumentNamespace.DRAW.getUri(), "page");
for (int i = 0; i < slideNodes.getLength(); i++) {
DrawPageElement slideElement = (DrawPageElement) slideNodes.item(i);
String slideName = slideElement.getDrawNameAttribute();
if ((slideName == null) || slideNameList.contains(slideName)) {
slideName = "page" + (i + 1) + "-" + makeUniqueName();
slideElement.setDrawNameAttribute(slideName);
}
slideNameList.add(slideName);
}
hasCheckSlideName = true;
}
/**
* Return a list iterator containing all slides in this presentation.
*
* @return a list iterator containing all slides in this presentation
*/
public Iterator<Slide> getSlides() {
checkAllSlideName();
OfficePresentationElement contentRoot = null;
try {
contentRoot = getContentRoot();
} catch (Exception e) {
Logger.getLogger(PresentationDocument.class.getName()).log(Level.SEVERE, null, e);
return null;
}
ArrayList<Slide> slideList = new ArrayList<Slide>();
NodeList slideNodes = contentRoot.getElementsByTagNameNS(OdfDocumentNamespace.DRAW.getUri(), "page");
for (int i = 0; i < slideNodes.getLength(); i++) {
DrawPageElement slideElement = (DrawPageElement) slideNodes.item(i);
slideList.add(Slide.getInstance(slideElement));
}
return slideList.iterator();
}
/**
* Delete the slide at a specified position in this presentation.
*
* @param index
* the index of the slide that need to be delete
* <p>
* Throw IndexOutOfBoundsException if the slide index is out of
* the presentation document slide count.
* @return false if the operation was not successful
*/
public boolean deleteSlideByIndex(int index) {
boolean success = true;
checkAllSlideName();
OfficePresentationElement contentRoot = null;
try {
contentRoot = getContentRoot();
} catch (Exception e) {
Logger.getLogger(PresentationDocument.class.getName()).log(Level.SEVERE, null, e);
success = false;
return success;
}
NodeList slideNodes = contentRoot.getElementsByTagNameNS(OdfDocumentNamespace.DRAW.getUri(), "page");
if ((index >= slideNodes.getLength()) || (index < 0)) {
throw new IndexOutOfBoundsException(
"the specified Index is out of slide count when call deleteSlideByIndex method.");
}
DrawPageElement slideElement = (DrawPageElement) slideNodes.item(index);
// remove all the content of the current page
// 1. the reference of the path that contained in this slide is 1, then
// remove it
// success &= deleteLinkedRef(slideElement);
// 2.the reference of the style is 1, then remove it
// in order to save time, do not delete the style here
// success &= deleteStyleRef(slideElement);
// these two methods have been merged into 1 method
success &= removeElementLinkedResource(slideElement);
// remove the current page element
contentRoot.removeChild(slideElement);
adjustNotePageNumber(index);
return success;
}
/**
* Delete all the slides with a specified name in this presentation.
*
* @param name
* the name of the slide that need to be delete
* @return false if the operation was not successful
*/
public boolean deleteSlideByName(String name) {
boolean success = true;
checkAllSlideName();
OfficePresentationElement contentRoot = null;
try {
contentRoot = getContentRoot();
} catch (Exception e) {
Logger.getLogger(PresentationDocument.class.getName()).log(Level.SEVERE, null, e);
success = false;
return success;
}
Slide slide = getSlideByName(name);
DrawPageElement slideElement = slide.getOdfElement();
// remove all the content of the current page
// 1. the reference of the path that contained in this slide is 1, then
// remove its
success &= deleteLinkedRef(slideElement);
// 2.the reference of the style is 1, then remove it
// in order to save time, do not delete style here
success &= deleteStyleRef(slideElement);
// remove the current page element
contentRoot.removeChild(slideElement);
adjustNotePageNumber(0);
return success;
}
/**
* Make a copy of the slide at a specified position to another position in
* this presentation. The original slide which at the dest index and after
* the dest index will move after.
* <p>
*
* @param source
* the source position of the slide need to be copied
* @param dest
* the destination position of the slide need to be copied
* @param newName
* the new name of the copied slide
* @return the new slide at the destination position with the specified
* name, and it has the same content with the slide at the source
* position.
* <p>
* Throw IndexOutOfBoundsException if the slide index is out of the
* presentation document slide count. If copy the slide at the end
* of document, destIndex should set the same value with the slide
* count.
*/
public Slide copySlide(int source, int dest, String newName) {
checkAllSlideName();
OfficePresentationElement contentRoot = null;
try {
contentRoot = getContentRoot();
} catch (Exception e) {
Logger.getLogger(PresentationDocument.class.getName()).log(Level.SEVERE, null, e);
return null;
}
NodeList slideList = contentRoot.getElementsByTagNameNS(OdfDocumentNamespace.DRAW.getUri(), "page");
int slideCount = slideList.getLength();
if ((source < 0) || (source >= slideCount) || (dest < 0) || (dest > slideCount)) {
throw new IndexOutOfBoundsException("the specified Index is out of slide count when call copySlide method.");
}
DrawPageElement sourceSlideElement = (DrawPageElement) slideList.item(source);
DrawPageElement cloneSlideElement = (DrawPageElement) sourceSlideElement.cloneNode(true);
cloneSlideElement.setDrawNameAttribute(newName);
if (dest == slideCount) {
contentRoot.appendChild(cloneSlideElement);
} else {
DrawPageElement refSlide = (DrawPageElement) slideList.item(dest);
contentRoot.insertBefore(cloneSlideElement, refSlide);
}
adjustNotePageNumber(Math.min(source, dest));
// in case that the appended new slide have the same name with the
// original slide
hasCheckSlideName = false;
checkAllSlideName();
return Slide.getInstance(cloneSlideElement);
}
/**
* Move the slide at a specified position to the destination position.
*
* @param source
* the current index of the slide that need to be moved
* @param dest
* The index of the destination position before the move action
* <p>
* Throw IndexOutOfBoundsException if the slide index is out of
* the presentation document slide count.
*/
public void moveSlide(int source, int dest) {
checkAllSlideName();
OfficePresentationElement contentRoot = null;
try {
contentRoot = getContentRoot();
} catch (Exception e) {
Logger.getLogger(PresentationDocument.class.getName()).log(Level.SEVERE, null, e);
return;
}
NodeList slideList = contentRoot.getElementsByTagNameNS(OdfDocumentNamespace.DRAW.getUri(), "page");
int slideCount = slideList.getLength();
if ((source < 0) || (source >= slideCount) || (dest < 0) || (dest > slideCount)) {
throw new IndexOutOfBoundsException("the specified Index is out of slide count when call moveSlide method.");
}
DrawPageElement sourceSlide = (DrawPageElement) slideList.item(source);
if (dest == slideCount) {
contentRoot.appendChild(sourceSlide);
} else {
DrawPageElement refSlide = (DrawPageElement) slideList.item(dest);
contentRoot.insertBefore(sourceSlide, refSlide);
}
adjustNotePageNumber(Math.min(source, dest));
}
private Node cloneForeignElement_(Node element, OdfFileDom dom, boolean deep) {
checkAllSlideName();
return cloneForeignElement(element, dom, deep);
}
/**
* Append all the slides of the specified presentation document to the
* current document.
*
* @param srcDoc
* the specified <code>PresentationDocument</code> that need to
* be appended
*/
public void appendPresentation(PresentationDocument srcDoc) {
checkAllSlideName();
OfficePresentationElement contentRoot = null;
OdfFileDom contentDom = null;
OfficePresentationElement srcContentRoot = null;
try {
contentRoot = getContentRoot();
contentDom = getContentDom();
srcContentRoot = srcDoc.getContentRoot();
} catch (Exception e) {
Logger.getLogger(PresentationDocument.class.getName()).log(Level.SEVERE, null, e);
}
NodeList slideList = contentRoot.getElementsByTagNameNS(OdfDocumentNamespace.DRAW.getUri(), "page");
int slideNum = slideList.getLength();
// clone the srcContentRoot, and make a modification on this clone node.
OfficePresentationElement srcCloneContentRoot = (OfficePresentationElement) srcContentRoot.cloneNode(true);
// copy all the referred xlink:href here
copyLinkedRefInBatch(srcCloneContentRoot, srcDoc);
// copy all the referred style definition here
copyForeignStyleRef(srcCloneContentRoot, srcDoc);
Node child = srcCloneContentRoot.getFirstChild();
while (child != null) {
Node cloneElement = cloneForeignElement_(child, contentDom, true);
contentRoot.appendChild(cloneElement);
child = child.getNextSibling();
}
adjustNotePageNumber(slideNum - 1);
// in case that the appended new slide have the same name with the
// original slide
hasCheckSlideName = false;
checkAllSlideName();
}
/**
* Make a copy of slide which locates at the specified position of the
* source presentation document and insert it to the current presentation
* document at the new position. The original slide which at the dest index
* and after the dest index will move after.
*
* @param destIndex
* the new position of the copied slide in the current document
* @param srcDoc
* the source document of the copied slide
* @param srcIndex
* the slide index of the source document that need to be copied
* @return the new slide which has the same content with the source slide
* <p>
* Throw IndexOutOfBoundsException if the slide index is out of the
* presentation document slide count If insert the foreign slide at
* the end of document, destIndex should set the same value with the
* slide count of the current presentation document.
*/
public Slide copyForeignSlide(int destIndex, PresentationDocument srcDoc, int srcIndex) {
checkAllSlideName();
OfficePresentationElement contentRoot = null;
OdfFileDom contentDom = null;
try {
contentRoot = getContentRoot();
contentDom = getContentDom();
} catch (Exception e) {
Logger.getLogger(PresentationDocument.class.getName()).log(Level.SEVERE, null, e);
return null;
}
NodeList slideList = contentRoot.getElementsByTagNameNS(OdfDocumentNamespace.DRAW.getUri(), "page");
int slideCount = slideList.getLength();
if ((destIndex < 0) || (destIndex > slideCount)) {
throw new IndexOutOfBoundsException(
"the specified Index is out of slide count when call copyForeignSlide method.");
}
Slide sourceSlide = srcDoc.getSlideByIndex(srcIndex);
DrawPageElement sourceSlideElement = sourceSlide.getOdfElement();
// clone the sourceSlideEle, and make a modification on this clone node.
DrawPageElement sourceCloneSlideElement = (DrawPageElement) sourceSlideElement.cloneNode(true);
// copy all the referred xlink:href here
copyLinkedRefInBatch(sourceCloneSlideElement, srcDoc);
// copy all the referred style definition here
copyForeignStyleRef(sourceCloneSlideElement, srcDoc);
// clone the sourceCloneSlideEle, and this cloned element should in the
// current dom tree
DrawPageElement cloneSlideElement = (DrawPageElement) cloneForeignElement_(sourceCloneSlideElement, contentDom,
true);
if (destIndex == slideCount) {
contentRoot.appendChild(cloneSlideElement);
} else {
DrawPageElement refSlide = (DrawPageElement) slideList.item(destIndex);
contentRoot.insertBefore(cloneSlideElement, refSlide);
}
adjustNotePageNumber(destIndex);
// in case that the appended new slide have the same name with the
// original slide
hasCheckSlideName = false;
checkAllSlideName();
return Slide.getInstance(cloneSlideElement);
}
/**
* New a slide at the specified position with the specified name, and use
* the specified slide template. See <code>OdfDrawPage.SlideLayout</code>.
* <p>
* If index is invalid, such as larger than the current document slide
* number or is negative, then append the new slide at the end of the
* document.
* <p>
* The slide name can be null.
*
* @param index
* the new slide position
* @param name
* the new slide name
* @param slideLayout
* the new slide template
* @return the new slide which locate at the specified position with the
* specified name and apply the specified slide template. If
* slideLayout is null, then use the default slide template which is
* a blank slide.
* <p>
* Throw IndexOutOfBoundsException if index is out of the
* presentation document slide count.
*/
public Slide newSlide(int index, String name, Slide.SlideLayout slideLayout) {
checkAllSlideName();
OfficePresentationElement contentRoot = null;
try {
contentRoot = getContentRoot();
} catch (Exception e) {
Logger.getLogger(PresentationDocument.class.getName()).log(Level.SEVERE, null, e);
return null;
}
NodeList slideList = contentRoot.getElementsByTagNameNS(OdfDocumentNamespace.DRAW.getUri(), "page");
int slideCount = slideList.getLength();
if ((index < 0) || (index > slideCount)) {
throw new IndexOutOfBoundsException("the specified Index is out of slide count when call newSlide method.");
}
// if insert page at the beginning of the document,
// get the next page style as the new page style
// else get the previous page style as the new page style
DrawPageElement refStyleSlide = null;
int refSlideIndex = 0;
if (index > 0) {
refSlideIndex = index - 1;
}
refStyleSlide = (DrawPageElement) slideList.item(refSlideIndex);
String masterPageStyleName = "Default";
String masterName = refStyleSlide.getDrawMasterPageNameAttribute();
if (masterName != null) {
masterPageStyleName = masterName;
}
DrawPageElement newSlideElement = contentRoot.newDrawPageElement(masterPageStyleName);
newSlideElement.setDrawNameAttribute(name);
String drawStyleName = refStyleSlide.getDrawStyleNameAttribute();
if (drawStyleName != null) {
newSlideElement.setDrawStyleNameAttribute(drawStyleName);
}
String pageLayoutName = refStyleSlide.getPresentationPresentationPageLayoutNameAttribute();
if (pageLayoutName != null) {
newSlideElement.setPresentationPresentationPageLayoutNameAttribute(pageLayoutName);
}
setSlideLayout(newSlideElement, slideLayout);
// insert notes page
NodeList noteNodes = refStyleSlide.getElementsByTagNameNS(OdfDocumentNamespace.PRESENTATION.getUri(), "notes");
if (noteNodes.getLength() > 0) {
PresentationNotesElement notePage = (PresentationNotesElement) noteNodes.item(0);
PresentationNotesElement cloneNotePage = (PresentationNotesElement) notePage.cloneNode(true);
newSlideElement.appendChild(cloneNotePage);
}
if (index < slideCount) {
DrawPageElement refSlide = (DrawPageElement) slideList.item(index);
contentRoot.insertBefore(newSlideElement, refSlide);
}
adjustNotePageNumber(index);
// in case that the appended new slide have the same name with the
// original slide
hasCheckSlideName = false;
checkAllSlideName();
return Slide.getInstance(newSlideElement);
}
public Chart createChart(String title, DataSet dataset, Rectangle rect) {
return getChartContainerImpl().createChart(title, dataset, rect);
}
public Chart createChart(String title, SpreadsheetDocument document, CellRangeAddressList cellRangeAddr, boolean firstRowAsLabel,
boolean firstColumnAsLabel, boolean rowAsDataSeries, Rectangle rect) {
return getChartContainerImpl().createChart(title, document, cellRangeAddr, firstRowAsLabel, firstColumnAsLabel,
rowAsDataSeries, rect);
}
public Chart createChart(String title, String[] labels, String[] legends, double[][] data, Rectangle rect) {
return getChartContainerImpl().createChart(title, labels, legends, data, rect);
}
public void deleteChartById(String chartId) {
getChartContainerImpl().deleteChartById(chartId);
}
public void deleteChartByTitle(String title) {
getChartContainerImpl().deleteChartByTitle(title);
}
public Chart getChartById(String chartId) {
return getChartContainerImpl().getChartById(chartId);
}
public List<Chart> getChartByTitle(String title) {
return getChartContainerImpl().getChartByTitle(title);
}
public int getChartCount() {
return getChartContainerImpl().getChartCount();
}
// when insert a slide, the note page for this slide is also inserted.
// note page refer the slide index in order to show the corresponding slide
// notes view
// this function is used to adjust note page referred slide index since
// startIndex
// when the slide at startIndex has been delete or insert
private void adjustNotePageNumber(int startIndex) {
try {
OfficePresentationElement contentRoot = getContentRoot();
NodeList slideList = contentRoot.getElementsByTagNameNS(OdfDocumentNamespace.DRAW.getUri(), "page");
for (int i = startIndex; i < getSlideCount(); i++) {
DrawPageElement page = (DrawPageElement) slideList.item(i);
NodeList noteNodes = page.getElementsByTagNameNS(OdfDocumentNamespace.PRESENTATION.getUri(), "notes");
if (noteNodes.getLength() > 0) {
PresentationNotesElement notePage = (PresentationNotesElement) noteNodes.item(0);
NodeList thumbnailList = notePage.getElementsByTagNameNS(OdfDocumentNamespace.DRAW.getUri(),
"page-thumbnail");
if (thumbnailList.getLength() > 0) {
DrawPageThumbnailElement thumbnail = (DrawPageThumbnailElement) thumbnailList.item(0);
thumbnail.setDrawPageNumberAttribute(i + 1);
}
}
}
} catch (Exception e) {
Logger.getLogger(PresentationDocument.class.getName()).log(Level.SEVERE, null, e);
}
}
// covered element
// <presentation:notes>, <draw:page-thumbnail>, <draw:frame>
// <style:presentation-page-layout>
private void setSlideLayout(DrawPageElement page, Slide.SlideLayout slideLayout) {
if (slideLayout == null) {
slideLayout = Slide.SlideLayout.BLANK;
}
slideLayout.apply(page);
}
public OdfElement getTableContainerElement() {
throw new UnsupportedOperationException("Presentation document is not supported to hold table directly.");
}
private ChartContainerImpl getChartContainerImpl() {
if (chartContainerImpl == null) {
chartContainerImpl = new ChartContainerImpl(this);
}
return chartContainerImpl;
}
private class ChartContainerImpl extends AbstractChartContainer {
PresentationDocument sdoc;
protected ChartContainerImpl(Document doc) {
super(doc);
sdoc = (PresentationDocument) doc;
}
protected DrawFrameElement getChartFrame() throws Exception {
OdfContentDom contentDom2 = sdoc.getContentDom();
DrawFrameElement drawFrame = contentDom2.newOdfElement(DrawFrameElement.class);
DrawPageElement lastPage = (DrawPageElement) contentDom2.getXPath().evaluate("//draw:page[last()]",
contentDom2, XPathConstants.NODE);
lastPage.appendChild(drawFrame);
drawFrame.setTextAnchorTypeAttribute(TextAnchorTypeAttribute.Value.PAGE.toString());
return drawFrame;
}
}
}