Bug 55164 - Support for adding slide notes

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1635010 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Andreas Beeker 2014-10-29 00:07:34 +00:00
parent 5dcf6b1a29
commit 1d84cd9828
8 changed files with 278 additions and 26 deletions

View File

@ -110,9 +110,18 @@ public abstract class POIXMLRelation {
} }
return _defaultName.replace("#", Integer.toString(index)); return _defaultName.replace("#", Integer.toString(index));
} }
/** /**
* Return type of the obejct used to construct instances of this relationship * Returns the index of the filename within the package for the given part.
* e.g. 4 for /xl/comments4.xml
*/
public Integer getFileNameIndex(POIXMLDocumentPart part) {
String regex = _defaultName.replace("#", "(\\d+)");
return Integer.parseInt(part.getPackageRelationship().getTargetURI().getPath().replaceAll(regex, "$1"));
}
/**
* Return type of the object used to construct instances of this relationship
* *
* @return the class of the object used to construct instances of this relation * @return the class of the object used to construct instances of this relation
*/ */

View File

@ -49,6 +49,8 @@ import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions; import org.apache.xmlbeans.XmlOptions;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraphProperties; import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraphProperties;
import org.openxmlformats.schemas.officeDocument.x2006.relationships.STRelationshipId; import org.openxmlformats.schemas.officeDocument.x2006.relationships.STRelationshipId;
import org.openxmlformats.schemas.presentationml.x2006.main.CTNotesMasterIdList;
import org.openxmlformats.schemas.presentationml.x2006.main.CTNotesMasterIdListEntry;
import org.openxmlformats.schemas.presentationml.x2006.main.CTPresentation; import org.openxmlformats.schemas.presentationml.x2006.main.CTPresentation;
import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideIdList; import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideIdList;
import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideIdListEntry; import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideIdListEntry;
@ -130,7 +132,7 @@ public class XMLSlideShow extends POIXMLDocument {
} else if (p instanceof XSLFSlideMaster) { } else if (p instanceof XSLFSlideMaster) {
XSLFSlideMaster master = (XSLFSlideMaster)p; XSLFSlideMaster master = (XSLFSlideMaster)p;
_masters.put(p.getPackageRelationship().getId(), master); _masters.put(p.getPackageRelationship().getId(), master);
}else if (p instanceof XSLFTableStyles){ } else if (p instanceof XSLFTableStyles){
_tableStyles = (XSLFTableStyles)p; _tableStyles = (XSLFTableStyles)p;
} else if (p instanceof XSLFNotesMaster) { } else if (p instanceof XSLFNotesMaster) {
_notesMaster = (XSLFNotesMaster)p; _notesMaster = (XSLFNotesMaster)p;
@ -155,7 +157,6 @@ public class XMLSlideShow extends POIXMLDocument {
} }
} }
@Override @Override
protected void commit() throws IOException { protected void commit() throws IOException {
XmlOptions xmlOptions = new XmlOptions(DEFAULT_XML_OPTIONS); XmlOptions xmlOptions = new XmlOptions(DEFAULT_XML_OPTIONS);
@ -231,7 +232,7 @@ public class XMLSlideShow extends POIXMLDocument {
_slides.add(slide); _slides.add(slide);
return slide; return slide;
} }
/** /**
* Create a blank slide. * Create a blank slide.
*/ */
@ -244,7 +245,96 @@ public class XMLSlideShow extends POIXMLDocument {
return createSlide(layout); return createSlide(layout);
} }
/**
* Return notes slide for the specified slide or create new if it does not exist yet.
*/
public XSLFNotes getNotesSlide(XSLFSlide slide) {
XSLFNotes notesSlide = slide.getNotes();
if (notesSlide == null) {
notesSlide = createNotesSlide(slide);
}
return notesSlide;
}
/**
* Create a blank notes slide.
*/
private XSLFNotes createNotesSlide(XSLFSlide slide) {
if (_notesMaster == null) {
createNotesMaster();
}
Integer slideIndex = XSLFRelation.SLIDE.getFileNameIndex(slide);
XSLFNotes notesSlide = (XSLFNotes) createRelationship(XSLFRelation.NOTES, XSLFFactory.getInstance(), slideIndex);
notesSlide.addRelation(_notesMaster.getPackageRelationship().getId(), _notesMaster);
PackagePartName notesMasterPackagePartName = _notesMaster.getPackagePart().getPartName();
notesSlide.getPackagePart().addRelationship(notesMasterPackagePartName, TargetMode.INTERNAL,
_notesMaster.getPackageRelationship().getRelationshipType());
slide.addRelation(notesSlide.getPackageRelationship().getId(), notesSlide);
PackagePartName notesSlidesPackagePartName = notesSlide.getPackagePart().getPartName();
slide.getPackagePart().addRelationship(notesSlidesPackagePartName, TargetMode.INTERNAL,
notesSlide.getPackageRelationship().getRelationshipType());
notesSlide.addRelation(slide.getPackageRelationship().getId(), slide);
PackagePartName slidesPackagePartName = slide.getPackagePart().getPartName();
notesSlide.getPackagePart().addRelationship(slidesPackagePartName, TargetMode.INTERNAL,
slide.getPackageRelationship().getRelationshipType());
notesSlide.importContent(_notesMaster);
return notesSlide;
}
/**
* Create a notes master.
*/
public void createNotesMaster() {
_notesMaster = (XSLFNotesMaster) createRelationship(XSLFRelation.NOTES_MASTER,
XSLFFactory.getInstance(), 1);
CTNotesMasterIdList notesMasterIdList = _presentation.addNewNotesMasterIdLst();
CTNotesMasterIdListEntry notesMasterId = notesMasterIdList.addNewNotesMasterId();
notesMasterId.setId(_notesMaster.getPackageRelationship().getId());
Integer themeIndex = 1;
List<Integer> themeIndexList = new ArrayList<Integer>();
for (POIXMLDocumentPart p : getRelations()) {
if (p instanceof XSLFTheme) {
themeIndexList.add(XSLFRelation.THEME.getFileNameIndex(p));
}
}
if (!themeIndexList.isEmpty()) {
Boolean found = false;
for (Integer i = 1; i <= themeIndexList.size(); i++) {
if (!themeIndexList.contains(i)) {
found = true;
themeIndex = i;
}
}
if (!found) {
themeIndex = themeIndexList.size() + 1;
}
}
XSLFTheme theme = (XSLFTheme) createRelationship(XSLFRelation.THEME,
XSLFFactory.getInstance(), themeIndex);
theme.importTheme(getSlides()[0].getTheme());
_notesMaster.addRelation(theme.getPackageRelationship().getId(), theme);
PackagePartName themePackagePartName = theme.getPackagePart().getPartName();
_notesMaster.getPackagePart().addRelationship(themePackagePartName, TargetMode.INTERNAL,
theme.getPackageRelationship().getRelationshipType());
}
/** /**
* Return the Notes Master, if there is one. * Return the Notes Master, if there is one.
* (May not be present if no notes exist) * (May not be present if no notes exist)

View File

@ -16,6 +16,8 @@
==================================================================== */ ==================================================================== */
package org.apache.poi.xslf.usermodel; package org.apache.poi.xslf.usermodel;
import java.io.IOException;
import org.apache.poi.POIXMLDocumentPart; import org.apache.poi.POIXMLDocumentPart;
import org.apache.poi.openxml4j.opc.PackagePart; import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.openxml4j.opc.PackageRelationship; import org.apache.poi.openxml4j.opc.PackageRelationship;
@ -25,8 +27,6 @@ import org.openxmlformats.schemas.presentationml.x2006.main.CTCommonSlideData;
import org.openxmlformats.schemas.presentationml.x2006.main.CTNotesSlide; import org.openxmlformats.schemas.presentationml.x2006.main.CTNotesSlide;
import org.openxmlformats.schemas.presentationml.x2006.main.NotesDocument; import org.openxmlformats.schemas.presentationml.x2006.main.NotesDocument;
import java.io.IOException;
@Beta @Beta
public final class XSLFNotes extends XSLFSheet { public final class XSLFNotes extends XSLFSheet {
private CTNotesSlide _notes; private CTNotesSlide _notes;
@ -57,12 +57,10 @@ public final class XSLFNotes extends XSLFSheet {
setCommonSlideData(_notes.getCSld()); setCommonSlideData(_notes.getCSld());
} }
private static CTNotesSlide prototype(){ private static CTNotesSlide prototype(){
CTNotesSlide ctNotes = CTNotesSlide.Factory.newInstance(); CTNotesSlide ctNotes = CTNotesSlide.Factory.newInstance();
CTCommonSlideData cSld = ctNotes.addNewCSld(); CTCommonSlideData cSld = ctNotes.addNewCSld();
cSld.addNewSpTree();
// TODO What else is needed for a mininum notes?
return ctNotes; return ctNotes;
} }
@ -77,6 +75,11 @@ public final class XSLFNotes extends XSLFSheet {
return "notes"; return "notes";
} }
@Override
public XSLFTheme getTheme(){
return getMasterSheet().getTheme();
}
@Override @Override
public XSLFNotesMaster getMasterSheet() { public XSLFNotesMaster getMasterSheet() {
for (POIXMLDocumentPart p : getRelations()) { for (POIXMLDocumentPart p : getRelations()) {

View File

@ -16,15 +16,19 @@
==================================================================== */ ==================================================================== */
package org.apache.poi.xslf.usermodel; package org.apache.poi.xslf.usermodel;
import java.io.IOException;
import java.io.InputStream;
import org.apache.poi.POIXMLDocumentPart;
import org.apache.poi.POIXMLException;
import org.apache.poi.openxml4j.opc.PackagePart; import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.openxml4j.opc.PackageRelationship; import org.apache.poi.openxml4j.opc.PackageRelationship;
import org.apache.poi.util.Beta; import org.apache.poi.util.Beta;
import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlException;
import org.openxmlformats.schemas.drawingml.x2006.main.CTColorMapping;
import org.openxmlformats.schemas.presentationml.x2006.main.CTNotesMaster; import org.openxmlformats.schemas.presentationml.x2006.main.CTNotesMaster;
import org.openxmlformats.schemas.presentationml.x2006.main.NotesMasterDocument; import org.openxmlformats.schemas.presentationml.x2006.main.NotesMasterDocument;
import java.io.IOException;
/** /**
* Notes master object associated with this layout. * Notes master object associated with this layout.
* <p> * <p>
@ -44,10 +48,11 @@ import java.io.IOException;
@Beta @Beta
public class XSLFNotesMaster extends XSLFSheet { public class XSLFNotesMaster extends XSLFSheet {
private CTNotesMaster _slide; private CTNotesMaster _slide;
private XSLFTheme _theme;
XSLFNotesMaster() { XSLFNotesMaster() {
super(); super();
_slide = CTNotesMaster.Factory.newInstance(); _slide = prototype();
} }
protected XSLFNotesMaster(PackagePart part, PackageRelationship rel) throws IOException, XmlException { protected XSLFNotesMaster(PackagePart part, PackageRelationship rel) throws IOException, XmlException {
@ -58,6 +63,21 @@ import java.io.IOException;
setCommonSlideData(_slide.getCSld()); setCommonSlideData(_slide.getCSld());
} }
private static CTNotesMaster prototype() {
InputStream is = XSLFNotesMaster.class.getResourceAsStream("notesMaster.xml");
if (is == null) {
throw new POIXMLException("Missing resource 'notesMaster.xml'");
}
try {
NotesMasterDocument doc = NotesMasterDocument.Factory.parse(is);
CTNotesMaster slide = doc.getNotesMaster();
return slide;
} catch (Exception e) {
throw new POIXMLException("Can't initialize NotesMaster", e);
}
}
@Override @Override
public CTNotesMaster getXmlObject() { public CTNotesMaster getXmlObject() {
return _slide; return _slide;
@ -72,5 +92,21 @@ import java.io.IOException;
public XSLFSheet getMasterSheet() { public XSLFSheet getMasterSheet() {
return null; return null;
} }
@Override
public XSLFTheme getTheme() {
if (_theme == null) {
for (POIXMLDocumentPart p : getRelations()) {
if (p instanceof XSLFTheme) {
_theme = (XSLFTheme) p;
CTColorMapping cmap = _slide.getClrMap();
if (cmap != null) {
_theme.initColorMap(cmap);
}
break;
}
}
}
return _theme;
}
} }

View File

@ -999,14 +999,23 @@ public class XSLFTextParagraph implements Iterable<XSLFTextRun>{
"declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main' " + "declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main' " +
"declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' " + "declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' " +
".//p:txStyles/p:" + defaultStyleSelector +"/a:lvl" +(level+1)+ "pPr"); ".//p:txStyles/p:" + defaultStyleSelector +"/a:lvl" +(level+1)+ "pPr");
if(o.length == 1){ if (o.length == 1){
return (CTTextParagraphProperties)o[0]; return (CTTextParagraphProperties)o[0];
} else {
o = masterSheet.getXmlObject().selectPath(
"declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main' " +
"declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' " +
".//p:notesStyle/a:lvl" +(level+1)+ "pPr");
if (o.length == 1){
return (CTTextParagraphProperties)o[0];
}
throw new IllegalArgumentException("Failed to fetch default style for " +
defaultStyleSelector + " and level=" + level);
} }
throw new IllegalArgumentException("Failed to fetch default style for " +
defaultStyleSelector + " and level=" + level);
} }
private boolean fetchParagraphProperty(ParagraphPropertyFetcher visitor){ private boolean fetchParagraphProperty(ParagraphPropertyFetcher visitor){
boolean ok = false; boolean ok = false;

View File

@ -16,6 +16,13 @@
==================================================================== */ ==================================================================== */
package org.apache.poi.xslf.usermodel; package org.apache.poi.xslf.usermodel;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import javax.xml.namespace.QName;
import org.apache.poi.POIXMLDocumentPart; import org.apache.poi.POIXMLDocumentPart;
import org.apache.poi.openxml4j.opc.PackagePart; import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.openxml4j.opc.PackageRelationship; import org.apache.poi.openxml4j.opc.PackageRelationship;
@ -32,12 +39,6 @@ import org.openxmlformats.schemas.drawingml.x2006.main.CTOfficeStyleSheet;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraphProperties; import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraphProperties;
import org.openxmlformats.schemas.drawingml.x2006.main.ThemeDocument; import org.openxmlformats.schemas.drawingml.x2006.main.ThemeDocument;
import javax.xml.namespace.QName;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
/** /**
* A shared style sheet in a .pptx slide show * A shared style sheet in a .pptx slide show
* *
@ -52,7 +53,7 @@ public class XSLFTheme extends POIXMLDocumentPart {
super(); super();
_theme = CTOfficeStyleSheet.Factory.newInstance(); _theme = CTOfficeStyleSheet.Factory.newInstance();
} }
public XSLFTheme(PackagePart part, PackageRelationship rel) throws IOException, XmlException { public XSLFTheme(PackagePart part, PackageRelationship rel) throws IOException, XmlException {
super(part, rel); super(part, rel);
ThemeDocument doc = ThemeDocument doc =
@ -60,6 +61,11 @@ public class XSLFTheme extends POIXMLDocumentPart {
_theme = doc.getTheme(); _theme = doc.getTheme();
initialize(); initialize();
} }
public void importTheme(XSLFTheme theme) {
_theme = theme.getXmlObject();
_schemeColors = theme._schemeColors;
}
private void initialize(){ private void initialize(){
CTBaseStyles elems = _theme.getThemeElements(); CTBaseStyles elems = _theme.getThemeElements();
@ -111,7 +117,7 @@ public class XSLFTheme extends POIXMLDocumentPart {
return _schemeColors.get(name); return _schemeColors.get(name);
} }
/** /**
* While developing only! * While developing only!
*/ */
@Internal @Internal

View File

@ -0,0 +1,97 @@
/* ====================================================================
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 static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import org.apache.poi.xslf.XSLFTestDataSamples;
import org.junit.Test;
public class TestXSLFNotes {
@Test
public void createNewNote() {
XMLSlideShow ppt = new XMLSlideShow();
XSLFSlide slide1 = ppt.createSlide();
assertNull(ppt.getNotesMaster());
assertNull(slide1.getNotes());
XSLFNotes notesSlide = ppt.getNotesSlide(slide1);
assertNotNull(ppt.getNotesMaster());
assertNotNull(notesSlide);
String note = null;
for (XSLFTextShape shape : notesSlide.getPlaceholders()) {
if (shape.getTextType() == Placeholder.BODY) {
shape.setText("New Note");
note = shape.getText();
break;
}
}
assertNotNull(note);
assertEquals("New Note", note);
}
@Test
public void addNote() {
XMLSlideShow ppt = XSLFTestDataSamples.openSampleDocument("sample.pptx");
XSLFSlide slide = ppt.createSlide();
XSLFNotes notesSlide = ppt.getNotesSlide(slide);
assertNotNull(notesSlide);
String note = null;
for (XSLFTextShape shape : notesSlide.getPlaceholders()) {
if (shape.getTextType() == Placeholder.BODY) {
shape.setText("New Note");
note = shape.getText();
break;
}
}
assertNotNull(note);
assertEquals("New Note", note);
}
@Test
public void replaceNotes() {
XMLSlideShow ppt = XSLFTestDataSamples.openSampleDocument("sample.pptx");
for (XSLFSlide slide : ppt.getSlides()) {
assertNotNull(slide.getNotes());
XSLFNotes notesSlide = ppt.getNotesSlide(slide);
assertNotNull(notesSlide);
String note = null;
for (XSLFTextShape shape : notesSlide.getPlaceholders()) {
if (shape.getTextType() == Placeholder.BODY) {
shape.setText("New Note");
note = shape.getText();
break;
}
}
assertNotNull(note);
assertEquals("New Note", note);
}
}
}

File diff suppressed because one or more lines are too long