diff --git a/src/documentation/content/xdocs/changes.xml b/src/documentation/content/xdocs/changes.xml index 62b53e541..985db6ab9 100644 --- a/src/documentation/content/xdocs/changes.xml +++ b/src/documentation/content/xdocs/changes.xml @@ -37,6 +37,7 @@ + 45590 - Fix for Header/footer extraction for .ppt files saved in Office 2007 Big improvement in how HWPF handles unicode text, and more sanity checking of text ranges within HWPF Include headers and footers int he extracted text from HWPF's WordExtractor Added support to HWPF for headers and footers diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index a020a892a..d762f1e5a 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -34,6 +34,7 @@ + 45590 - Fix for Header/footer extraction for .ppt files saved in Office 2007 Big improvement in how HWPF handles unicode text, and more sanity checking of text ranges within HWPF Include headers and footers int he extracted text from HWPF's WordExtractor Added support to HWPF for headers and footers diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/HeadersFooters.java b/src/scratchpad/src/org/apache/poi/hslf/model/HeadersFooters.java index 90aee3421..38b0dff9e 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/HeadersFooters.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/HeadersFooters.java @@ -32,11 +32,22 @@ public class HeadersFooters { private HeadersFootersContainer _container; private boolean _newRecord; private SlideShow _ppt; + private Sheet _sheet; + private boolean _ppt2007; - public HeadersFooters(HeadersFootersContainer rec, SlideShow ppt, boolean newRecord){ + + public HeadersFooters(HeadersFootersContainer rec, SlideShow ppt, boolean newRecord, boolean isPpt2007){ _container = rec; _newRecord = newRecord; _ppt = ppt; + _ppt2007 = isPpt2007; + } + + public HeadersFooters(HeadersFootersContainer rec, Sheet sheet, boolean newRecord, boolean isPpt2007){ + _container = rec; + _newRecord = newRecord; + _sheet = sheet; + _ppt2007 = isPpt2007; } /** @@ -45,8 +56,8 @@ public class HeadersFooters { * @return Headers's text */ public String getHeaderText(){ - CString cs = _container.getHeaderAtom(); - return cs == null ? null : cs.getText(); + CString cs = _container == null ? null : _container.getHeaderAtom(); + return getPlaceholderText(OEPlaceholderAtom.MasterHeader, cs); } /** @@ -70,8 +81,8 @@ public class HeadersFooters { * @return Footer's text */ public String getFooterText(){ - CString cs = _container.getFooterAtom(); - return cs == null ? null : cs.getText(); + CString cs = _container == null ? null : _container.getFooterAtom(); + return getPlaceholderText(OEPlaceholderAtom.MasterFooter, cs); } /** @@ -95,8 +106,8 @@ public class HeadersFooters { * @return custom user date */ public String getDateTimeText(){ - CString cs = _container.getUserDateAtom(); - return cs == null ? null : cs.getText(); + CString cs = _container == null ? null : _container.getUserDateAtom(); + return getPlaceholderText(OEPlaceholderAtom.MasterDate, cs); } /** @@ -119,7 +130,7 @@ public class HeadersFooters { * whether the footer text is displayed. */ public boolean isFooterVisible(){ - return _container.getHeadersFootersAtom().getFlag(HeadersFootersAtom.fHasFooter); + return isVisible(HeadersFootersAtom.fHasFooter, OEPlaceholderAtom.MasterFooter); } /** @@ -134,7 +145,7 @@ public class HeadersFooters { * whether the header text is displayed. */ public boolean isHeaderVisible(){ - return _container.getHeadersFootersAtom().getFlag(HeadersFootersAtom.fHasHeader); + return isVisible(HeadersFootersAtom.fHasHeader, OEPlaceholderAtom.MasterHeader); } /** @@ -149,7 +160,7 @@ public class HeadersFooters { * whether the date is displayed in the footer. */ public boolean isDateTimeVisible(){ - return _container.getHeadersFootersAtom().getFlag(HeadersFootersAtom.fHasDate); + return isVisible(HeadersFootersAtom.fHasDate, OEPlaceholderAtom.MasterDate); } /** @@ -164,7 +175,7 @@ public class HeadersFooters { * whether the custom user date is used instead of today's date. */ public boolean isUserDateVisible(){ - return _container.getHeadersFootersAtom().getFlag(HeadersFootersAtom.fHasUserDate); + return isVisible(HeadersFootersAtom.fHasUserDate, OEPlaceholderAtom.MasterDate); } /** @@ -179,7 +190,7 @@ public class HeadersFooters { * whether the slide number is displayed in the footer. */ public boolean isSlideNumberVisible(){ - return _container.getHeadersFootersAtom().getFlag(HeadersFootersAtom.fHasSlideNumber); + return isVisible(HeadersFootersAtom.fHasSlideNumber, OEPlaceholderAtom.MasterSlideNumber); } /** @@ -225,4 +236,32 @@ public class HeadersFooters { doc.addChildAfter(_container, lst); _newRecord = false; } + + private boolean isVisible(int flag, int placeholderId){ + boolean visible; + if(_ppt2007){ + Sheet master = _sheet != null ? _sheet : _ppt.getSlidesMasters()[0]; + TextShape placeholder = master.getPlaceholder(placeholderId); + visible = placeholder != null && placeholder.getText() != null; + } else { + visible = _container.getHeadersFootersAtom().getFlag(flag); + } + return visible; + } + + private String getPlaceholderText(int placeholderId, CString cs){ + String text = null; + if(_ppt2007){ + Sheet master = _sheet != null ? _sheet : _ppt.getSlidesMasters()[0]; + TextShape placeholder = master.getPlaceholder(placeholderId); + if(placeholder != null) text = placeholder.getText(); + + //default text in master placeholders is not visible + if("*".equals(text)) text = null; + } else { + text = cs == null ? null : cs.getText(); + } + return text; + } + } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/MasterSheet.java b/src/scratchpad/src/org/apache/poi/hslf/model/MasterSheet.java index 17c1d1c23..501cf65bd 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/MasterSheet.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/MasterSheet.java @@ -19,6 +19,7 @@ package org.apache.poi.hslf.model; import org.apache.poi.hslf.record.SheetContainer; import org.apache.poi.hslf.record.Record; import org.apache.poi.hslf.record.RecordTypes; +import org.apache.poi.hslf.record.OEPlaceholderAtom; import org.apache.poi.hslf.model.textproperties.TextProp; /** @@ -54,20 +55,4 @@ public abstract class MasterSheet extends Sheet { return tx.getPlaceholderAtom() != null; } - /** - * Return placeholder by text type - */ - public TextShape getPlaceholder(int type){ - Shape[] shape = getShapes(); - for (int i = 0; i < shape.length; i++) { - if(shape[i] instanceof TextShape){ - TextShape tx = (TextShape)shape[i]; - TextRun run = tx.getTextRun(); - if(run != null && run.getRunType() == type){ - return tx; - } - } - } - return null; - } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Sheet.java b/src/scratchpad/src/org/apache/poi/hslf/model/Sheet.java index 6eb84ca2e..ccbc03829 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/Sheet.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/Sheet.java @@ -362,4 +362,83 @@ public abstract class Sheet { public void draw(Graphics2D graphics){ } + + /** + * Return placeholder by text type + * + * @param type type of text, See {@link org.apache.poi.hslf.record.TextHeaderAtom} + * @return TextShape or null + */ + public TextShape getPlaceholderByTextType(int type){ + Shape[] shape = getShapes(); + for (int i = 0; i < shape.length; i++) { + if(shape[i] instanceof TextShape){ + TextShape tx = (TextShape)shape[i]; + TextRun run = tx.getTextRun(); + if(run != null && run.getRunType() == type){ + return tx; + } + } + } + return null; + } + + /** + * Search text placeholer by its type + * + * @param type type of placeholder to search. See {@link org.apache.poi.hslf.record.OEPlaceholderAtom} + * @return TextShape or null + */ + public TextShape getPlaceholder(int type){ + Shape[] shape = getShapes(); + for (int i = 0; i < shape.length; i++) { + if(shape[i] instanceof TextShape){ + TextShape tx = (TextShape)shape[i]; + int placeholderId = 0; + OEPlaceholderAtom oep = tx.getPlaceholderAtom(); + if(oep != null) { + placeholderId = oep.getPlaceholderId(); + } else { + //special case for files saved in Office 2007 + RoundTripHFPlaceholder12 hldr = (RoundTripHFPlaceholder12)tx.getClientDataRecord(RecordTypes.RoundTripHFPlaceholder12.typeID); + if(hldr != null) placeholderId = hldr.getPlaceholderId(); + } + if(placeholderId == type){ + return tx; + } + } + } + return null; + } + + /** + * Return programmable tag associated with this sheet, e.g. ___PPT12. + * + * @return programmable tag associated with this sheet. + */ + public String getProgrammableTag(){ + String tag = null; + RecordContainer progTags = (RecordContainer) + getSheetContainer().findFirstOfType( + RecordTypes.ProgTags.typeID + ); + if(progTags != null) { + RecordContainer progBinaryTag = (RecordContainer) + progTags.findFirstOfType( + RecordTypes.ProgBinaryTag.typeID + ); + if(progBinaryTag != null) { + CString binaryTag = (CString) + progBinaryTag.findFirstOfType( + RecordTypes.CString.typeID + ); + if(binaryTag != null) tag = binaryTag.getText(); + } + } + + return tag; + + } + + } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Slide.java b/src/scratchpad/src/org/apache/poi/hslf/model/Slide.java index 48f2fdefc..c524f31f7 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/Slide.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/Slide.java @@ -440,14 +440,24 @@ public class Slide extends Sheet public HeadersFooters getHeadersFooters(){ HeadersFootersContainer hdd = null; Record[] ch = getSheetContainer().getChildRecords(); + boolean ppt2007 = false; for (int i = 0; i < ch.length; i++) { if(ch[i] instanceof HeadersFootersContainer){ hdd = (HeadersFootersContainer)ch[i]; - break; + } else if (ch[i].getRecordType() == RecordTypes.RoundTripContentMasterId.typeID){ + ppt2007 = true; } } boolean newRecord = false; - if(hdd == null) return getSlideShow().getSlideHeadersFooters(); - else return new HeadersFooters(hdd, getSlideShow(), newRecord); + if(hdd == null && !ppt2007) { + return getSlideShow().getSlideHeadersFooters(); + } + else { + if(hdd == null) { + hdd = new HeadersFootersContainer(HeadersFootersContainer.SlideHeadersFootersContainer); + newRecord = true; + } + return new HeadersFooters(hdd, this, newRecord, ppt2007); + } } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/TextShape.java b/src/scratchpad/src/org/apache/poi/hslf/model/TextShape.java index 44cb2b241..a9975ab37 100755 --- a/src/scratchpad/src/org/apache/poi/hslf/model/TextShape.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/TextShape.java @@ -269,7 +269,7 @@ public abstract class TextShape extends SimpleShape { int type = getTextRun().getRunType(); MasterSheet master = getSheet().getMasterSheet(); if(master != null){ - TextShape masterShape = master.getPlaceholder(type); + TextShape masterShape = master.getPlaceholderByTextType(type); if(masterShape != null) valign = masterShape.getVerticalAlignment(); } else { //not found in the master sheet. Use the hardcoded defaults. diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/OEPlaceholderAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/OEPlaceholderAtom.java index 62a624af9..9bfae4fcc 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/OEPlaceholderAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/OEPlaceholderAtom.java @@ -26,7 +26,7 @@ import java.io.OutputStream; /** * OEPlaceholderAtom (3011). *

- * Atom that describes the placeholder. + * An atom record that specifies whether a shape is a placeholder shape. *

* * @author Yegor Kozlov @@ -34,61 +34,169 @@ import java.io.OutputStream; public class OEPlaceholderAtom extends RecordAtom{ + /** + * The full size of the master body text placeholder shape. + */ public static final int PLACEHOLDER_FULLSIZE = 0; + + /** + * Half of the size of the master body text placeholder shape. + */ public static final int PLACEHOLDER_HALFSIZE = 1; + + /** + * A quarter of the size of the master body text placeholder shape. + */ public static final int PLACEHOLDER_QUARTSIZE = 2; + /** + * MUST NOT be used for this field. + */ public static final byte None = 0; + /** + * The corresponding shape contains the master title text. + * The corresponding slide MUST be a main master slide. + */ public static final byte MasterTitle = 1; + /** + * The corresponding shape contains the master body text. + * The corresponding slide MUST be a main master slide. + */ public static final byte MasterBody = 2; + /** + * The corresponding shape contains the master center title text. + * The corresponding slide MUST be a title master slide. + */ public static final byte MasterCenteredTitle = 3; - public static final byte MasterNotesSlideImage = 4; + /** + * The corresponding shape contains the master sub-title text. + * The corresponding slide MUST be a title master slide. + */ + public static final byte MasterSubTitle = 4; - public static final byte MasterNotesBodyImage = 5; + /** + * The corresponding shape contains the shared properties for slide image shapes. + * The corresponding slide MUST be a notes master slide. + */ + public static final byte MasterNotesSlideImage = 5; - public static final byte MasterDate = 6; + /** + * The corresponding shape contains the master body text. + * The corresponding slide MUST be a notes master slide. + */ + public static final byte MasterNotesBody = 6; - public static final byte MasterSlideNumber = 7; + /** + * The corresponding shape contains the date text field. + * The corresponding slide MUST be a main master slide, title master slide, notes master slide, or handout master slide. + */ + public static final byte MasterDate = 7; - public static final byte MasterFooter = 8; + /** + * The corresponding shape contains a slide number text field. + * The corresponding slide MUST be a main master slide, title master slide, notes master slide, or handout master slide. + */ + public static final byte MasterSlideNumber = 8; - public static final byte MasterHeader = 9; + /** + * The corresponding shape contains a footer text field. + * The corresponding slide MUST be a main master slide, title master slide, notes master slide, or handout master slide. + */ + public static final byte MasterFooter = 9; - public static final byte MasterSubtitle = 10; + /** + * The corresponding shape contains a header text field. + * The corresponding slide must be a notes master slide or handout master slide. + */ + public static final byte MasterHeader = 10; - public static final byte GenericTextObject = 11; + /** + * The corresponding shape contains a presentation slide image. + * The corresponding slide MUST be a notes slide. + */ + public static final byte NotesSlideImage = 11; - public static final byte Title = 12; + /** + * The corresponding shape contains the notes text. + * The corresponding slide MUST be a notes slide. + */ + public static final byte NotesBody = 12; - public static final byte Body = 13; + /** + * The corresponding shape contains the title text. + * The corresponding slide MUST be a presentation slide. + */ + public static final byte Title = 13; - public static final byte NotesBody = 14; + /** + * The corresponding shape contains the body text. + * The corresponding slide MUST be a presentation slide. + */ + public static final byte Body = 14; + /** + * The corresponding shape contains the title text. + * The corresponding slide MUST be a presentation slide. + */ public static final byte CenteredTitle = 15; + /** + * The corresponding shape contains the sub-title text. + * The corresponding slide MUST be a presentation slide. + */ public static final byte Subtitle = 16; + /** + * The corresponding shape contains the title text with vertical text flow. + * The corresponding slide MUST be a presentation slide. + */ public static final byte VerticalTextTitle = 17; + /** + * The corresponding shape contains the body text with vertical text flow. + * The corresponding slide MUST be a presentation slide. + */ public static final byte VerticalTextBody = 18; - public static final byte NotesSlideImage = 19; + /** + * The corresponding shape contains a generic object. + * The corresponding slide MUST be a presentation slide. + */ + public static final byte Object = 19; - public static final byte Object = 20; + /** + * The corresponding shape contains a chart object. + * The corresponding slide MUST be a presentation slide. + */ + public static final byte Graph = 20; - public static final byte Graph = 21; + /** + * The corresponding shape contains a table object. + * The corresponding slide MUST be a presentation slide. + */ + public static final byte Table = 21; - public static final byte Table = 22; + /** + * The corresponding shape contains a clipart object. + * The corresponding slide MUST be a presentation slide. + */ + public static final byte ClipArt = 22; - public static final byte ClipArt = 23; + /** + * The corresponding shape contains an organization chart object. + * The corresponding slide MUST be a presentation slide. + */ + public static final byte OrganizationChart = 23; - public static final byte OrganizationChart = 24; - - public static final byte MediaClip = 25; + /** + * The corresponding shape contains a media object. + * The corresponding slide MUST be a presentation slide. + */ + public static final byte MediaClip = 24; private byte[] _header; @@ -116,11 +224,13 @@ public class OEPlaceholderAtom extends RecordAtom{ */ protected OEPlaceholderAtom(byte[] source, int start, int len) { _header = new byte[8]; - System.arraycopy(source,start,_header,0,8); + int offset = start; + System.arraycopy(source,start,_header,0,8); + offset += _header.length; - placementId = LittleEndian.getInt(source, start); - placeholderId = LittleEndian.getUnsignedByte(source, start+4); - placeholderSize = LittleEndian.getUnsignedByte(source, start+5); + placementId = LittleEndian.getInt(source, offset); offset += 4; + placeholderId = LittleEndian.getUnsignedByte(source, offset); offset++; + placeholderSize = LittleEndian.getUnsignedByte(source, offset); offset++; } /** @@ -130,6 +240,11 @@ public class OEPlaceholderAtom extends RecordAtom{ /** * Returns the placement Id. + *

+ * The placement Id is a number assigned to the placeholder. It goes from -1 to the number of placeholders. + * It SHOULD be unique among all PlacholderAtom records contained in the corresponding slide. + * The value 0xFFFFFFFF specifies that the corresponding shape is not a placeholder shape. + *

* * @return the placement Id. */ @@ -139,6 +254,11 @@ public class OEPlaceholderAtom extends RecordAtom{ /** * Sets the placement Id. + *

+ * The placement Id is a number assigned to the placeholder. It goes from -1 to the number of placeholders. + * It SHOULD be unique among all PlacholderAtom records contained in the corresponding slide. + * The value 0xFFFFFFFF specifies that the corresponding shape is not a placeholder shape. + *

* * @param id the placement Id. */ @@ -149,6 +269,11 @@ public class OEPlaceholderAtom extends RecordAtom{ /** * Returns the placeholder Id. * + *

+ * placeholder Id specifies the type of the placeholder shape. + * The value MUST be one of the static constants defined in this class + *

+ * * @return the placeholder Id. */ public int getPlaceholderId(){ @@ -158,6 +283,10 @@ public class OEPlaceholderAtom extends RecordAtom{ /** * Sets the placeholder Id. * + *

+ * placeholder Id specifies the type of the placeholder shape. + * The value MUST be one of the static constants defined in this class + *

* @param id the placeholder Id. */ public void setPlaceholderId(byte id){ diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java b/src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java index d7a664725..9c867d5ab 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java @@ -162,6 +162,17 @@ public class RecordTypes { // Records ~12050 seem to be related to Document Encryption public static final Type DocumentEncryptionAtom = new Type(12052,DocumentEncryptionAtom.class); + public static final Type OriginalMainMasterId = new Type(1052,null); + public static final Type CompositeMasterId = new Type(1052,null); + public static final Type RoundTripContentMasterInfo12 = new Type(1054,null); + public static final Type RoundTripShapeId12 = new Type(1055,null); + public static final Type RoundTripHFPlaceholder12 = new Type(1056,RoundTripHFPlaceholder12.class); + public static final Type RoundTripContentMasterId = new Type(1058,null); + public static final Type RoundTripOArtTextStyles12 = new Type(1059,null); + public static final Type RoundTripShapeCheckSumForCustomLayouts12 = new Type(1062,null); + public static final Type RoundTripNotesMasterTextStyles12 = new Type(1063,null); + public static final Type RoundTripCustomTableStyles12 = new Type(1064,null); + //records greater then 0xF000 belong to with Microsoft Office Drawing format also known as Escher public static final int EscherDggContainer = 0xf000; public static final int EscherDgg = 0xf006; diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/RoundTripHFPlaceholder12.java b/src/scratchpad/src/org/apache/poi/hslf/record/RoundTripHFPlaceholder12.java new file mode 100644 index 000000000..0f49b5bd0 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hslf/record/RoundTripHFPlaceholder12.java @@ -0,0 +1,99 @@ +/* ==================================================================== + 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.record; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Date; + +import org.apache.poi.hslf.util.SystemTimeUtils; +import org.apache.poi.util.LittleEndian; + +/** + * An atom record that specifies that a shape is a header or footer placeholder shape + * + * @since PowerPoint 2007 + * @author Yegor Kozlov + */ + +public class RoundTripHFPlaceholder12 extends RecordAtom +{ + /** + * Record header. + */ + private byte[] _header; + + /** + * Specifies the placeholder shape ID. + * + * MUST be {@link OEPlaceholderAtom#MasterDate}, {@link OEPlaceholderAtom#MasterSlideNumber}, + * {@link OEPlaceholderAtom#MasterFooter}, or {@link OEPlaceholderAtom#MasterHeader} + */ + private byte _placeholderId; + + /** + * Constructs the comment atom record from its source data. + * + * @param source the source data as a byte array. + * @param start the start offset into the byte array. + * @param len the length of the slice in the byte array. + */ + protected RoundTripHFPlaceholder12(byte[] source, int start, int len) { + // Get the header. + _header = new byte[8]; + System.arraycopy(source,start,_header,0,8); + + // Get the record data. + _placeholderId = source[start+8]; + } + + /** + * Gets the comment number (note - each user normally has their own count). + * @return the comment number. + */ + public int getPlaceholderId() { + return _placeholderId; + } + + /** + * Sets the comment number (note - each user normally has their own count). + * @param number the comment number. + */ + public void setPlaceholderId(int number) { + _placeholderId = (byte)number; + } + + /** + * Gets the record type. + * @return the record type. + */ + public long getRecordType() { return RecordTypes.RoundTripHFPlaceholder12.typeID; } + + /** + * Write the contents of the record back, so it can be written + * to disk + * + * @param out the output stream to write to. + * @throws java.io.IOException if an error occurs. + */ + public void writeOut(OutputStream out) throws IOException { + out.write(_header); + out.write(_placeholderId); + } +} \ No newline at end of file diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java index 1b2b9f5ee..a546d0444 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java @@ -37,14 +37,7 @@ import org.apache.poi.ddf.EscherRecord; import org.apache.poi.hslf.HSLFSlideShow; import org.apache.poi.hslf.exceptions.CorruptPowerPointFileException; import org.apache.poi.hslf.exceptions.HSLFException; -import org.apache.poi.hslf.model.HeadersFooters; -import org.apache.poi.hslf.model.Notes; -import org.apache.poi.hslf.model.PPFont; -import org.apache.poi.hslf.model.Picture; -import org.apache.poi.hslf.model.Shape; -import org.apache.poi.hslf.model.Slide; -import org.apache.poi.hslf.model.SlideMaster; -import org.apache.poi.hslf.model.TitleMaster; +import org.apache.poi.hslf.model.*; import org.apache.poi.hslf.record.Document; import org.apache.poi.hslf.record.DocumentAtom; import org.apache.poi.hslf.record.FontCollection; @@ -843,6 +836,10 @@ public final class SlideShow { * @return Header / Footer settings for slides */ public HeadersFooters getSlideHeadersFooters(){ + //detect if this ppt was saved in Office2007 + String tag = getSlidesMasters()[0].getProgrammableTag(); + boolean ppt2007 = "___PPT12".equals(tag); + HeadersFootersContainer hdd = null; Record[] ch = _documentRecord.getChildRecords(); for (int i = 0; i < ch.length; i++) { @@ -857,7 +854,7 @@ public final class SlideShow { hdd = new HeadersFootersContainer(HeadersFootersContainer.SlideHeadersFootersContainer); newRecord = true; } - return new HeadersFooters(hdd, this, newRecord); + return new HeadersFooters(hdd, this, newRecord, ppt2007); } /** @@ -866,6 +863,10 @@ public final class SlideShow { * @return Header / Footer settings for notes */ public HeadersFooters getNotesHeadersFooters(){ + //detect if this ppt was saved in Office2007 + String tag = getSlidesMasters()[0].getProgrammableTag(); + boolean ppt2007 = "___PPT12".equals(tag); + HeadersFootersContainer hdd = null; Record[] ch = _documentRecord.getChildRecords(); for (int i = 0; i < ch.length; i++) { @@ -873,13 +874,18 @@ public final class SlideShow { ((HeadersFootersContainer)ch[i]).getOptions() == HeadersFootersContainer.NotesHeadersFootersContainer){ hdd = (HeadersFootersContainer)ch[i]; break; - } + } } boolean newRecord = false; if(hdd == null) { hdd = new HeadersFootersContainer(HeadersFootersContainer.NotesHeadersFootersContainer); newRecord = true; } - return new HeadersFooters(hdd, this, newRecord); + if(ppt2007 && _notes.length > 0){ + return new HeadersFooters(hdd, _notes[0], newRecord, ppt2007); + } else { + return new HeadersFooters(hdd, this, newRecord, ppt2007); + } } + } diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/data/headers_footers_2007.ppt b/src/scratchpad/testcases/org/apache/poi/hslf/data/headers_footers_2007.ppt new file mode 100644 index 000000000..51b2c53b6 Binary files /dev/null and b/src/scratchpad/testcases/org/apache/poi/hslf/data/headers_footers_2007.ppt differ diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestHeadersFooters.java b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestHeadersFooters.java index 8b1cdbe09..39255d3e8 100644 --- a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestHeadersFooters.java +++ b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestHeadersFooters.java @@ -77,6 +77,112 @@ public class TestHeadersFooters extends TestCase assertEquals("custom date format", hd2.getDateTimeText()); } + /** + * If Headers / Footers are not set, all the getters should return false or null + */ + public void testReadNoHeadersFooters() throws Exception + { + File file = new File(cwd, "basic_test_ppt_file.ppt"); + FileInputStream is = new FileInputStream(file); + SlideShow ppt = new SlideShow(is); + is.close(); + + HeadersFooters slideHdd = ppt.getSlideHeadersFooters(); + assertFalse(slideHdd.isFooterVisible()); + assertNull(slideHdd.getFooterText()); + assertFalse(slideHdd.isSlideNumberVisible()); + assertFalse(slideHdd.isHeaderVisible()); + assertNull(slideHdd.getHeaderText()); + assertFalse(slideHdd.isUserDateVisible()); + assertNull(slideHdd.getDateTimeText()); + + + HeadersFooters notesHdd = ppt.getNotesHeadersFooters(); + assertFalse(notesHdd.isFooterVisible()); + assertNull(notesHdd.getFooterText()); + assertFalse(notesHdd.isHeaderVisible()); + assertNull(notesHdd.getHeaderText()); + assertFalse(notesHdd.isUserDateVisible()); + assertNull(notesHdd.getDateTimeText()); + + Slide[] slide = ppt.getSlides(); + for(int i=0 ; i < slide.length; i++){ + HeadersFooters hd1 = slide[i].getHeadersFooters(); + assertFalse(hd1.isFooterVisible()); + assertNull(hd1.getFooterText()); + assertFalse(hd1.isHeaderVisible()); + assertNull(hd1.getHeaderText()); + assertFalse(hd1.isUserDateVisible()); + assertNull(hd1.getDateTimeText()); + } + } + + /** + * Test extraction of headers / footers from PPTs saved in Office 2007 + */ + public void testRead2007() throws Exception + { + File file = new File(cwd, "headers_footers_2007.ppt"); + FileInputStream is = new FileInputStream(file); + SlideShow ppt = new SlideShow(is); + is.close(); + + HeadersFooters slideHdd = ppt.getSlideHeadersFooters(); + assertTrue(slideHdd.isFooterVisible()); + assertEquals("THE FOOTER TEXT", slideHdd.getFooterText()); + assertTrue(slideHdd.isSlideNumberVisible()); + assertFalse(slideHdd.isHeaderVisible()); + assertNull(slideHdd.getHeaderText()); + assertTrue(slideHdd.isUserDateVisible()); + assertEquals("Wednesday, August 06, 2008", slideHdd.getDateTimeText()); + + + HeadersFooters notesHdd = ppt.getNotesHeadersFooters(); + assertTrue(notesHdd.isFooterVisible()); + assertEquals("THE NOTES FOOTER TEXT", notesHdd.getFooterText()); + assertTrue(notesHdd.isHeaderVisible()); + assertEquals("THE NOTES HEADER TEXT", notesHdd.getHeaderText()); + assertTrue(notesHdd.isUserDateVisible()); + assertTrue(notesHdd.isDateTimeVisible()); + //TODO: depending on the formatId getDateTimeText() should return formatted date + //assertEquals("08/12/08", notesHdd.getDateTimeText()); + + //per-slide headers / footers + Slide[] slide = ppt.getSlides(); + //the first slide uses presentation-scope headers / footers + HeadersFooters hd1 = slide[0].getHeadersFooters(); + assertTrue(hd1.isFooterVisible()); + assertEquals("THE FOOTER TEXT", hd1.getFooterText()); + assertTrue(hd1.isSlideNumberVisible()); + assertFalse(hd1.isHeaderVisible()); + assertNull(hd1.getHeaderText()); + assertTrue(hd1.isUserDateVisible()); + assertTrue(hd1.isDateTimeVisible()); + assertEquals("Wednesday, August 06, 2008", hd1.getDateTimeText()); + + //the second slide uses custom per-slide headers / footers + HeadersFooters hd2 = slide[1].getHeadersFooters(); + assertTrue(hd2.isFooterVisible()); + assertEquals("THE FOOTER TEXT FOR SLIDE 2", hd2.getFooterText()); + assertTrue(hd2.isSlideNumberVisible()); + assertFalse(hd2.isHeaderVisible()); + assertNull(hd2.getHeaderText()); + assertTrue(hd2.isUserDateVisible()); + assertTrue(hd2.isDateTimeVisible()); + assertEquals("August 06, 2008", hd2.getDateTimeText()); + + //the third slide uses per-slide headers / footers + HeadersFooters hd3 = slide[2].getHeadersFooters(); + assertTrue(hd3.isFooterVisible()); + assertEquals("THE FOOTER TEXT", hd3.getFooterText()); + assertTrue(hd3.isSlideNumberVisible()); + assertFalse(hd3.isHeaderVisible()); + assertNull(hd3.getHeaderText()); + assertTrue(hd3.isUserDateVisible()); + assertTrue(hd3.isDateTimeVisible()); + assertEquals("Wednesday, August 06, 2008", hd3.getDateTimeText()); + } + public void testCreateSlideFooters() throws Exception { SlideShow ppt = new SlideShow();