| /* ==================================================================== |
| 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.hslf.usermodel; |
| |
| import java.awt.Graphics2D; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.apache.poi.ddf.EscherContainerRecord; |
| import org.apache.poi.ddf.EscherDgRecord; |
| import org.apache.poi.ddf.EscherDggRecord; |
| import org.apache.poi.ddf.EscherRecordTypes; |
| import org.apache.poi.ddf.EscherSpRecord; |
| import org.apache.poi.hslf.exceptions.HSLFException; |
| import org.apache.poi.hslf.model.HeadersFooters; |
| import org.apache.poi.hslf.record.CString; |
| import org.apache.poi.hslf.record.ColorSchemeAtom; |
| import org.apache.poi.hslf.record.Comment2000; |
| import org.apache.poi.hslf.record.EscherTextboxWrapper; |
| import org.apache.poi.hslf.record.HeadersFootersContainer; |
| import org.apache.poi.hslf.record.RecordContainer; |
| import org.apache.poi.hslf.record.RecordTypes; |
| import org.apache.poi.hslf.record.SSSlideInfoAtom; |
| import org.apache.poi.hslf.record.SlideAtom; |
| import org.apache.poi.hslf.record.SlideAtomLayout.SlideLayoutType; |
| import org.apache.poi.hslf.record.SlideListWithText.SlideAtomsSet; |
| import org.apache.poi.hslf.record.StyleTextProp9Atom; |
| import org.apache.poi.hslf.record.TextHeaderAtom; |
| import org.apache.poi.sl.draw.DrawFactory; |
| import org.apache.poi.sl.draw.Drawable; |
| import org.apache.poi.sl.usermodel.Notes; |
| import org.apache.poi.sl.usermodel.Placeholder; |
| import org.apache.poi.sl.usermodel.ShapeType; |
| import org.apache.poi.sl.usermodel.Slide; |
| import org.apache.poi.sl.usermodel.TextShape.TextPlaceholder; |
| |
| /** |
| * This class represents a slide in a PowerPoint Document. It allows |
| * access to the text within, and the layout. For now, it only does |
| * the text side of things though |
| */ |
| |
| public final class HSLFSlide extends HSLFSheet implements Slide<HSLFShape,HSLFTextParagraph> { |
| private int _slideNo; |
| private SlideAtomsSet _atomSet; |
| private final List<List<HSLFTextParagraph>> _paragraphs = new ArrayList<>(); |
| private HSLFNotes _notes; // usermodel needs to set this |
| |
| /** |
| * Constructs a Slide from the Slide record, and the SlideAtomsSet |
| * containing the text. |
| * Initializes TextRuns, to provide easier access to the text |
| * |
| * @param slide the Slide record we're based on |
| * @param notes the Notes sheet attached to us |
| * @param atomSet the SlideAtomsSet to get the text from |
| */ |
| public HSLFSlide(org.apache.poi.hslf.record.Slide slide, HSLFNotes notes, SlideAtomsSet atomSet, int slideIdentifier, int slideNumber) { |
| super(slide, slideIdentifier); |
| |
| _notes = notes; |
| _atomSet = atomSet; |
| _slideNo = slideNumber; |
| |
| // For the text coming in from the SlideAtomsSet: |
| // Build up TextRuns from pairs of TextHeaderAtom and |
| // one of TextBytesAtom or TextCharsAtom |
| if (_atomSet != null && _atomSet.getSlideRecords().length > 0) { |
| // Grab text from SlideListWithTexts entries |
| _paragraphs.addAll(HSLFTextParagraph.findTextParagraphs(_atomSet.getSlideRecords())); |
| if (_paragraphs.isEmpty()) { |
| throw new HSLFException("No text records found for slide"); |
| } |
| } |
| |
| // Grab text from slide's PPDrawing |
| for (List<HSLFTextParagraph> l : HSLFTextParagraph.findTextParagraphs(getPPDrawing(), this)) { |
| if (!_paragraphs.contains(l)) { |
| _paragraphs.add(l); |
| } |
| } |
| } |
| |
| /** |
| * Create a new Slide instance |
| * @param sheetNumber The internal number of the sheet, as used by PersistPtrHolder |
| * @param slideNumber The user facing number of the sheet |
| */ |
| public HSLFSlide(int sheetNumber, int sheetRefId, int slideNumber){ |
| super(new org.apache.poi.hslf.record.Slide(), sheetNumber); |
| _slideNo = slideNumber; |
| getSheetContainer().setSheetId(sheetRefId); |
| } |
| |
| /** |
| * Returns the Notes Sheet for this slide, or null if there isn't one |
| */ |
| @Override |
| public HSLFNotes getNotes() { |
| return _notes; |
| } |
| |
| /** |
| * Sets the Notes that are associated with this. Updates the |
| * references in the records to point to the new ID |
| */ |
| @Override |
| public void setNotes(Notes<HSLFShape,HSLFTextParagraph> notes) { |
| if (notes != null && !(notes instanceof HSLFNotes)) { |
| throw new IllegalArgumentException("notes needs to be of type HSLFNotes"); |
| } |
| _notes = (HSLFNotes)notes; |
| |
| // Update the Slide Atom's ID of where to point to |
| SlideAtom sa = getSlideRecord().getSlideAtom(); |
| |
| if(_notes == null) { |
| // Set to 0 |
| sa.setNotesID(0); |
| } else { |
| // Set to the value from the notes' sheet id |
| sa.setNotesID(_notes._getSheetNumber()); |
| } |
| } |
| |
| /** |
| * Changes the Slide's (external facing) page number. |
| * @see org.apache.poi.hslf.usermodel.HSLFSlideShow#reorderSlide(int, int) |
| */ |
| public void setSlideNumber(int newSlideNumber) { |
| _slideNo = newSlideNumber; |
| } |
| |
| /** |
| * Called by SlideShow ater a new slide is created. |
| * <p> |
| * For Slide we need to do the following: |
| * <ul> |
| * <li> set id of the drawing group.</li> |
| * <li> set shapeId for the container descriptor and background</li> |
| * </ul> |
| */ |
| @Override |
| public void onCreate(){ |
| //initialize drawing group id |
| EscherDggRecord dgg = getSlideShow().getDocumentRecord().getPPDrawingGroup().getEscherDggRecord(); |
| EscherContainerRecord dgContainer = getSheetContainer().getPPDrawing().getDgContainer(); |
| EscherDgRecord dg = HSLFShape.getEscherChild(dgContainer, EscherDgRecord.RECORD_ID); |
| int dgId = dgg.getMaxDrawingGroupId() + 1; |
| dg.setOptions((short)(dgId << 4)); |
| dgg.setDrawingsSaved(dgg.getDrawingsSaved() + 1); |
| |
| for (EscherContainerRecord c : dgContainer.getChildContainers()) { |
| EscherSpRecord spr = null; |
| switch(EscherRecordTypes.forTypeID(c.getRecordId())){ |
| case SPGR_CONTAINER: |
| EscherContainerRecord dc = (EscherContainerRecord)c.getChild(0); |
| spr = dc.getChildById(EscherSpRecord.RECORD_ID); |
| break; |
| case SP_CONTAINER: |
| spr = c.getChildById(EscherSpRecord.RECORD_ID); |
| break; |
| default: |
| break; |
| } |
| if(spr != null) { |
| spr.setShapeId(allocateShapeId()); |
| } |
| } |
| |
| //PPT doen't increment the number of saved shapes for group descriptor and background |
| dg.setNumShapes(1); |
| } |
| |
| /** |
| * Create a {@code TextBox} object that represents the slide's title. |
| * |
| * @return {@code TextBox} object that represents the slide's title. |
| */ |
| public HSLFTextBox addTitle() { |
| HSLFPlaceholder pl = new HSLFPlaceholder(); |
| pl.setShapeType(ShapeType.RECT); |
| pl.setPlaceholder(Placeholder.TITLE); |
| pl.setRunType(TextPlaceholder.TITLE.nativeId); |
| pl.setText("Click to edit title"); |
| pl.setAnchor(new java.awt.Rectangle(54, 48, 612, 90)); |
| addShape(pl); |
| return pl; |
| } |
| |
| |
| // Complex Accesser methods follow |
| |
| /** |
| * <p> |
| * The title is a run of text of type {@code TextHeaderAtom.CENTER_TITLE_TYPE} or |
| * {@code TextHeaderAtom.TITLE_TYPE} |
| * </p> |
| * |
| * @see TextHeaderAtom |
| */ |
| @Override |
| public String getTitle(){ |
| for (List<HSLFTextParagraph> tp : getTextParagraphs()) { |
| if (tp.isEmpty()) { |
| continue; |
| } |
| int type = tp.get(0).getRunType(); |
| if (TextPlaceholder.isTitle(type)) { |
| String str = HSLFTextParagraph.getRawText(tp); |
| return HSLFTextParagraph.toExternalString(str, type); |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public String getSlideName() { |
| final CString name = (CString)getSlideRecord().findFirstOfType(RecordTypes.CString.typeID); |
| return name != null ? name.getText() : "Slide"+getSlideNumber(); |
| } |
| |
| |
| /** |
| * Returns an array of all the TextRuns found |
| */ |
| @Override |
| public List<List<HSLFTextParagraph>> getTextParagraphs() { return _paragraphs; } |
| |
| /** |
| * Returns the (public facing) page number of this slide |
| */ |
| @Override |
| public int getSlideNumber() { return _slideNo; } |
| |
| /** |
| * Returns the underlying slide record |
| */ |
| public org.apache.poi.hslf.record.Slide getSlideRecord() { |
| return (org.apache.poi.hslf.record.Slide)getSheetContainer(); |
| } |
| |
| /** |
| * @return set of records inside {@code SlideListWithtext} container |
| * which hold text data for this slide (typically for placeholders). |
| */ |
| protected SlideAtomsSet getSlideAtomsSet() { return _atomSet; } |
| |
| /** |
| * Returns master sheet associated with this slide. |
| * It can be either SlideMaster or TitleMaster objects. |
| * |
| * @return the master sheet associated with this slide. |
| */ |
| @Override |
| public HSLFMasterSheet getMasterSheet(){ |
| int masterId = getSlideRecord().getSlideAtom().getMasterID(); |
| for (HSLFSlideMaster sm : getSlideShow().getSlideMasters()) { |
| if (masterId == sm._getSheetNumber()) { |
| return sm; |
| } |
| } |
| for (HSLFTitleMaster tm : getSlideShow().getTitleMasters()) { |
| if (masterId == tm._getSheetNumber()) { |
| return tm; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Change Master of this slide. |
| */ |
| public void setMasterSheet(HSLFMasterSheet master){ |
| SlideAtom sa = getSlideRecord().getSlideAtom(); |
| int sheetNo = master._getSheetNumber(); |
| sa.setMasterID(sheetNo); |
| } |
| |
| /** |
| * Sets whether this slide follows master background |
| * |
| * @param flag {@code true} if the slide follows master, |
| * {@code false} otherwise |
| */ |
| @Override |
| public void setFollowMasterBackground(boolean flag){ |
| SlideAtom sa = getSlideRecord().getSlideAtom(); |
| sa.setFollowMasterBackground(flag); |
| } |
| |
| /** |
| * Whether this slide follows master sheet background |
| * |
| * @return {@code true} if the slide follows master background, |
| * {@code false} otherwise |
| */ |
| @Override |
| public boolean getFollowMasterBackground(){ |
| SlideAtom sa = getSlideRecord().getSlideAtom(); |
| return sa.getFollowMasterBackground(); |
| } |
| |
| /** |
| * Sets whether this slide draws master sheet objects |
| * |
| * @param flag {@code true} if the slide draws master sheet objects, |
| * {@code false} otherwise |
| */ |
| @Override |
| public void setFollowMasterObjects(boolean flag){ |
| SlideAtom sa = getSlideRecord().getSlideAtom(); |
| sa.setFollowMasterObjects(flag); |
| } |
| |
| /** |
| * Whether this slide follows master color scheme |
| * |
| * @return {@code true} if the slide follows master color scheme, |
| * {@code false} otherwise |
| */ |
| public boolean getFollowMasterScheme(){ |
| SlideAtom sa = getSlideRecord().getSlideAtom(); |
| return sa.getFollowMasterScheme(); |
| } |
| |
| /** |
| * Sets whether this slide draws master color scheme |
| * |
| * @param flag {@code true} if the slide draws master color scheme, |
| * {@code false} otherwise |
| */ |
| public void setFollowMasterScheme(boolean flag){ |
| SlideAtom sa = getSlideRecord().getSlideAtom(); |
| sa.setFollowMasterScheme(flag); |
| } |
| |
| /** |
| * Whether this slide draws master sheet objects |
| * |
| * @return {@code true} if the slide draws master sheet objects, |
| * {@code false} otherwise |
| */ |
| @Override |
| public boolean getFollowMasterObjects(){ |
| SlideAtom sa = getSlideRecord().getSlideAtom(); |
| return sa.getFollowMasterObjects(); |
| } |
| |
| /** |
| * Background for this slide. |
| */ |
| @Override |
| public HSLFBackground getBackground() { |
| if (getFollowMasterBackground()) { |
| final HSLFMasterSheet ms = getMasterSheet(); |
| return (ms == null) ? null : ms.getBackground(); |
| } |
| return super.getBackground(); |
| } |
| |
| /** |
| * Color scheme for this slide. |
| */ |
| @Override |
| public ColorSchemeAtom getColorScheme() { |
| if (getFollowMasterScheme()) { |
| final HSLFMasterSheet ms = getMasterSheet(); |
| return (ms == null) ? null : ms.getColorScheme(); |
| } |
| return super.getColorScheme(); |
| } |
| |
| private static RecordContainer selectContainer(final RecordContainer root, final int index, final RecordTypes... path) { |
| if (root == null || index >= path.length) { |
| return root; |
| } |
| final RecordContainer newRoot = (RecordContainer) root.findFirstOfType(path[index].typeID); |
| return selectContainer(newRoot, index+1, path); |
| } |
| |
| /** |
| * Get the comment(s) for this slide. |
| * Note - for now, only works on PPT 2000 and |
| * PPT 2003 files. Doesn't work for PPT 97 |
| * ones, as they do their comments oddly. |
| */ |
| @Override |
| public List<HSLFComment> getComments() { |
| final List<HSLFComment> comments = new ArrayList<>(); |
| // If there are any, they're in |
| // ProgTags -> ProgBinaryTag -> BinaryTagData |
| final RecordContainer binaryTags = |
| selectContainer(getSheetContainer(), 0, |
| RecordTypes.ProgTags, RecordTypes.ProgBinaryTag, RecordTypes.BinaryTagData); |
| |
| if (binaryTags != null) { |
| for (final org.apache.poi.hslf.record.Record record : binaryTags.getChildRecords()) { |
| if (record instanceof Comment2000) { |
| comments.add(new HSLFComment((Comment2000)record)); |
| } |
| } |
| } |
| |
| return comments; |
| } |
| |
| /** |
| * Header / Footer settings for this slide. |
| * |
| * @return Header / Footer settings for this slide |
| */ |
| @Override |
| public HeadersFooters getHeadersFooters(){ |
| return new HeadersFooters(this, HeadersFootersContainer.SlideHeadersFootersContainer); |
| } |
| |
| @Override |
| protected void onAddTextShape(HSLFTextShape shape) { |
| List<HSLFTextParagraph> newParas = shape.getTextParagraphs(); |
| _paragraphs.add(newParas); |
| } |
| |
| /** This will return an atom per TextBox, so if the page has two text boxes the method should return two atoms. */ |
| public StyleTextProp9Atom[] getNumberedListInfo() { |
| return this.getPPDrawing().getNumberedListInfo(); |
| } |
| |
| public EscherTextboxWrapper[] getTextboxWrappers() { |
| return this.getPPDrawing().getTextboxWrappers(); |
| } |
| |
| @Override |
| public void setHidden(boolean hidden) { |
| org.apache.poi.hslf.record.Slide cont = getSlideRecord(); |
| |
| SSSlideInfoAtom slideInfo = |
| (SSSlideInfoAtom)cont.findFirstOfType(RecordTypes.SSSlideInfoAtom.typeID); |
| if (slideInfo == null) { |
| slideInfo = new SSSlideInfoAtom(); |
| cont.addChildAfter(slideInfo, cont.findFirstOfType(RecordTypes.SlideAtom.typeID)); |
| } |
| |
| slideInfo.setEffectTransitionFlagByBit(SSSlideInfoAtom.HIDDEN_BIT, hidden); |
| } |
| |
| @Override |
| public boolean isHidden() { |
| SSSlideInfoAtom slideInfo = |
| (SSSlideInfoAtom)getSlideRecord().findFirstOfType(RecordTypes.SSSlideInfoAtom.typeID); |
| return (slideInfo != null) && slideInfo.getEffectTransitionFlagByBit(SSSlideInfoAtom.HIDDEN_BIT); |
| } |
| |
| @Override |
| public void draw(Graphics2D graphics) { |
| DrawFactory drawFact = DrawFactory.getInstance(graphics); |
| Drawable draw = drawFact.getDrawable(this); |
| draw.draw(graphics); |
| } |
| |
| @Override |
| public boolean getFollowMasterColourScheme() { |
| return false; |
| } |
| |
| @Override |
| public void setFollowMasterColourScheme(boolean follow) { |
| } |
| |
| @Override |
| public boolean getFollowMasterGraphics() { |
| return getFollowMasterObjects(); |
| } |
| |
| @Override |
| public boolean getDisplayPlaceholder(final Placeholder placeholder) { |
| final HeadersFooters hf = getHeadersFooters(); |
| final SlideLayoutType slt = getSlideRecord().getSlideAtom().getSSlideLayoutAtom().getGeometryType(); |
| final boolean isTitle = |
| (slt == SlideLayoutType.TITLE_SLIDE || slt == SlideLayoutType.TITLE_ONLY || slt == SlideLayoutType.MASTER_TITLE); |
| switch (placeholder) { |
| case DATETIME: |
| return hf.isDateTimeVisible() && !isTitle; |
| case SLIDE_NUMBER: |
| return hf.isSlideNumberVisible() && !isTitle; |
| case HEADER: |
| return hf.isHeaderVisible() && !isTitle; |
| case FOOTER: |
| return hf.isFooterVisible() && !isTitle; |
| default: |
| return false; |
| } |
| } |
| |
| @Override |
| public HSLFMasterSheet getSlideLayout(){ |
| // TODO: find out how we can find the mastersheet base on the slide layout type, i.e. |
| // getSlideRecord().getSlideAtom().getSSlideLayoutAtom().getGeometryType() |
| return getMasterSheet(); |
| } |
| |
| } |