From 1f615e39c74360ec5ba99399a5ef5a9d20f57d32 Mon Sep 17 00:00:00 2001 From: Andreas Beeker Date: Wed, 29 Oct 2014 00:07:34 +0000 Subject: [PATCH] Bug 55164 - Support for adding slide notes git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1635010 13f79535-47bb-0310-9956-ffa450edef68 --- .../java/org/apache/poi/POIXMLRelation.java | 13 ++- .../poi/xslf/usermodel/XMLSlideShow.java | 96 +++++++++++++++++- .../apache/poi/xslf/usermodel/XSLFNotes.java | 13 ++- .../poi/xslf/usermodel/XSLFNotesMaster.java | 44 ++++++++- .../poi/xslf/usermodel/XSLFTextParagraph.java | 17 +++- .../apache/poi/xslf/usermodel/XSLFTheme.java | 22 +++-- .../poi/xslf/usermodel/TestXSLFNotes.java | 97 +++++++++++++++++++ .../apache/poi/xslf/usermodel/notesMaster.xml | 2 + 8 files changed, 278 insertions(+), 26 deletions(-) create mode 100644 src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFNotes.java create mode 100644 src/resources/ooxml/org/apache/poi/xslf/usermodel/notesMaster.xml diff --git a/src/ooxml/java/org/apache/poi/POIXMLRelation.java b/src/ooxml/java/org/apache/poi/POIXMLRelation.java index e89090960c..8766dfff72 100644 --- a/src/ooxml/java/org/apache/poi/POIXMLRelation.java +++ b/src/ooxml/java/org/apache/poi/POIXMLRelation.java @@ -110,9 +110,18 @@ public abstract class POIXMLRelation { } return _defaultName.replace("#", Integer.toString(index)); } - + + /** + * 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 obejct used to construct instances of this relationship + * 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 */ diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java index 69dc029523..dca1aed06d 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java @@ -49,6 +49,8 @@ import org.apache.xmlbeans.XmlObject; import org.apache.xmlbeans.XmlOptions; import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraphProperties; 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.CTSlideIdList; import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideIdListEntry; @@ -130,7 +132,7 @@ public class XMLSlideShow extends POIXMLDocument { } else if (p instanceof XSLFSlideMaster) { XSLFSlideMaster master = (XSLFSlideMaster)p; _masters.put(p.getPackageRelationship().getId(), master); - }else if (p instanceof XSLFTableStyles){ + } else if (p instanceof XSLFTableStyles){ _tableStyles = (XSLFTableStyles)p; } else if (p instanceof XSLFNotesMaster) { _notesMaster = (XSLFNotesMaster)p; @@ -155,7 +157,6 @@ public class XMLSlideShow extends POIXMLDocument { } } - @Override protected void commit() throws IOException { XmlOptions xmlOptions = new XmlOptions(DEFAULT_XML_OPTIONS); @@ -231,7 +232,7 @@ public class XMLSlideShow extends POIXMLDocument { _slides.add(slide); return slide; } - + /** * Create a blank slide. */ @@ -244,7 +245,96 @@ public class XMLSlideShow extends POIXMLDocument { 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 themeIndexList = new ArrayList(); + 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. * (May not be present if no notes exist) diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFNotes.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFNotes.java index dd25953da6..0a33132e28 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFNotes.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFNotes.java @@ -16,6 +16,8 @@ ==================================================================== */ package org.apache.poi.xslf.usermodel; +import java.io.IOException; + import org.apache.poi.POIXMLDocumentPart; import org.apache.poi.openxml4j.opc.PackagePart; 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.NotesDocument; -import java.io.IOException; - @Beta public final class XSLFNotes extends XSLFSheet { private CTNotesSlide _notes; @@ -57,12 +57,10 @@ public final class XSLFNotes extends XSLFSheet { setCommonSlideData(_notes.getCSld()); } - private static CTNotesSlide prototype(){ CTNotesSlide ctNotes = CTNotesSlide.Factory.newInstance(); CTCommonSlideData cSld = ctNotes.addNewCSld(); - - // TODO What else is needed for a mininum notes? + cSld.addNewSpTree(); return ctNotes; } @@ -77,6 +75,11 @@ public final class XSLFNotes extends XSLFSheet { return "notes"; } + @Override + public XSLFTheme getTheme(){ + return getMasterSheet().getTheme(); + } + @Override public XSLFNotesMaster getMasterSheet() { for (POIXMLDocumentPart p : getRelations()) { diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFNotesMaster.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFNotesMaster.java index 77ebf05956..7ea875d36e 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFNotesMaster.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFNotesMaster.java @@ -16,15 +16,19 @@ ==================================================================== */ 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.PackageRelationship; import org.apache.poi.util.Beta; 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.NotesMasterDocument; -import java.io.IOException; - /** * Notes master object associated with this layout. *

@@ -44,10 +48,11 @@ import java.io.IOException; @Beta public class XSLFNotesMaster extends XSLFSheet { private CTNotesMaster _slide; + private XSLFTheme _theme; XSLFNotesMaster() { super(); - _slide = CTNotesMaster.Factory.newInstance(); + _slide = prototype(); } protected XSLFNotesMaster(PackagePart part, PackageRelationship rel) throws IOException, XmlException { @@ -58,6 +63,21 @@ import java.io.IOException; 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 public CTNotesMaster getXmlObject() { return _slide; @@ -72,5 +92,21 @@ import java.io.IOException; public XSLFSheet getMasterSheet() { 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; + } } \ No newline at end of file diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java index 4f156baa4f..78efb49dd1 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java @@ -999,14 +999,23 @@ public class XSLFTextParagraph implements Iterable{ "declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main' " + "declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' " + ".//p:txStyles/p:" + defaultStyleSelector +"/a:lvl" +(level+1)+ "pPr"); - if(o.length == 1){ + if (o.length == 1){ 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){ boolean ok = false; diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTheme.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTheme.java index 41f2109fcd..9c34ea55e3 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTheme.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTheme.java @@ -16,6 +16,13 @@ ==================================================================== */ 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.openxml4j.opc.PackagePart; 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.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 * @@ -52,7 +53,7 @@ public class XSLFTheme extends POIXMLDocumentPart { super(); _theme = CTOfficeStyleSheet.Factory.newInstance(); } - + public XSLFTheme(PackagePart part, PackageRelationship rel) throws IOException, XmlException { super(part, rel); ThemeDocument doc = @@ -60,6 +61,11 @@ public class XSLFTheme extends POIXMLDocumentPart { _theme = doc.getTheme(); initialize(); } + + public void importTheme(XSLFTheme theme) { + _theme = theme.getXmlObject(); + _schemeColors = theme._schemeColors; + } private void initialize(){ CTBaseStyles elems = _theme.getThemeElements(); @@ -111,7 +117,7 @@ public class XSLFTheme extends POIXMLDocumentPart { return _schemeColors.get(name); } - /** + /** * While developing only! */ @Internal diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFNotes.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFNotes.java new file mode 100644 index 0000000000..0eb075acaf --- /dev/null +++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFNotes.java @@ -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); + } + } +} diff --git a/src/resources/ooxml/org/apache/poi/xslf/usermodel/notesMaster.xml b/src/resources/ooxml/org/apache/poi/xslf/usermodel/notesMaster.xml new file mode 100644 index 0000000000..0a8db65bd8 --- /dev/null +++ b/src/resources/ooxml/org/apache/poi/xslf/usermodel/notesMaster.xml @@ -0,0 +1,2 @@ + +1.7.2013Click to edit Master text stylesSecond levelThird levelFourth levelFifth level‹#› \ No newline at end of file -- 2.39.5