123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612 |
- /* ====================================================================
- 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.apache.poi.POIXMLTypeLoader.DEFAULT_XML_OPTIONS;
-
- import java.awt.Dimension;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.OutputStream;
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.Collections;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
- import java.util.regex.Pattern;
-
- import org.apache.poi.POIXMLDocument;
- import org.apache.poi.POIXMLDocumentPart;
- import org.apache.poi.POIXMLException;
- import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
- import org.apache.poi.openxml4j.opc.OPCPackage;
- import org.apache.poi.openxml4j.opc.PackagePart;
- import org.apache.poi.sl.usermodel.MasterSheet;
- import org.apache.poi.sl.usermodel.PictureData.PictureType;
- import org.apache.poi.sl.usermodel.Resources;
- import org.apache.poi.sl.usermodel.SlideShow;
- import org.apache.poi.util.Beta;
- import org.apache.poi.util.IOUtils;
- import org.apache.poi.util.Internal;
- import org.apache.poi.util.LittleEndian;
- import org.apache.poi.util.LittleEndianConsts;
- import org.apache.poi.util.POILogFactory;
- import org.apache.poi.util.POILogger;
- import org.apache.poi.util.PackageHelper;
- import org.apache.poi.util.Units;
- import org.apache.xmlbeans.XmlException;
- import org.apache.xmlbeans.XmlObject;
- import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraphProperties;
- 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;
- import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideMasterIdListEntry;
- import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideSize;
- import org.openxmlformats.schemas.presentationml.x2006.main.PresentationDocument;
-
- /**
- * High level representation of a ooxml slideshow.
- * This is the first object most users will construct whether
- * they are reading or writing a slideshow. It is also the
- * top level object for creating new slides/etc.
- */
- @Beta
- public class XMLSlideShow extends POIXMLDocument
- implements SlideShow<XSLFShape,XSLFTextParagraph> {
- private static final POILogger LOG = POILogFactory.getLogger(XMLSlideShow.class);
-
- private CTPresentation _presentation;
- private List<XSLFSlide> _slides;
- private List<XSLFSlideMaster> _masters;
- private List<XSLFPictureData> _pictures;
- private XSLFTableStyles _tableStyles;
- private XSLFNotesMaster _notesMaster;
- private XSLFCommentAuthors _commentAuthors;
-
- public XMLSlideShow() {
- this(empty());
- }
-
- public XMLSlideShow(OPCPackage pkg) {
- super(pkg);
-
- try {
- if(getCorePart().getContentType().equals(XSLFRelation.THEME_MANAGER.getContentType())) {
- rebase(getPackage());
- }
-
- //build a tree of POIXMLDocumentParts, this presentation being the root
- load(XSLFFactory.getInstance());
- } catch (Exception e){
- throw new POIXMLException(e);
- }
- }
-
- public XMLSlideShow(InputStream is) throws IOException {
- this(PackageHelper.open(is));
- }
-
- static OPCPackage empty() {
- InputStream is = XMLSlideShow.class.getResourceAsStream("empty.pptx");
- if (is == null) {
- throw new POIXMLException("Missing resource 'empty.pptx'");
- }
- try {
- return OPCPackage.open(is);
- } catch (Exception e){
- throw new POIXMLException(e);
- } finally {
- IOUtils.closeQuietly(is);
- }
- }
-
- @Override
- protected void onDocumentRead() throws IOException {
- try {
- PresentationDocument doc =
- PresentationDocument.Factory.parse(getCorePart().getInputStream(), DEFAULT_XML_OPTIONS);
- _presentation = doc.getPresentation();
-
- Map<String, XSLFSlideMaster> masterMap = new HashMap<String, XSLFSlideMaster>();
- Map<String, XSLFSlide> shIdMap = new HashMap<String, XSLFSlide>();
- for (RelationPart rp : getRelationParts()) {
- POIXMLDocumentPart p = rp.getDocumentPart();
- if (p instanceof XSLFSlide) {
- shIdMap.put(rp.getRelationship().getId(), (XSLFSlide) p);
- } else if (p instanceof XSLFSlideMaster) {
- masterMap.put(getRelationId(p), (XSLFSlideMaster) p);
- } else if (p instanceof XSLFTableStyles){
- _tableStyles = (XSLFTableStyles)p;
- } else if (p instanceof XSLFNotesMaster) {
- _notesMaster = (XSLFNotesMaster)p;
- } else if (p instanceof XSLFCommentAuthors) {
- _commentAuthors = (XSLFCommentAuthors)p;
- }
- }
-
- _masters = new ArrayList<XSLFSlideMaster>(masterMap.size());
- for (CTSlideMasterIdListEntry masterId : _presentation.getSldMasterIdLst().getSldMasterIdList()) {
- XSLFSlideMaster master = masterMap.get(masterId.getId2());
- _masters.add(master);
- }
-
- _slides = new ArrayList<XSLFSlide>(shIdMap.size());
- if (_presentation.isSetSldIdLst()) {
- for (CTSlideIdListEntry slId : _presentation.getSldIdLst().getSldIdList()) {
- XSLFSlide sh = shIdMap.get(slId.getId2());
- if (sh == null) {
- LOG.log(POILogger.WARN, "Slide with r:id " + slId.getId() + " was defined, but didn't exist in package, skipping");
- continue;
- }
- _slides.add(sh);
- }
- }
- } catch (XmlException e) {
- throw new POIXMLException(e);
- }
- }
-
- @Override
- protected void commit() throws IOException {
- PackagePart part = getPackagePart();
- OutputStream out = part.getOutputStream();
- _presentation.save(out, DEFAULT_XML_OPTIONS);
- out.close();
- }
-
- /**
- * Get the document's embedded files.
- */
- @Override
- public List<PackagePart> getAllEmbedds() throws OpenXML4JException {
- return Collections.unmodifiableList(
- getPackage().getPartsByName(Pattern.compile("/ppt/embeddings/.*?"))
- );
- }
-
- @Override
- public List<XSLFPictureData> getPictureData() {
- if(_pictures == null){
- List<PackagePart> mediaParts = getPackage().getPartsByName(Pattern.compile("/ppt/media/.*?"));
- _pictures = new ArrayList<XSLFPictureData>(mediaParts.size());
- for(PackagePart part : mediaParts){
- XSLFPictureData pd = new XSLFPictureData(part);
- pd.setIndex(_pictures.size());
- _pictures.add(pd);
- }
- }
- return Collections.unmodifiableList(_pictures);
- }
-
- /**
- * Create a slide and initialize it from the specified layout.
- *
- * @param layout The layout to use for the new slide.
- * @return created slide
- */
- public XSLFSlide createSlide(XSLFSlideLayout layout) {
- int slideNumber = 256, cnt = 1;
- CTSlideIdList slideList;
- if (!_presentation.isSetSldIdLst()) {
- slideList = _presentation.addNewSldIdLst();
- } else {
- slideList = _presentation.getSldIdLst();
- for(CTSlideIdListEntry slideId : slideList.getSldIdArray()){
- slideNumber = (int)Math.max(slideId.getId() + 1, slideNumber);
- cnt++;
- }
-
- // Bug 55791: We also need to check that the resulting file name is not already taken
- // this can happen when removing/adding slides
- while(true) {
- String slideName = XSLFRelation.SLIDE.getFileName(cnt);
- boolean found = false;
- for (POIXMLDocumentPart relation : getRelations()) {
- if (relation.getPackagePart() != null &&
- slideName.equals(relation.getPackagePart().getPartName().getName())) {
- // name is taken => try next one
- found = true;
- break;
- }
- }
-
- if(!found &&
- getPackage().getPartsByName(Pattern.compile(Pattern.quote(slideName))).size() > 0) {
- // name is taken => try next one
- found = true;
- }
-
- if (!found) {
- break;
- }
- cnt++;
- }
- }
-
- RelationPart rp = createRelationship(
- XSLFRelation.SLIDE, XSLFFactory.getInstance(), cnt, false);
- XSLFSlide slide = rp.getDocumentPart();
-
- CTSlideIdListEntry slideId = slideList.addNewSldId();
- slideId.setId(slideNumber);
- slideId.setId2(rp.getRelationship().getId());
-
- layout.copyLayout(slide);
- slide.getPackagePart().clearRelationships();
- slide.addRelation(null, XSLFRelation.SLIDE_LAYOUT, layout);
-
- _slides.add(slide);
- return slide;
- }
-
- /**
- * Create a blank slide using the default (first) master.
- */
- @Override
- public XSLFSlide createSlide() {
- XSLFSlideMaster sm = _masters.get(0);
- XSLFSlideLayout layout = sm.getLayout(SlideLayout.BLANK);
- if (layout == null) {
- LOG.log(POILogger.WARN, "Blank layout was not found - defaulting to first slide layout in master");
- XSLFSlideLayout sl[] = sm.getSlideLayouts();
- if (sl.length == 0) {
- throw new POIXMLException("SlideMaster must contain a SlideLayout.");
- }
- layout = sl[0];
- }
-
- 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);
-
- // Bug 55791: We also need to check that the resulting file name is not already taken
- // this can happen when removing/adding slides
- while(true) {
- String slideName = XSLFRelation.NOTES.getFileName(slideIndex);
- boolean found = false;
- for (POIXMLDocumentPart relation : getRelations()) {
- if (relation.getPackagePart() != null &&
- slideName.equals(relation.getPackagePart().getPartName().getName())) {
- // name is taken => try next one
- found = true;
- break;
- }
- }
-
- if(!found &&
- getPackage().getPartsByName(Pattern.compile(Pattern.quote(slideName))).size() > 0) {
- // name is taken => try next one
- found = true;
- }
-
- if (!found) {
- break;
- }
- slideIndex++;
- }
-
- // add notes slide to presentation
- XSLFNotes notesSlide = (XSLFNotes) createRelationship
- (XSLFRelation.NOTES, XSLFFactory.getInstance(), slideIndex);
- // link slide and notes slide with each other
- slide.addRelation(null, XSLFRelation.NOTES, notesSlide);
- notesSlide.addRelation(null, XSLFRelation.NOTES_MASTER, _notesMaster);
- notesSlide.addRelation(null, XSLFRelation.SLIDE, slide);
-
- notesSlide.importContent(_notesMaster);
-
- return notesSlide;
- }
-
- /**
- * Create a notes master.
- */
- public void createNotesMaster() {
- RelationPart rp = createRelationship
- (XSLFRelation.NOTES_MASTER, XSLFFactory.getInstance(), 1, false);
- _notesMaster = rp.getDocumentPart();
-
- CTNotesMasterIdList notesMasterIdList = _presentation.addNewNotesMasterIdLst();
- CTNotesMasterIdListEntry notesMasterId = notesMasterIdList.addNewNotesMasterId();
- notesMasterId.setId(rp.getRelationship().getId());
-
- Integer themeIndex = 1;
- // TODO: check if that list can be replaced by idx = Math.max(idx,themeIdx)
- 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().get(0).getTheme());
-
- _notesMaster.addRelation(null, XSLFRelation.THEME, theme);
- }
-
- /**
- * Return the Notes Master, if there is one.
- * (May not be present if no notes exist)
- */
- public XSLFNotesMaster getNotesMaster() {
- return _notesMaster;
- }
-
- @Override
- public List<XSLFSlideMaster> getSlideMasters() {
- return _masters;
- }
-
- /**
- * Return all the slides in the slideshow
- */
- @Override
- public List<XSLFSlide> getSlides() {
- return _slides;
- }
-
- /**
- * Returns the list of comment authors, if there is one.
- * Will only be present if at least one slide has comments on it.
- */
- public XSLFCommentAuthors getCommentAuthors() {
- return _commentAuthors;
- }
-
- /**
- *
- * @param newIndex 0-based index of the slide
- */
- public void setSlideOrder(XSLFSlide slide, int newIndex){
- int oldIndex = _slides.indexOf(slide);
- if(oldIndex == -1) {
- throw new IllegalArgumentException("Slide not found");
- }
- if (oldIndex == newIndex) {
- return;
- }
-
- // fix the usermodel container
- _slides.add(newIndex, _slides.remove(oldIndex));
-
- // fix ordering in the low-level xml
- CTSlideIdList sldIdLst = _presentation.getSldIdLst();
- CTSlideIdListEntry[] entries = sldIdLst.getSldIdArray();
- CTSlideIdListEntry oldEntry = entries[oldIndex];
- if (oldIndex < newIndex) {
- System.arraycopy(entries, oldIndex + 1, entries, oldIndex, newIndex - oldIndex);
- } else {
- System.arraycopy(entries, newIndex, entries, newIndex + 1, oldIndex - newIndex);
- }
- entries[newIndex] = oldEntry;
- sldIdLst.setSldIdArray(entries);
- }
-
- public XSLFSlide removeSlide(int index){
- XSLFSlide slide = _slides.remove(index);
- removeRelation(slide);
- _presentation.getSldIdLst().removeSldId(index);
- return slide;
- }
-
- @Override
- public Dimension getPageSize(){
- CTSlideSize sz = _presentation.getSldSz();
- int cx = sz.getCx();
- int cy = sz.getCy();
- return new Dimension((int)Units.toPoints(cx), (int)Units.toPoints(cy));
- }
-
- @Override
- public void setPageSize(Dimension pgSize){
- CTSlideSize sz = CTSlideSize.Factory.newInstance();
- sz.setCx(Units.toEMU(pgSize.getWidth()));
- sz.setCy(Units.toEMU(pgSize.getHeight()));
- _presentation.setSldSz(sz);
- }
-
-
- @Internal
- public CTPresentation getCTPresentation(){
- return _presentation;
- }
-
- /**
- * Adds a picture to the workbook.
- *
- * @param pictureData The bytes of the picture
- * @param format The format of the picture.
- *
- * @return the picture data
- */
- @Override
- public XSLFPictureData addPicture(byte[] pictureData, PictureType format) {
- XSLFPictureData img = findPictureData(pictureData);
-
- if (img != null) {
- return img;
- }
-
- int imageNumber = _pictures.size();
- XSLFRelation relType = XSLFPictureData.getRelationForType(format);
- if (relType == null) {
- throw new IllegalArgumentException("Picture type "+format+" is not supported.");
- }
- img = (XSLFPictureData) createRelationship(relType, XSLFFactory.getInstance(), imageNumber + 1, true).getDocumentPart();
- img.setIndex(imageNumber);
- _pictures.add(img);
- try {
- OutputStream out = img.getPackagePart().getOutputStream();
- out.write(pictureData);
- out.close();
- } catch (IOException e) {
- throw new POIXMLException(e);
- }
-
- return img;
- }
-
-
- /**
- * Adds a picture to the slideshow.
- *
- * @param is The stream to read image from
- * @param format The format of the picture
- *
- * @return the picture data
- * @since 3.15 beta 2
- */
- @Override
- public XSLFPictureData addPicture(InputStream is, PictureType format) throws IOException
- {
- return addPicture(IOUtils.toByteArray(is), format);
- }
-
-
- /**
- * Adds a picture to the presentation.
- *
- * @param pict The file containing the image to add
- * @param format The format of the picture.
- *
- * @return the picture data
- * @since 3.15 beta 2
- */
- @Override
- public XSLFPictureData addPicture(File pict, PictureType format) throws IOException
- {
- int length = (int) pict.length();
- byte[] data = new byte[length];
- FileInputStream is = new FileInputStream(pict);
- try {
- IOUtils.readFully(is, data);
- } finally {
- is.close();
- }
- return addPicture(data, format);
- }
-
-
- /**
- * check if a picture with this picture data already exists in this presentation
- *
- * @param pictureData The picture data to find in the SlideShow
- * @return {@code null} if picture data is not found in this slideshow
- * @since 3.15 beta 2
- */
- @Override
- public XSLFPictureData findPictureData(byte[] pictureData) {
- long checksum = IOUtils.calculateChecksum(pictureData);
- byte cs[] = new byte[LittleEndianConsts.LONG_SIZE];
- LittleEndian.putLong(cs,0,checksum);
-
- for(XSLFPictureData pic : getPictureData()){
- if(Arrays.equals(pic.getChecksum(), cs)) {
- return pic;
- }
- }
- return null;
- }
-
-
- /**
- * Scan the master slides for the first slide layout with the given name.
- *
- * @param name The layout name (case-insensitive). Cannot be null.
- * @return the first layout found or null on failure
- */
- public XSLFSlideLayout findLayout(String name) {
- for (XSLFSlideMaster master : getSlideMasters()) {
- XSLFSlideLayout layout = master.getLayout(name);
- if (layout != null) {
- return layout;
- }
- }
- return null;
- }
-
-
- public XSLFTableStyles getTableStyles(){
- return _tableStyles;
- }
-
- CTTextParagraphProperties getDefaultParagraphStyle(int level) {
- XmlObject[] o = _presentation.selectPath(
- "declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main' " +
- "declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' " +
- ".//p:defaultTextStyle/a:lvl" +(level+1)+ "pPr");
- if(o.length == 1){
- return (CTTextParagraphProperties)o[0];
- }
- return null;
- }
-
- @Override
- public MasterSheet<XSLFShape,XSLFTextParagraph> createMasterSheet() throws IOException {
- // TODO: implement!
- throw new UnsupportedOperationException();
- }
-
- @Override
- public Resources getResources() {
- // TODO: implement!
- throw new UnsupportedOperationException();
- }
- }
|