Fix bug #45590: Header/footer extraction for .ppt files saved in Office 2007

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@685054 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Yegor Kozlov 2008-08-12 06:55:10 +00:00
parent 6624d6420e
commit d92587222d
13 changed files with 533 additions and 67 deletions

View File

@ -37,6 +37,7 @@
<!-- Don't forget to update status.xml too! --> <!-- Don't forget to update status.xml too! -->
<release version="3.1.1-alpha1" date="2008-??-??"> <release version="3.1.1-alpha1" date="2008-??-??">
<action dev="POI-DEVELOPERS" type="fix">45590 - Fix for Header/footer extraction for .ppt files saved in Office 2007</action>
<action dev="POI-DEVELOPERS" type="fix">Big improvement in how HWPF handles unicode text, and more sanity checking of text ranges within HWPF</action> <action dev="POI-DEVELOPERS" type="fix">Big improvement in how HWPF handles unicode text, and more sanity checking of text ranges within HWPF</action>
<action dev="POI-DEVELOPERS" type="add">Include headers and footers int he extracted text from HWPF's WordExtractor</action> <action dev="POI-DEVELOPERS" type="add">Include headers and footers int he extracted text from HWPF's WordExtractor</action>
<action dev="POI-DEVELOPERS" type="add">Added support to HWPF for headers and footers</action> <action dev="POI-DEVELOPERS" type="add">Added support to HWPF for headers and footers</action>

View File

@ -34,6 +34,7 @@
<!-- Don't forget to update changes.xml too! --> <!-- Don't forget to update changes.xml too! -->
<changes> <changes>
<release version="3.1.1-alpha1" date="2008-??-??"> <release version="3.1.1-alpha1" date="2008-??-??">
<action dev="POI-DEVELOPERS" type="fix">45590 - Fix for Header/footer extraction for .ppt files saved in Office 2007</action>
<action dev="POI-DEVELOPERS" type="fix">Big improvement in how HWPF handles unicode text, and more sanity checking of text ranges within HWPF</action> <action dev="POI-DEVELOPERS" type="fix">Big improvement in how HWPF handles unicode text, and more sanity checking of text ranges within HWPF</action>
<action dev="POI-DEVELOPERS" type="add">Include headers and footers int he extracted text from HWPF's WordExtractor</action> <action dev="POI-DEVELOPERS" type="add">Include headers and footers int he extracted text from HWPF's WordExtractor</action>
<action dev="POI-DEVELOPERS" type="add">Added support to HWPF for headers and footers</action> <action dev="POI-DEVELOPERS" type="add">Added support to HWPF for headers and footers</action>

View File

@ -32,11 +32,22 @@ public class HeadersFooters {
private HeadersFootersContainer _container; private HeadersFootersContainer _container;
private boolean _newRecord; private boolean _newRecord;
private SlideShow _ppt; 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; _container = rec;
_newRecord = newRecord; _newRecord = newRecord;
_ppt = ppt; _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 * @return Headers's text
*/ */
public String getHeaderText(){ public String getHeaderText(){
CString cs = _container.getHeaderAtom(); CString cs = _container == null ? null : _container.getHeaderAtom();
return cs == null ? null : cs.getText(); return getPlaceholderText(OEPlaceholderAtom.MasterHeader, cs);
} }
/** /**
@ -70,8 +81,8 @@ public class HeadersFooters {
* @return Footer's text * @return Footer's text
*/ */
public String getFooterText(){ public String getFooterText(){
CString cs = _container.getFooterAtom(); CString cs = _container == null ? null : _container.getFooterAtom();
return cs == null ? null : cs.getText(); return getPlaceholderText(OEPlaceholderAtom.MasterFooter, cs);
} }
/** /**
@ -95,8 +106,8 @@ public class HeadersFooters {
* @return custom user date * @return custom user date
*/ */
public String getDateTimeText(){ public String getDateTimeText(){
CString cs = _container.getUserDateAtom(); CString cs = _container == null ? null : _container.getUserDateAtom();
return cs == null ? null : cs.getText(); return getPlaceholderText(OEPlaceholderAtom.MasterDate, cs);
} }
/** /**
@ -119,7 +130,7 @@ public class HeadersFooters {
* whether the footer text is displayed. * whether the footer text is displayed.
*/ */
public boolean isFooterVisible(){ 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. * whether the header text is displayed.
*/ */
public boolean isHeaderVisible(){ 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. * whether the date is displayed in the footer.
*/ */
public boolean isDateTimeVisible(){ 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. * whether the custom user date is used instead of today's date.
*/ */
public boolean isUserDateVisible(){ 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. * whether the slide number is displayed in the footer.
*/ */
public boolean isSlideNumberVisible(){ 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); doc.addChildAfter(_container, lst);
_newRecord = false; _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;
}
} }

View File

@ -19,6 +19,7 @@ package org.apache.poi.hslf.model;
import org.apache.poi.hslf.record.SheetContainer; import org.apache.poi.hslf.record.SheetContainer;
import org.apache.poi.hslf.record.Record; import org.apache.poi.hslf.record.Record;
import org.apache.poi.hslf.record.RecordTypes; import org.apache.poi.hslf.record.RecordTypes;
import org.apache.poi.hslf.record.OEPlaceholderAtom;
import org.apache.poi.hslf.model.textproperties.TextProp; import org.apache.poi.hslf.model.textproperties.TextProp;
/** /**
@ -54,20 +55,4 @@ public abstract class MasterSheet extends Sheet {
return tx.getPlaceholderAtom() != null; 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;
}
} }

View File

@ -362,4 +362,83 @@ public abstract class Sheet {
public void draw(Graphics2D graphics){ public void draw(Graphics2D graphics){
} }
/**
* Return placeholder by text type
*
* @param type type of text, See {@link org.apache.poi.hslf.record.TextHeaderAtom}
* @return <code>TextShape</code> or <code>null</code>
*/
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 <code>TextShape</code> or <code>null</code>
*/
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. <code>___PPT12</code>.
*
* @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;
}
} }

View File

@ -440,14 +440,24 @@ public class Slide extends Sheet
public HeadersFooters getHeadersFooters(){ public HeadersFooters getHeadersFooters(){
HeadersFootersContainer hdd = null; HeadersFootersContainer hdd = null;
Record[] ch = getSheetContainer().getChildRecords(); Record[] ch = getSheetContainer().getChildRecords();
boolean ppt2007 = false;
for (int i = 0; i < ch.length; i++) { for (int i = 0; i < ch.length; i++) {
if(ch[i] instanceof HeadersFootersContainer){ if(ch[i] instanceof HeadersFootersContainer){
hdd = (HeadersFootersContainer)ch[i]; hdd = (HeadersFootersContainer)ch[i];
break; } else if (ch[i].getRecordType() == RecordTypes.RoundTripContentMasterId.typeID){
ppt2007 = true;
} }
} }
boolean newRecord = false; boolean newRecord = false;
if(hdd == null) return getSlideShow().getSlideHeadersFooters(); if(hdd == null && !ppt2007) {
else return new HeadersFooters(hdd, getSlideShow(), newRecord); return getSlideShow().getSlideHeadersFooters();
}
else {
if(hdd == null) {
hdd = new HeadersFootersContainer(HeadersFootersContainer.SlideHeadersFootersContainer);
newRecord = true;
}
return new HeadersFooters(hdd, this, newRecord, ppt2007);
}
} }
} }

View File

@ -269,7 +269,7 @@ public abstract class TextShape extends SimpleShape {
int type = getTextRun().getRunType(); int type = getTextRun().getRunType();
MasterSheet master = getSheet().getMasterSheet(); MasterSheet master = getSheet().getMasterSheet();
if(master != null){ if(master != null){
TextShape masterShape = master.getPlaceholder(type); TextShape masterShape = master.getPlaceholderByTextType(type);
if(masterShape != null) valign = masterShape.getVerticalAlignment(); if(masterShape != null) valign = masterShape.getVerticalAlignment();
} else { } else {
//not found in the master sheet. Use the hardcoded defaults. //not found in the master sheet. Use the hardcoded defaults.

View File

@ -26,7 +26,7 @@ import java.io.OutputStream;
/** /**
* OEPlaceholderAtom (3011). * OEPlaceholderAtom (3011).
* <p> * <p>
* Atom that describes the placeholder. * An atom record that specifies whether a shape is a placeholder shape.
* </p> * </p>
* *
* @author Yegor Kozlov * @author Yegor Kozlov
@ -34,61 +34,169 @@ import java.io.OutputStream;
public class OEPlaceholderAtom extends RecordAtom{ public class OEPlaceholderAtom extends RecordAtom{
/**
* The full size of the master body text placeholder shape.
*/
public static final int PLACEHOLDER_FULLSIZE = 0; 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; 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; public static final int PLACEHOLDER_QUARTSIZE = 2;
/**
* MUST NOT be used for this field.
*/
public static final byte None = 0; 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; 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; 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 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; 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; 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; 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 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; /**
* The corresponding shape contains a media object.
public static final byte MediaClip = 25; * The corresponding slide MUST be a presentation slide.
*/
public static final byte MediaClip = 24;
private byte[] _header; private byte[] _header;
@ -116,11 +224,13 @@ public class OEPlaceholderAtom extends RecordAtom{
*/ */
protected OEPlaceholderAtom(byte[] source, int start, int len) { protected OEPlaceholderAtom(byte[] source, int start, int len) {
_header = new byte[8]; _header = new byte[8];
int offset = start;
System.arraycopy(source,start,_header,0,8); System.arraycopy(source,start,_header,0,8);
offset += _header.length;
placementId = LittleEndian.getInt(source, start); placementId = LittleEndian.getInt(source, offset); offset += 4;
placeholderId = LittleEndian.getUnsignedByte(source, start+4); placeholderId = LittleEndian.getUnsignedByte(source, offset); offset++;
placeholderSize = LittleEndian.getUnsignedByte(source, start+5); placeholderSize = LittleEndian.getUnsignedByte(source, offset); offset++;
} }
/** /**
@ -130,6 +240,11 @@ public class OEPlaceholderAtom extends RecordAtom{
/** /**
* Returns the placement Id. * Returns the placement Id.
* <p>
* 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.
* </p>
* *
* @return the placement Id. * @return the placement Id.
*/ */
@ -139,6 +254,11 @@ public class OEPlaceholderAtom extends RecordAtom{
/** /**
* Sets the placement Id. * Sets the placement Id.
* <p>
* 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.
* </p>
* *
* @param id the placement Id. * @param id the placement Id.
*/ */
@ -149,6 +269,11 @@ public class OEPlaceholderAtom extends RecordAtom{
/** /**
* Returns the placeholder Id. * Returns the placeholder Id.
* *
* <p>
* placeholder Id specifies the type of the placeholder shape.
* The value MUST be one of the static constants defined in this class
* </p>
*
* @return the placeholder Id. * @return the placeholder Id.
*/ */
public int getPlaceholderId(){ public int getPlaceholderId(){
@ -158,6 +283,10 @@ public class OEPlaceholderAtom extends RecordAtom{
/** /**
* Sets the placeholder Id. * Sets the placeholder Id.
* *
* <p>
* placeholder Id specifies the type of the placeholder shape.
* The value MUST be one of the static constants defined in this class
* </p>
* @param id the placeholder Id. * @param id the placeholder Id.
*/ */
public void setPlaceholderId(byte id){ public void setPlaceholderId(byte id){

View File

@ -162,6 +162,17 @@ public class RecordTypes {
// Records ~12050 seem to be related to Document Encryption // Records ~12050 seem to be related to Document Encryption
public static final Type DocumentEncryptionAtom = new Type(12052,DocumentEncryptionAtom.class); 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 //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 EscherDggContainer = 0xf000;
public static final int EscherDgg = 0xf006; public static final int EscherDgg = 0xf006;

View File

@ -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);
}
}

View File

@ -37,14 +37,7 @@ import org.apache.poi.ddf.EscherRecord;
import org.apache.poi.hslf.HSLFSlideShow; import org.apache.poi.hslf.HSLFSlideShow;
import org.apache.poi.hslf.exceptions.CorruptPowerPointFileException; import org.apache.poi.hslf.exceptions.CorruptPowerPointFileException;
import org.apache.poi.hslf.exceptions.HSLFException; import org.apache.poi.hslf.exceptions.HSLFException;
import org.apache.poi.hslf.model.HeadersFooters; import org.apache.poi.hslf.model.*;
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.record.Document; import org.apache.poi.hslf.record.Document;
import org.apache.poi.hslf.record.DocumentAtom; import org.apache.poi.hslf.record.DocumentAtom;
import org.apache.poi.hslf.record.FontCollection; import org.apache.poi.hslf.record.FontCollection;
@ -843,6 +836,10 @@ public final class SlideShow {
* @return Header / Footer settings for slides * @return Header / Footer settings for slides
*/ */
public HeadersFooters getSlideHeadersFooters(){ public HeadersFooters getSlideHeadersFooters(){
//detect if this ppt was saved in Office2007
String tag = getSlidesMasters()[0].getProgrammableTag();
boolean ppt2007 = "___PPT12".equals(tag);
HeadersFootersContainer hdd = null; HeadersFootersContainer hdd = null;
Record[] ch = _documentRecord.getChildRecords(); Record[] ch = _documentRecord.getChildRecords();
for (int i = 0; i < ch.length; i++) { for (int i = 0; i < ch.length; i++) {
@ -857,7 +854,7 @@ public final class SlideShow {
hdd = new HeadersFootersContainer(HeadersFootersContainer.SlideHeadersFootersContainer); hdd = new HeadersFootersContainer(HeadersFootersContainer.SlideHeadersFootersContainer);
newRecord = true; 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 * @return Header / Footer settings for notes
*/ */
public HeadersFooters getNotesHeadersFooters(){ public HeadersFooters getNotesHeadersFooters(){
//detect if this ppt was saved in Office2007
String tag = getSlidesMasters()[0].getProgrammableTag();
boolean ppt2007 = "___PPT12".equals(tag);
HeadersFootersContainer hdd = null; HeadersFootersContainer hdd = null;
Record[] ch = _documentRecord.getChildRecords(); Record[] ch = _documentRecord.getChildRecords();
for (int i = 0; i < ch.length; i++) { for (int i = 0; i < ch.length; i++) {
@ -880,6 +881,11 @@ public final class SlideShow {
hdd = new HeadersFootersContainer(HeadersFootersContainer.NotesHeadersFootersContainer); hdd = new HeadersFootersContainer(HeadersFootersContainer.NotesHeadersFootersContainer);
newRecord = true; 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);
} }
}
} }

View File

@ -77,6 +77,112 @@ public class TestHeadersFooters extends TestCase
assertEquals("custom date format", hd2.getDateTimeText()); assertEquals("custom date format", hd2.getDateTimeText());
} }
/**
* If Headers / Footers are not set, all the getters should return <code>false</code> or <code>null</code>
*/
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 public void testCreateSlideFooters() throws Exception
{ {
SlideShow ppt = new SlideShow(); SlideShow ppt = new SlideShow();