git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1829453 13f79535-47bb-0310-9956-ffa450edef68tags/REL_4_0_0_FINAL
@@ -52,6 +52,7 @@ import org.apache.poi.sl.usermodel.TextRun; | |||
import org.apache.poi.sl.usermodel.TextRun.FieldType; | |||
import org.apache.poi.sl.usermodel.TextShape; | |||
import org.apache.poi.sl.usermodel.TextShape.TextDirection; | |||
import org.apache.poi.util.Internal; | |||
import org.apache.poi.util.LocaleUtil; | |||
import org.apache.poi.util.POILogFactory; | |||
import org.apache.poi.util.POILogger; | |||
@@ -381,7 +382,8 @@ public class DrawTextParagraph implements Drawable { | |||
return getRenderableText(tr); | |||
} | |||
private String getRenderableText(final TextRun tr) { | |||
@Internal | |||
public String getRenderableText(final TextRun tr) { | |||
final String txtSpace = tr.getRawText().replace("\t", tab2space(tr)).replace('\u000b', '\n'); | |||
final Locale loc = LocaleUtil.getUserLocale(); | |||
@@ -0,0 +1,307 @@ | |||
package org.apache.poi.sl.extractor; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import org.apache.poi.POITextExtractor; | |||
import org.apache.poi.sl.usermodel.Comment; | |||
import org.apache.poi.sl.usermodel.MasterSheet; | |||
import org.apache.poi.sl.usermodel.Notes; | |||
import org.apache.poi.sl.usermodel.ObjectShape; | |||
import org.apache.poi.sl.usermodel.Placeholder; | |||
import org.apache.poi.sl.usermodel.PlaceholderDetails; | |||
import org.apache.poi.sl.usermodel.Shape; | |||
import org.apache.poi.sl.usermodel.ShapeContainer; | |||
import org.apache.poi.sl.usermodel.Sheet; | |||
import org.apache.poi.sl.usermodel.Slide; | |||
import org.apache.poi.sl.usermodel.SlideShow; | |||
import org.apache.poi.sl.usermodel.TableCell; | |||
import org.apache.poi.sl.usermodel.TableShape; | |||
import org.apache.poi.sl.usermodel.TextParagraph; | |||
import org.apache.poi.sl.usermodel.TextRun; | |||
import org.apache.poi.sl.usermodel.TextShape; | |||
import org.apache.poi.util.LocaleUtil; | |||
import org.apache.poi.util.POILogFactory; | |||
import org.apache.poi.util.POILogger; | |||
/** | |||
* Common SlideShow extractor | |||
* | |||
* @since POI 4.0.0 | |||
*/ | |||
public class SlideShowExtractor< | |||
S extends Shape<S,P>, | |||
P extends TextParagraph<S,P,? extends TextRun> | |||
> extends POITextExtractor { | |||
private static final POILogger LOG = POILogFactory.getLogger(SlideShowExtractor.class); | |||
private SlideShow<S,P> slideshow; | |||
private boolean slidesByDefault = true; | |||
private boolean notesByDefault; | |||
private boolean commentsByDefault; | |||
private boolean masterByDefault; | |||
public SlideShowExtractor(final SlideShow<S,P> slideshow) { | |||
setFilesystem(slideshow); | |||
this.slideshow = slideshow; | |||
} | |||
/** | |||
* Should a call to getText() return slide text? Default is yes | |||
*/ | |||
public void setSlidesByDefault(final boolean slidesByDefault) { | |||
this.slidesByDefault = slidesByDefault; | |||
} | |||
/** | |||
* Should a call to getText() return notes text? Default is no | |||
*/ | |||
public void setNotesByDefault(final boolean notesByDefault) { | |||
this.notesByDefault = notesByDefault; | |||
} | |||
/** | |||
* Should a call to getText() return comments text? Default is no | |||
*/ | |||
public void setCommentsByDefault(final boolean commentsByDefault) { | |||
this.commentsByDefault = commentsByDefault; | |||
} | |||
/** | |||
* Should a call to getText() return text from master? Default is no | |||
*/ | |||
public void setMasterByDefault(final boolean masterByDefault) { | |||
this.masterByDefault = masterByDefault; | |||
} | |||
@Override | |||
public POITextExtractor getMetadataTextExtractor() { | |||
return slideshow.getMetadataTextExtractor(); | |||
} | |||
/** | |||
* Fetches all the slide text from the slideshow, but not the notes, unless | |||
* you've called setSlidesByDefault() and setNotesByDefault() to change this | |||
*/ | |||
@Override | |||
public String getText() { | |||
final StringBuilder sb = new StringBuilder(); | |||
if (masterByDefault) { | |||
for (final MasterSheet<S,P> master : slideshow.getSlideMasters()) { | |||
for (final Shape<S,P> shape : master) { | |||
if (shape instanceof TextShape) { | |||
final TextShape<S,P> ts = (TextShape<S,P>)shape; | |||
final String text = ts.getText(); | |||
if (text == null || text.isEmpty() || "*".equals(text)) { | |||
continue; | |||
} | |||
if (ts.isPlaceholder()) { | |||
// don't bother about boiler plate text on master sheets | |||
LOG.log(POILogger.INFO, "Ignoring boiler plate (placeholder) text on slide master:", text); | |||
continue; | |||
} | |||
sb.append(text); | |||
if (!text.endsWith("\n")) { | |||
sb.append("\n"); | |||
} | |||
} | |||
} | |||
} | |||
} | |||
for (final Slide<S, P> slide : slideshow.getSlides()) { | |||
sb.append(getText(slide)); | |||
} | |||
return sb.toString(); | |||
} | |||
public String getText(final Slide<S,P> slide) { | |||
final StringBuilder sb = new StringBuilder(); | |||
if (slidesByDefault) { | |||
printShapeText(slide, sb); | |||
} | |||
if (commentsByDefault) { | |||
printComments(slide, sb); | |||
} | |||
if (notesByDefault) { | |||
printNotes(slide, sb); | |||
} | |||
return sb.toString(); | |||
} | |||
private String printHeaderReturnFooter(final Sheet<S,P> sheet, final StringBuilder sb) { | |||
final Sheet<S, P> m = (sheet instanceof Slide) ? sheet.getMasterSheet() : sheet; | |||
final StringBuilder footer = new StringBuilder("\n"); | |||
addSheetPlaceholderDatails(sheet, Placeholder.HEADER, sb); | |||
addSheetPlaceholderDatails(sheet, Placeholder.FOOTER, footer); | |||
if (masterByDefault) { | |||
// write header texts and determine footer text | |||
for (Shape<S, P> s : m) { | |||
if (!(s instanceof TextShape)) { | |||
continue; | |||
} | |||
final TextShape<S, P> ts = (TextShape<S, P>) s; | |||
final PlaceholderDetails pd = ts.getPlaceholderDetails(); | |||
if (pd == null || !pd.isVisible()) { | |||
continue; | |||
} | |||
switch (pd.getPlaceholder()) { | |||
case HEADER: | |||
sb.append(ts.getText()); | |||
sb.append('\n'); | |||
break; | |||
case SLIDE_NUMBER: | |||
if (sheet instanceof Slide) { | |||
footer.append(ts.getText().replace("‹#›", Integer.toString(((Slide<S, P>) sheet).getSlideNumber() + 1))); | |||
footer.append('\n'); | |||
} | |||
break; | |||
case FOOTER: | |||
footer.append(ts.getText()); | |||
footer.append('\n'); | |||
break; | |||
case DATETIME: | |||
// currently not supported | |||
default: | |||
break; | |||
} | |||
} | |||
} | |||
return (footer.length() > 1) ? footer.toString() : ""; | |||
} | |||
private void addSheetPlaceholderDatails(final Sheet<S,P> sheet, final Placeholder placeholder, final StringBuilder sb) { | |||
final PlaceholderDetails headerPD = sheet.getPlaceholderDetails(placeholder); | |||
if (headerPD == null) { | |||
return; | |||
} | |||
final String headerStr = headerPD.getText(); | |||
if (headerStr == null) { | |||
return; | |||
} | |||
sb.append(headerStr); | |||
} | |||
private void printShapeText(final Sheet<S,P> sheet, final StringBuilder sb) { | |||
final String footer = printHeaderReturnFooter(sheet, sb); | |||
printShapeText((ShapeContainer<S,P>)sheet, sb); | |||
sb.append(footer); | |||
} | |||
@SuppressWarnings("unchecked") | |||
private void printShapeText(final ShapeContainer<S,P> container, final StringBuilder sb) { | |||
for (Shape<S,P> shape : container) { | |||
if (shape instanceof TextShape) { | |||
printShapeText((TextShape<S,P>)shape, sb); | |||
} else if (shape instanceof TableShape) { | |||
printShapeText((TableShape<S,P>)shape, sb); | |||
} else if (shape instanceof ShapeContainer) { | |||
printShapeText((ShapeContainer<S,P>)shape, sb); | |||
} | |||
} | |||
} | |||
private void printShapeText(final TextShape<S,P> shape, final StringBuilder sb) { | |||
final List<P> paraList = shape.getTextParagraphs(); | |||
if (paraList.isEmpty()) { | |||
sb.append('\n'); | |||
return; | |||
} | |||
for (final P para : paraList) { | |||
final int oldLen = sb.length(); | |||
for (final TextRun tr : para) { | |||
final String str = tr.getRawText().replace("\r", ""); | |||
final String newStr; | |||
switch (tr.getTextCap()) { | |||
case ALL: | |||
newStr = str.toUpperCase(LocaleUtil.getUserLocale()); | |||
break; | |||
case SMALL: | |||
newStr = str.toLowerCase(LocaleUtil.getUserLocale()); | |||
break; | |||
default: | |||
case NONE: | |||
newStr = str; | |||
break; | |||
} | |||
sb.append(newStr); | |||
} | |||
sb.append('\n'); | |||
} | |||
} | |||
@SuppressWarnings("Duplicates") | |||
private void printShapeText(final TableShape<S,P> shape, final StringBuilder sb) { | |||
final int nrows = shape.getNumberOfRows(); | |||
final int ncols = shape.getNumberOfColumns(); | |||
for (int row = 0; row < nrows; row++){ | |||
for (int col = 0; col < ncols; col++){ | |||
TableCell<S, P> cell = shape.getCell(row, col); | |||
//defensive null checks; don't know if they're necessary | |||
if (cell != null){ | |||
String txt = cell.getText(); | |||
txt = (txt == null) ? "" : txt; | |||
sb.append(txt); | |||
if (col < ncols-1){ | |||
sb.append('\t'); | |||
} | |||
} | |||
} | |||
sb.append('\n'); | |||
} | |||
} | |||
private void printComments(final Slide<S,P> slide, final StringBuilder sb) { | |||
for (final Comment comment : slide.getComments()) { | |||
sb.append(comment.getAuthor()); | |||
sb.append(" - "); | |||
sb.append(comment.getText()); | |||
sb.append("\n"); | |||
} | |||
} | |||
private void printNotes(final Slide<S,P> slide, final StringBuilder sb) { | |||
final Notes<S, P> notes = slide.getNotes(); | |||
if (notes == null) { | |||
return; | |||
} | |||
final String footer = printHeaderReturnFooter(notes, sb); | |||
printShapeText(notes, sb); | |||
sb.append(footer); | |||
} | |||
public List<? extends ObjectShape<S,P>> getOLEShapes() { | |||
final List<ObjectShape<S,P>> oleShapes = new ArrayList<>(); | |||
for (final Slide<S,P> slide : slideshow.getSlides()) { | |||
addOLEShapes(oleShapes, slide); | |||
} | |||
return oleShapes; | |||
} | |||
@SuppressWarnings("unchecked") | |||
private void addOLEShapes(final List<ObjectShape<S,P>> oleShapes, ShapeContainer<S,P> container) { | |||
for (Shape<S,P> shape : container) { | |||
if (shape instanceof ShapeContainer) { | |||
addOLEShapes(oleShapes, (ShapeContainer<S,P>)shape); | |||
} else if (shape instanceof ObjectShape) { | |||
oleShapes.add((ObjectShape<S,P>)shape); | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,85 @@ | |||
/* ==================================================================== | |||
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.sl.usermodel; | |||
import java.awt.geom.Point2D; | |||
import java.util.Date; | |||
/** | |||
* Common interface for comments | |||
* | |||
* @since POI 4.0.0 | |||
*/ | |||
public interface Comment { | |||
/** | |||
* Get the Author of this comment | |||
*/ | |||
String getAuthor(); | |||
/** | |||
* Set the Author of this comment. | |||
* if the author wasn't registered before, create a new entry | |||
*/ | |||
void setAuthor(String author); | |||
/** | |||
* Get the Author's Initials of this comment | |||
*/ | |||
String getAuthorInitials(); | |||
/** | |||
* Set the Author's Initials of this comment. | |||
* if the author wasn't registered before via {@link #setAuthor(String)} | |||
* this has no effect | |||
*/ | |||
void setAuthorInitials(String initials); | |||
/** | |||
* Get the text of this comment | |||
*/ | |||
String getText(); | |||
/** | |||
* Set the text of this comment | |||
*/ | |||
void setText(String text); | |||
/** | |||
* Gets the date the comment was made. | |||
* @return the comment date. | |||
*/ | |||
Date getDate(); | |||
/** | |||
* Sets the date the comment was made. | |||
* @param date the comment date. | |||
*/ | |||
void setDate(Date date); | |||
/** | |||
* Gets the offset of the comment on the page. | |||
* @return the offset. | |||
*/ | |||
Point2D getOffset(); | |||
/** | |||
* Sets the offset of the comment on the page. | |||
* @param offset the offset. | |||
*/ | |||
void setOffset(Point2D offset); | |||
} |
@@ -0,0 +1,69 @@ | |||
/* ==================================================================== | |||
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.sl.usermodel; | |||
/** | |||
* Extended details about placholders | |||
* | |||
* @since POI 4.0.0 | |||
*/ | |||
public interface PlaceholderDetails { | |||
enum PlaceholderSize { | |||
quarter, half, full; | |||
} | |||
Placeholder getPlaceholder(); | |||
/** | |||
* Specifies that the corresponding shape should be represented by the generating application | |||
* as a placeholder. When a shape is considered a placeholder by the generating application | |||
* it can have special properties to alert the user that they may enter content into the shape. | |||
* Different types of placeholders are allowed and can be specified by using the placeholder | |||
* type attribute for this element | |||
* | |||
* @param placeholder The shape to use as placeholder or null if no placeholder should be set. | |||
*/ | |||
void setPlaceholder(Placeholder placeholder); | |||
boolean isVisible(); | |||
void setVisible(boolean isVisible); | |||
PlaceholderSize getSize(); | |||
void setSize(PlaceholderSize size); | |||
/** | |||
* If the placeholder shape or object stores text, this text is returned otherwise {@code null}. | |||
* | |||
* @return the text of the shape / placeholder | |||
* | |||
* @since POI 4.0.0 | |||
*/ | |||
String getText(); | |||
/** | |||
* If the placeholder shape or object stores text, the given text is stored otherwise this is a no-op. | |||
* | |||
* @param text the placeholder text | |||
* | |||
* @since POI 4.0.0 | |||
*/ | |||
void setText(String text); | |||
} |
@@ -46,4 +46,16 @@ public interface Sheet< | |||
* @param graphics | |||
*/ | |||
void draw(Graphics2D graphics); | |||
/** | |||
* Get the placeholder details for the given placeholder type. Not all placeholders are also shapes - | |||
* this is especially true for old HSLF slideshows, which notes have header/footers elements which | |||
* aren't shapes. | |||
* | |||
* @param placeholder the placeholder type | |||
* @return the placeholder details or {@code null}, if the placeholder isn't contained in the sheet | |||
* | |||
* @since POI 4.0.0 | |||
*/ | |||
PlaceholderDetails getPlaceholderDetails(Placeholder placeholder); | |||
} |
@@ -65,6 +65,24 @@ public interface SimpleShape< | |||
*/ | |||
void setPlaceholder(Placeholder placeholder); | |||
/** | |||
* @return an accessor for placeholder details | |||
* | |||
* @since POI 4.0.0 | |||
*/ | |||
PlaceholderDetails getPlaceholderDetails(); | |||
/** | |||
* Checks if the shape is a placeholder. | |||
* (placeholders aren't normal shapes, they are visible only in the Edit Master mode) | |||
* | |||
* @return {@code true} if the shape is a placeholder | |||
* | |||
* @since POI 4.0.0 | |||
*/ | |||
boolean isPlaceholder(); | |||
Shadow<S,P> getShadow(); | |||
/** |
@@ -17,6 +17,8 @@ | |||
package org.apache.poi.sl.usermodel; | |||
import java.util.List; | |||
public interface Slide< | |||
S extends Shape<S,P>, | |||
P extends TextParagraph<S,P,? extends TextRun> | |||
@@ -48,7 +50,7 @@ public interface Slide< | |||
* whereas in HSLF they are activated via a HeadersFooter configuration. | |||
* This method is used to generalize that handling. | |||
* | |||
* @param placeholder | |||
* @param placeholder the placeholder type | |||
* @return {@code true} if the placeholder should be displayed/rendered | |||
* @since POI 3.16-beta2 | |||
*/ | |||
@@ -69,4 +71,9 @@ public interface Slide< | |||
* @since POI 4.0.0 | |||
*/ | |||
boolean isHidden(); | |||
/** | |||
* @return the comment(s) for this slide | |||
*/ | |||
List<? extends Comment> getComments(); | |||
} |
@@ -25,6 +25,7 @@ import java.io.InputStream; | |||
import java.io.OutputStream; | |||
import java.util.List; | |||
import org.apache.poi.POITextExtractor; | |||
import org.apache.poi.sl.usermodel.PictureData.PictureType; | |||
public interface SlideShow< | |||
@@ -118,4 +119,11 @@ public interface SlideShow< | |||
* OutputStream | |||
*/ | |||
void write(OutputStream out) throws IOException; | |||
/** | |||
* @return an extractor for the slideshow metadata | |||
* | |||
* @since POI 4.0.0 | |||
*/ | |||
POITextExtractor getMetadataTextExtractor(); | |||
} |
@@ -21,202 +21,188 @@ import java.io.IOException; | |||
import org.apache.poi.POIXMLTextExtractor; | |||
import org.apache.poi.openxml4j.exceptions.OpenXML4JException; | |||
import org.apache.poi.openxml4j.opc.OPCPackage; | |||
import org.apache.poi.sl.extractor.SlideShowExtractor; | |||
import org.apache.poi.util.Removal; | |||
import org.apache.poi.xslf.usermodel.XMLSlideShow; | |||
import org.apache.poi.xslf.usermodel.XSLFCommentAuthors; | |||
import org.apache.poi.xslf.usermodel.XSLFComments; | |||
import org.apache.poi.xslf.usermodel.XSLFNotes; | |||
import org.apache.poi.xslf.usermodel.XSLFRelation; | |||
import org.apache.poi.xslf.usermodel.XSLFShape; | |||
import org.apache.poi.xslf.usermodel.XSLFShapeContainer; | |||
import org.apache.poi.xslf.usermodel.XSLFSlide; | |||
import org.apache.poi.xslf.usermodel.XSLFSlideLayout; | |||
import org.apache.poi.xslf.usermodel.XSLFSlideMaster; | |||
import org.apache.poi.xslf.usermodel.XSLFSlideShow; | |||
import org.apache.poi.xslf.usermodel.XSLFTable; | |||
import org.apache.poi.xslf.usermodel.XSLFTableCell; | |||
import org.apache.poi.xslf.usermodel.XSLFTableRow; | |||
import org.apache.poi.xslf.usermodel.XSLFTextShape; | |||
import org.apache.poi.xslf.usermodel.XSLFTextParagraph; | |||
import org.apache.xmlbeans.XmlException; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.CTComment; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.CTCommentAuthor; | |||
/** | |||
* Extractor for XSLF SlideShows | |||
* | |||
* @deprecated use {@link SlideShowExtractor} | |||
*/ | |||
@Deprecated | |||
@Removal(version="4.2.0") | |||
public class XSLFPowerPointExtractor extends POIXMLTextExtractor { | |||
public static final XSLFRelation[] SUPPORTED_TYPES = new XSLFRelation[] { | |||
XSLFRelation.MAIN, XSLFRelation.MACRO, XSLFRelation.MACRO_TEMPLATE, | |||
XSLFRelation.PRESENTATIONML, XSLFRelation.PRESENTATIONML_TEMPLATE, | |||
XSLFRelation.PRESENTATION_MACRO | |||
}; | |||
private XMLSlideShow slideshow; | |||
private boolean slidesByDefault = true; | |||
private boolean notesByDefault; | |||
private boolean masterByDefault; | |||
public XSLFPowerPointExtractor(XMLSlideShow slideshow) { | |||
super(slideshow); | |||
this.slideshow = slideshow; | |||
} | |||
public XSLFPowerPointExtractor(XSLFSlideShow slideshow) throws XmlException, IOException { | |||
this(new XMLSlideShow(slideshow.getPackage())); | |||
} | |||
public XSLFPowerPointExtractor(OPCPackage container) throws XmlException, OpenXML4JException, IOException { | |||
this(new XSLFSlideShow(container)); | |||
} | |||
public static void main(String[] args) throws Exception { | |||
if(args.length < 1) { | |||
System.err.println("Use:"); | |||
System.err.println(" XSLFPowerPointExtractor <filename.pptx>"); | |||
System.exit(1); | |||
} | |||
POIXMLTextExtractor extractor = | |||
new XSLFPowerPointExtractor( | |||
new XSLFSlideShow(args[0])); | |||
System.out.println(extractor.getText()); | |||
extractor.close(); | |||
} | |||
/** | |||
* Should a call to getText() return slide text? | |||
* Default is yes | |||
*/ | |||
public void setSlidesByDefault(boolean slidesByDefault) { | |||
this.slidesByDefault = slidesByDefault; | |||
} | |||
/** | |||
* Should a call to getText() return notes text? | |||
* Default is no | |||
*/ | |||
public void setNotesByDefault(boolean notesByDefault) { | |||
this.notesByDefault = notesByDefault; | |||
} | |||
/** | |||
* Should a call to getText() return text from master? Default is no | |||
*/ | |||
public void setMasterByDefault(boolean masterByDefault) { | |||
this.masterByDefault = masterByDefault; | |||
} | |||
/** | |||
* Gets the slide text, but not the notes text | |||
*/ | |||
@Override | |||
public static final XSLFRelation[] SUPPORTED_TYPES = new XSLFRelation[]{ | |||
XSLFRelation.MAIN, XSLFRelation.MACRO, XSLFRelation.MACRO_TEMPLATE, | |||
XSLFRelation.PRESENTATIONML, XSLFRelation.PRESENTATIONML_TEMPLATE, | |||
XSLFRelation.PRESENTATION_MACRO | |||
}; | |||
private final SlideShowExtractor<XSLFShape, XSLFTextParagraph> delegate; | |||
private boolean slidesByDefault = true; | |||
private boolean notesByDefault; | |||
private boolean commentsByDefault; | |||
private boolean masterByDefault; | |||
@SuppressWarnings("WeakerAccess") | |||
public XSLFPowerPointExtractor(XMLSlideShow slideShow) { | |||
super(slideShow); | |||
delegate = new SlideShowExtractor<>(slideShow); | |||
} | |||
public XSLFPowerPointExtractor(XSLFSlideShow slideShow) { | |||
this(new XMLSlideShow(slideShow.getPackage())); | |||
} | |||
public XSLFPowerPointExtractor(OPCPackage container) throws XmlException, OpenXML4JException, IOException { | |||
this(new XSLFSlideShow(container)); | |||
} | |||
public static void main(String[] args) throws Exception { | |||
if (args.length < 1) { | |||
System.err.println("Use:"); | |||
System.err.println(" XSLFPowerPointExtractor <filename.pptx>"); | |||
System.exit(1); | |||
} | |||
POIXMLTextExtractor extractor = | |||
new XSLFPowerPointExtractor( | |||
new XSLFSlideShow(args[0])); | |||
System.out.println(extractor.getText()); | |||
extractor.close(); | |||
} | |||
/** | |||
* Should a call to getText() return slide text? | |||
* Default is yes | |||
*/ | |||
@SuppressWarnings("WeakerAccess") | |||
public void setSlidesByDefault(final boolean slidesByDefault) { | |||
this.slidesByDefault = slidesByDefault; | |||
delegate.setSlidesByDefault(slidesByDefault); | |||
} | |||
/** | |||
* Should a call to getText() return notes text? | |||
* Default is no | |||
*/ | |||
@SuppressWarnings("WeakerAccess") | |||
public void setNotesByDefault(final boolean notesByDefault) { | |||
this.notesByDefault = notesByDefault; | |||
delegate.setNotesByDefault(notesByDefault); | |||
} | |||
/** | |||
* Should a call to getText() return comments text? Default is no | |||
*/ | |||
@SuppressWarnings({"WeakerAccess", "unused"}) | |||
public void setCommentsByDefault(final boolean commentsByDefault) { | |||
this.commentsByDefault = commentsByDefault; | |||
delegate.setCommentsByDefault(commentsByDefault); | |||
} | |||
/** | |||
* Should a call to getText() return text from master? Default is no | |||
*/ | |||
@SuppressWarnings("WeakerAccess") | |||
public void setMasterByDefault(final boolean masterByDefault) { | |||
this.masterByDefault = masterByDefault; | |||
delegate.setMasterByDefault(masterByDefault); | |||
} | |||
/** | |||
* Gets the slide text, but not the notes text | |||
*/ | |||
@Override | |||
public String getText() { | |||
return getText(slidesByDefault, notesByDefault); | |||
} | |||
/** | |||
* Gets the requested text from the file | |||
* @param slideText Should we retrieve text from slides? | |||
* @param notesText Should we retrieve text from notes? | |||
*/ | |||
public String getText(boolean slideText, boolean notesText) { | |||
return getText(slideText, notesText, masterByDefault); | |||
} | |||
/** | |||
* Gets the requested text from the file | |||
* | |||
* @param slideText Should we retrieve text from slides? | |||
* @param notesText Should we retrieve text from notes? | |||
* @param masterText Should we retrieve text from master slides? | |||
* | |||
* @return the extracted text | |||
*/ | |||
public String getText(boolean slideText, boolean notesText, boolean masterText) { | |||
StringBuilder text = new StringBuilder(); | |||
for (XSLFSlide slide : slideshow.getSlides()) { | |||
text.append(getText(slide, slideText, notesText, masterText)); | |||
} | |||
return text.toString(); | |||
} | |||
/** | |||
* Gets the requested text from the slide | |||
* | |||
* @param slide the slide to retrieve the text from | |||
* @param slideText Should we retrieve text from slides? | |||
* @param notesText Should we retrieve text from notes? | |||
* @param masterText Should we retrieve text from master slides? | |||
* | |||
* @return the extracted text | |||
*/ | |||
public static String getText(XSLFSlide slide, boolean slideText, boolean notesText, boolean masterText) { | |||
StringBuilder text = new StringBuilder(); | |||
XSLFCommentAuthors commentAuthors = slide.getSlideShow().getCommentAuthors(); | |||
XSLFNotes notes = slide.getNotes(); | |||
XSLFComments comments = slide.getComments(); | |||
XSLFSlideLayout layout = slide.getSlideLayout(); | |||
XSLFSlideMaster master = layout.getSlideMaster(); | |||
// TODO Do the slide's name | |||
// (Stored in docProps/app.xml) | |||
// Do the slide's text if requested | |||
if (slideText) { | |||
extractText(slide, false, text); | |||
// If requested, get text from the master and it's layout | |||
if(masterText) { | |||
assert (layout != null); | |||
extractText(layout, true, text); | |||
assert (master != null); | |||
extractText(master, true, text); | |||
} | |||
// If the slide has comments, do those too | |||
if (comments != null) { | |||
for (CTComment comment : comments.getCTCommentsList().getCmArray()) { | |||
// Do the author if we can | |||
if (commentAuthors != null) { | |||
CTCommentAuthor author = commentAuthors.getAuthorById(comment.getAuthorId()); | |||
if(author != null) { | |||
text.append(author.getName() + ": "); | |||
} | |||
} | |||
// Then the comment text, with a new line afterwards | |||
text.append(comment.getText()); | |||
text.append("\n"); | |||
} | |||
} | |||
} | |||
// Do the notes if requested | |||
if (notesText && notes != null) { | |||
extractText(notes, false, text); | |||
} | |||
return text.toString(); | |||
} | |||
private static void extractText(XSLFShapeContainer data, boolean skipPlaceholders, StringBuilder text) { | |||
for (XSLFShape s : data) { | |||
if (s instanceof XSLFShapeContainer) { | |||
extractText((XSLFShapeContainer)s, skipPlaceholders, text); | |||
} else if (s instanceof XSLFTextShape) { | |||
XSLFTextShape ts = (XSLFTextShape)s; | |||
// Skip non-customised placeholder text | |||
if (!(skipPlaceholders && ts.isPlaceholder())) { | |||
text.append(ts.getText()); | |||
text.append("\n"); | |||
} | |||
} else if (s instanceof XSLFTable) { | |||
XSLFTable ts = (XSLFTable)s; | |||
// Skip non-customised placeholder text | |||
for (XSLFTableRow r : ts) { | |||
for (XSLFTableCell c : r) { | |||
text.append(c.getText()); | |||
text.append("\t"); | |||
} | |||
text.append("\n"); | |||
} | |||
} | |||
} | |||
} | |||
return delegate.getText(); | |||
} | |||
/** | |||
* Gets the requested text from the file | |||
* | |||
* @param slideText Should we retrieve text from slides? | |||
* @param notesText Should we retrieve text from notes? | |||
*/ | |||
public String getText(final boolean slideText, final boolean notesText) { | |||
return getText(slideText, notesText, commentsByDefault, masterByDefault); | |||
} | |||
/** | |||
* Gets the requested text from the file | |||
* | |||
* @param slideText Should we retrieve text from slides? | |||
* @param notesText Should we retrieve text from notes? | |||
* @param masterText Should we retrieve text from master slides? | |||
* @return the extracted text | |||
*/ | |||
public String getText(boolean slideText, boolean notesText, boolean masterText) { | |||
return getText(slideText, notesText, commentsByDefault, masterText); | |||
} | |||
/** | |||
* Gets the requested text from the file | |||
* | |||
* @param slideText Should we retrieve text from slides? | |||
* @param notesText Should we retrieve text from notes? | |||
* @param commentText Should we retrieve text from comments? | |||
* @param masterText Should we retrieve text from master slides? | |||
* @return the extracted text | |||
*/ | |||
@SuppressWarnings("Duplicates") | |||
public String getText(boolean slideText, boolean notesText, boolean commentText, boolean masterText) { | |||
delegate.setSlidesByDefault(slideText); | |||
delegate.setNotesByDefault(notesText); | |||
delegate.setCommentsByDefault(commentText); | |||
delegate.setMasterByDefault(masterText); | |||
try { | |||
return delegate.getText(); | |||
} finally { | |||
delegate.setSlidesByDefault(slidesByDefault); | |||
delegate.setNotesByDefault(notesByDefault); | |||
delegate.setCommentsByDefault(commentsByDefault); | |||
delegate.setMasterByDefault(masterByDefault); | |||
} | |||
} | |||
/** | |||
* Gets the requested text from the slide | |||
* | |||
* @param slide the slide to retrieve the text from | |||
* @param slideText Should we retrieve text from slides? | |||
* @param notesText Should we retrieve text from notes? | |||
* @param masterText Should we retrieve text from master slides? | |||
* @return the extracted text | |||
*/ | |||
public static String getText(XSLFSlide slide, boolean slideText, boolean notesText, boolean masterText) { | |||
return getText(slide, slideText, notesText, false, masterText); | |||
} | |||
/** | |||
* Gets the requested text from the slide | |||
* | |||
* @param slide the slide to retrieve the text from | |||
* @param slideText Should we retrieve text from slides? | |||
* @param notesText Should we retrieve text from notes? | |||
* @param commentText Should we retrieve text from comments? | |||
* @param masterText Should we retrieve text from master slides? | |||
* @return the extracted text | |||
*/ | |||
public static String getText(XSLFSlide slide, boolean slideText, boolean notesText, boolean commentText, boolean masterText) { | |||
final SlideShowExtractor<XSLFShape, XSLFTextParagraph> ex = new SlideShowExtractor<>(slide.getSlideShow()); | |||
ex.setSlidesByDefault(slideText); | |||
ex.setNotesByDefault(notesText); | |||
ex.setCommentsByDefault(commentText); | |||
ex.setMasterByDefault(masterText); | |||
return ex.getText(slide); | |||
} | |||
} |
@@ -35,10 +35,10 @@ import java.util.regex.Pattern; | |||
import org.apache.poi.POIXMLDocument; | |||
import org.apache.poi.POIXMLDocumentPart; | |||
import org.apache.poi.POIXMLException; | |||
import org.apache.poi.POIXMLPropertiesTextExtractor; | |||
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.openxml4j.opc.PackageRelationship; | |||
import org.apache.poi.sl.usermodel.MasterSheet; | |||
import org.apache.poi.sl.usermodel.PictureData.PictureType; | |||
import org.apache.poi.sl.usermodel.Resources; | |||
@@ -626,4 +626,9 @@ public class XMLSlideShow extends POIXMLDocument | |||
// TODO: implement! | |||
throw new UnsupportedOperationException(); | |||
} | |||
@Override | |||
public POIXMLPropertiesTextExtractor getMetadataTextExtractor() { | |||
return new POIXMLPropertiesTextExtractor(this); | |||
} | |||
} |
@@ -0,0 +1,126 @@ | |||
/* ==================================================================== | |||
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 java.awt.geom.Point2D; | |||
import java.util.Calendar; | |||
import java.util.Date; | |||
import org.apache.poi.sl.usermodel.Comment; | |||
import org.apache.poi.util.LocaleUtil; | |||
import org.apache.poi.util.Units; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTPoint2D; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.CTComment; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.CTCommentAuthor; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.CTCommentAuthorList; | |||
/** | |||
* XSLF Comment | |||
* | |||
* @since POI 4.0.0 | |||
*/ | |||
public class XSLFComment implements Comment { | |||
final CTComment comment; | |||
final XSLFCommentAuthors authors; | |||
XSLFComment(final CTComment comment, final XSLFCommentAuthors authors) { | |||
this.comment = comment; | |||
this.authors = authors; | |||
} | |||
@Override | |||
public String getAuthor() { | |||
return authors.getAuthorById(comment.getAuthorId()).getName(); | |||
} | |||
@Override | |||
public void setAuthor(final String author) { | |||
if (author == null) { | |||
throw new IllegalArgumentException("author must not be null"); | |||
} | |||
final CTCommentAuthorList list = authors.getCTCommentAuthorsList(); | |||
long maxId = -1; | |||
for (final CTCommentAuthor aut : list.getCmAuthorArray()) { | |||
maxId = Math.max(aut.getId(), maxId); | |||
if (author.equals(aut.getName())) { | |||
comment.setAuthorId(aut.getId()); | |||
return; | |||
} | |||
} | |||
// author not found -> add new author | |||
final CTCommentAuthor newAuthor = list.addNewCmAuthor(); | |||
newAuthor.setName(author); | |||
newAuthor.setId(maxId+1); | |||
newAuthor.setInitials(author.replaceAll( "\\s*(\\w)\\S*", "$1").toUpperCase(LocaleUtil.getUserLocale())); | |||
comment.setAuthorId(maxId+1); | |||
} | |||
@Override | |||
public String getAuthorInitials() { | |||
final CTCommentAuthor aut = authors.getAuthorById(comment.getAuthorId()); | |||
return aut == null ? null : aut.getInitials(); | |||
} | |||
@Override | |||
public void setAuthorInitials(final String initials) { | |||
final CTCommentAuthor aut = authors.getAuthorById(comment.getAuthorId()); | |||
if (aut != null) { | |||
aut.setInitials(initials); | |||
} | |||
} | |||
@Override | |||
public String getText() { | |||
return comment.getText(); | |||
} | |||
@Override | |||
public void setText(final String text) { | |||
comment.setText(text); | |||
} | |||
@Override | |||
public Date getDate() { | |||
final Calendar cal = comment.getDt(); | |||
return (cal == null) ? null : cal.getTime(); | |||
} | |||
@Override | |||
public void setDate(final Date date) { | |||
final Calendar cal = LocaleUtil.getLocaleCalendar(); | |||
cal.setTime(date); | |||
comment.setDt(cal); | |||
} | |||
@Override | |||
public Point2D getOffset() { | |||
final CTPoint2D pos = comment.getPos(); | |||
return new Point2D.Double(Units.toPoints(pos.getX()), Units.toPoints(pos.getY())); | |||
} | |||
@Override | |||
public void setOffset(final Point2D offset) { | |||
CTPoint2D pos = comment.getPos(); | |||
if (pos == null) { | |||
pos = comment.addNewPos(); | |||
} | |||
pos.setX(Units.toEMU(offset.getX())); | |||
pos.setY(Units.toEMU(offset.getY())); | |||
} | |||
} |
@@ -31,42 +31,37 @@ import org.openxmlformats.schemas.presentationml.x2006.main.CmLstDocument; | |||
@Beta | |||
public class XSLFComments extends POIXMLDocumentPart { | |||
private final CTCommentList _comments; | |||
private final CmLstDocument doc; | |||
/** | |||
* Create a new set of slide comments | |||
*/ | |||
XSLFComments() { | |||
super(); | |||
CmLstDocument doc = CmLstDocument.Factory.newInstance(); | |||
_comments = doc.addNewCmLst(); | |||
doc = CmLstDocument.Factory.newInstance(); | |||
} | |||
/** | |||
* Construct a SpreadsheetML slide comments from a package part | |||
* | |||
* @param part the package part holding the comments data, | |||
* the content type must be <code>application/vnd.openxmlformats-officedocument.comments+xml</code> | |||
* | |||
* the content type must be <code>application/vnd.openxmlformats-officedocument.comments+xml</code> | |||
* @since POI 3.14-Beta1 | |||
*/ | |||
XSLFComments(PackagePart part) throws IOException, XmlException { | |||
super(part); | |||
CmLstDocument doc = | |||
CmLstDocument.Factory.parse(getPackagePart().getInputStream(), DEFAULT_XML_OPTIONS); | |||
_comments = doc.getCmLst(); | |||
doc = CmLstDocument.Factory.parse(getPackagePart().getInputStream(), DEFAULT_XML_OPTIONS); | |||
} | |||
public CTCommentList getCTCommentsList() { | |||
return _comments; | |||
return doc.getCmLst(); | |||
} | |||
public int getNumberOfComments() { | |||
return _comments.sizeOfCmArray(); | |||
return doc.getCmLst().sizeOfCmArray(); | |||
} | |||
public CTComment getCommentAt(int pos) { | |||
return _comments.getCmArray(pos); | |||
return doc.getCmLst().getCmArray(pos); | |||
} | |||
} |
@@ -51,7 +51,6 @@ import org.openxmlformats.schemas.presentationml.x2006.main.NotesMasterDocument; | |||
public class XSLFNotesMaster extends XSLFSheet | |||
implements MasterSheet<XSLFShape,XSLFTextParagraph> { | |||
private CTNotesMaster _slide; | |||
private XSLFTheme _theme; | |||
XSLFNotesMaster() { | |||
super(); | |||
@@ -100,21 +99,15 @@ import org.openxmlformats.schemas.presentationml.x2006.main.NotesMasterDocument; | |||
public MasterSheet<XSLFShape,XSLFTextParagraph> getMasterSheet() { | |||
return null; | |||
} | |||
@Override | |||
public XSLFTheme getTheme() { | |||
if (_theme == null) { | |||
for (POIXMLDocumentPart p : getRelations()) { | |||
if (p instanceof XSLFTheme) { | |||
_theme = (XSLFTheme) p; | |||
CTColorMapping cmap = _slide.getClrMap(); | |||
if (cmap != null) { | |||
_theme.initColorMap(cmap); | |||
} | |||
break; | |||
} | |||
} | |||
} | |||
return _theme; | |||
} | |||
boolean isSupportTheme() { | |||
return true; | |||
} | |||
@Override | |||
CTColorMapping getColorMapping() { | |||
return _slide.getClrMap(); | |||
} | |||
} |
@@ -0,0 +1,203 @@ | |||
package org.apache.poi.xslf.usermodel; | |||
import static org.apache.poi.xslf.usermodel.XSLFShape.PML_NS; | |||
import java.util.function.Consumer; | |||
import java.util.function.Function; | |||
import org.apache.poi.sl.usermodel.MasterSheet; | |||
import org.apache.poi.sl.usermodel.Placeholder; | |||
import org.apache.poi.sl.usermodel.PlaceholderDetails; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.CTApplicationNonVisualDrawingProps; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.CTHeaderFooter; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.CTNotesMaster; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideMaster; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.STPlaceholderSize; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.STPlaceholderType; | |||
/** | |||
* XSLF Placeholder Details | |||
* | |||
* @since POI 4.0.0 | |||
*/ | |||
public class XSLFPlaceholderDetails implements PlaceholderDetails { | |||
private final XSLFShape shape; | |||
private CTPlaceholder _ph; | |||
XSLFPlaceholderDetails(final XSLFShape shape) { | |||
this.shape = shape; | |||
} | |||
@Override | |||
public Placeholder getPlaceholder() { | |||
final CTPlaceholder ph = getCTPlaceholder(false); | |||
if (ph == null || !(ph.isSetType() || ph.isSetIdx())) { | |||
return null; | |||
} | |||
return Placeholder.lookupOoxml(ph.getType().intValue()); | |||
} | |||
@Override | |||
public void setPlaceholder(final Placeholder placeholder) { | |||
CTPlaceholder ph = getCTPlaceholder(placeholder != null); | |||
if (ph != null) { | |||
if (placeholder != null) { | |||
ph.setType(STPlaceholderType.Enum.forInt(placeholder.ooxmlId)); | |||
} else { | |||
getNvProps().unsetPh(); | |||
} | |||
} | |||
} | |||
@Override | |||
public boolean isVisible() { | |||
final CTPlaceholder ph = getCTPlaceholder(false); | |||
if (ph == null || !ph.isSetType()) { | |||
return true; | |||
} | |||
final CTHeaderFooter hf = getHeaderFooter(false); | |||
if (hf == null) { | |||
return false; | |||
} | |||
final Placeholder pl = Placeholder.lookupOoxml(ph.getType().intValue()); | |||
if (pl == null) { | |||
return true; | |||
} | |||
switch (pl) { | |||
case DATETIME: | |||
return !hf.isSetDt() || hf.getDt(); | |||
case FOOTER: | |||
return !hf.isSetFtr() || hf.getFtr(); | |||
case HEADER: | |||
return !hf.isSetHdr() || hf.getHdr(); | |||
case SLIDE_NUMBER: | |||
return !hf.isSetSldNum() || hf.getSldNum(); | |||
default: | |||
return true; | |||
} | |||
} | |||
@Override | |||
public void setVisible(final boolean isVisible) { | |||
final Placeholder ph = getPlaceholder(); | |||
if (ph == null) { | |||
return; | |||
} | |||
final Function<CTHeaderFooter,Consumer<Boolean>> fun; | |||
switch (ph) { | |||
case DATETIME: | |||
fun = (hf) -> hf::setDt; | |||
break; | |||
case FOOTER: | |||
fun = (hf) -> hf::setFtr; | |||
break; | |||
case HEADER: | |||
fun = (hf) -> hf::setHdr; | |||
break; | |||
case SLIDE_NUMBER: | |||
fun = (hf) -> hf::setSldNum; | |||
break; | |||
default: | |||
return; | |||
} | |||
// only create a header, if we need to, i.e. the placeholder type is eligible | |||
final CTHeaderFooter hf = getHeaderFooter(true); | |||
if (hf == null) { | |||
return; | |||
} | |||
fun.apply(hf).accept(isVisible); | |||
} | |||
@Override | |||
public PlaceholderSize getSize() { | |||
final CTPlaceholder ph = getCTPlaceholder(false); | |||
if (ph == null || !ph.isSetSz()) { | |||
return null; | |||
} | |||
switch (ph.getSz().intValue()) { | |||
case STPlaceholderSize.INT_FULL: | |||
return PlaceholderSize.full; | |||
case STPlaceholderSize.INT_HALF: | |||
return PlaceholderSize.half; | |||
case STPlaceholderSize.INT_QUARTER: | |||
return PlaceholderSize.quarter; | |||
default: | |||
return null; | |||
} | |||
} | |||
@Override | |||
public void setSize(final PlaceholderSize size) { | |||
final CTPlaceholder ph = getCTPlaceholder(false); | |||
if (ph == null) { | |||
return; | |||
} | |||
if (size == null) { | |||
ph.unsetSz(); | |||
return; | |||
} | |||
switch (size) { | |||
case full: | |||
ph.setSz(STPlaceholderSize.FULL); | |||
break; | |||
case half: | |||
ph.setSz(STPlaceholderSize.HALF); | |||
break; | |||
case quarter: | |||
ph.setSz(STPlaceholderSize.QUARTER); | |||
break; | |||
} | |||
} | |||
/** | |||
* Gets or creates a new placeholder element | |||
* | |||
* @param create if {@code true} creates the element if it hasn't existed before | |||
* @return the placeholder or {@code null} if the shape doesn't support placeholders | |||
*/ | |||
CTPlaceholder getCTPlaceholder(final boolean create) { | |||
if (_ph != null) { | |||
return _ph; | |||
} | |||
final CTApplicationNonVisualDrawingProps nv = getNvProps(); | |||
if (nv == null) { | |||
// shape doesn't support CTApplicationNonVisualDrawingProps | |||
return null; | |||
} | |||
_ph = (nv.isSetPh() || !create) ? nv.getPh() : nv.addNewPh(); | |||
return _ph; | |||
} | |||
private CTApplicationNonVisualDrawingProps getNvProps() { | |||
final String xquery = "declare namespace p='" + PML_NS + "' .//*/p:nvPr"; | |||
return shape.selectProperty(CTApplicationNonVisualDrawingProps.class, xquery); | |||
} | |||
private CTHeaderFooter getHeaderFooter(final boolean create) { | |||
final XSLFSheet sheet = shape.getSheet(); | |||
final XSLFSheet master = (sheet instanceof MasterSheet && !(sheet instanceof XSLFSlideLayout)) ? sheet : (XSLFSheet)sheet.getMasterSheet(); | |||
if (master instanceof XSLFSlideMaster) { | |||
final CTSlideMaster ct = ((XSLFSlideMaster) master).getXmlObject(); | |||
return (ct.isSetHf() || !create) ? ct.getHf() : ct.addNewHf(); | |||
} else if (master instanceof XSLFNotesMaster) { | |||
final CTNotesMaster ct = ((XSLFNotesMaster) master).getXmlObject(); | |||
return (ct.isSetHf() || !create) ? ct.getHf() : ct.addNewHf(); | |||
} else { | |||
return null; | |||
} | |||
} | |||
@Override | |||
public String getText() { | |||
return null; | |||
} | |||
@Override | |||
public void setText(String text) { | |||
} | |||
} |
@@ -38,7 +38,9 @@ import org.apache.poi.sl.usermodel.PaintStyle.GradientPaint; | |||
import org.apache.poi.sl.usermodel.PaintStyle.TexturePaint; | |||
import org.apache.poi.sl.usermodel.PlaceableShape; | |||
import org.apache.poi.sl.usermodel.Placeholder; | |||
import org.apache.poi.sl.usermodel.PlaceholderDetails; | |||
import org.apache.poi.sl.usermodel.Shape; | |||
import org.apache.poi.sl.usermodel.SimpleShape; | |||
import org.apache.poi.util.Beta; | |||
import org.apache.poi.util.Internal; | |||
import org.apache.poi.xslf.model.PropertyFetcher; | |||
@@ -58,7 +60,6 @@ import org.openxmlformats.schemas.drawingml.x2006.main.CTSolidColorFillPropertie | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTStyleMatrix; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTStyleMatrixReference; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.STPathShadeType; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.CTApplicationNonVisualDrawingProps; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.CTBackgroundProperties; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.CTShape; | |||
@@ -69,7 +70,7 @@ import org.openxmlformats.schemas.presentationml.x2006.main.STPlaceholderType; | |||
*/ | |||
@Beta | |||
public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> { | |||
protected static final String PML_NS = "http://schemas.openxmlformats.org/presentationml/2006/main"; | |||
static final String PML_NS = "http://schemas.openxmlformats.org/presentationml/2006/main"; | |||
private final XmlObject _shape; | |||
private final XSLFSheet _sheet; | |||
@@ -77,7 +78,6 @@ public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> { | |||
private CTShapeStyle _spStyle; | |||
private CTNonVisualDrawingProps _nvPr; | |||
private CTPlaceholder _ph; | |||
protected XSLFShape(XmlObject shape, XSLFSheet sheet) { | |||
_shape = shape; | |||
@@ -238,45 +238,32 @@ public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> { | |||
cur.dispose(); | |||
return child; | |||
} | |||
protected CTPlaceholder getCTPlaceholder() { | |||
if (_ph == null) { | |||
String xquery = "declare namespace p='"+PML_NS+"' .//*/p:nvPr/p:ph"; | |||
_ph = selectProperty(CTPlaceholder.class, xquery); | |||
} | |||
return _ph; | |||
public boolean isPlaceholder() { | |||
return getPlaceholderDetails().getCTPlaceholder(false) != null; | |||
} | |||
/** | |||
* @see PlaceholderDetails#getPlaceholder() | |||
*/ | |||
public Placeholder getPlaceholder() { | |||
CTPlaceholder ph = getCTPlaceholder(); | |||
if (ph == null || !(ph.isSetType() || ph.isSetIdx())) { | |||
return null; | |||
} | |||
return Placeholder.lookupOoxml(ph.getType().intValue()); | |||
return getPlaceholderDetails().getPlaceholder(); | |||
} | |||
/** | |||
* Specifies that the corresponding shape should be represented by the generating application | |||
* as a placeholder. When a shape is considered a placeholder by the generating application | |||
* it can have special properties to alert the user that they may enter content into the shape. | |||
* Different types of placeholders are allowed and can be specified by using the placeholder | |||
* type attribute for this element | |||
* | |||
* @param placeholder The shape to use as placeholder or null if no placeholder should be set. | |||
* @see PlaceholderDetails#setPlaceholder(Placeholder) | |||
*/ | |||
protected void setPlaceholder(Placeholder placeholder) { | |||
String xquery = "declare namespace p='"+PML_NS+"' .//*/p:nvPr"; | |||
CTApplicationNonVisualDrawingProps nv = selectProperty(CTApplicationNonVisualDrawingProps.class, xquery); | |||
if (nv == null) return; | |||
if(placeholder == null) { | |||
if (nv.isSetPh()) nv.unsetPh(); | |||
_ph = null; | |||
} else { | |||
nv.addNewPh().setType(STPlaceholderType.Enum.forInt(placeholder.ooxmlId)); | |||
} | |||
public void setPlaceholder(final Placeholder placeholder) { | |||
getPlaceholderDetails().setPlaceholder(placeholder); | |||
} | |||
/** | |||
* @see SimpleShape#getPlaceholderDetails() | |||
*/ | |||
public XSLFPlaceholderDetails getPlaceholderDetails() { | |||
return new XSLFPlaceholderDetails(this); | |||
} | |||
/** | |||
* As there's no xmlbeans hierarchy, but XSLF works with subclassing, not all | |||
* child classes work with a {@link CTShape} object, but often contain the same | |||
@@ -315,7 +302,7 @@ public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> { | |||
return true; | |||
} | |||
CTPlaceholder ph = getCTPlaceholder(); | |||
final CTPlaceholder ph = getPlaceholderDetails().getCTPlaceholder(false); | |||
if (ph == null) { | |||
return false; | |||
} |
@@ -18,6 +18,7 @@ package org.apache.poi.xslf.usermodel; | |||
import static org.apache.poi.POIXMLTypeLoader.DEFAULT_XML_OPTIONS; | |||
import javax.xml.namespace.QName; | |||
import java.awt.Dimension; | |||
import java.awt.Graphics2D; | |||
import java.io.IOException; | |||
@@ -28,8 +29,7 @@ import java.util.HashMap; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import java.util.Map; | |||
import javax.xml.namespace.QName; | |||
import java.util.Optional; | |||
import org.apache.poi.POIXMLDocumentPart; | |||
import org.apache.poi.POIXMLException; | |||
@@ -56,6 +56,7 @@ import org.apache.xmlbeans.XmlException; | |||
import org.apache.xmlbeans.XmlObject; | |||
import org.apache.xmlbeans.XmlOptions; | |||
import org.apache.xmlbeans.impl.values.XmlAnyTypeImpl; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTColorMapping; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.CTConnector; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.CTGraphicalObjectFrame; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.CTGroupShape; | |||
@@ -72,6 +73,7 @@ implements XSLFShapeContainer, Sheet<XSLFShape,XSLFTextParagraph> { | |||
private XSLFDrawing _drawing; | |||
private List<XSLFShape> _shapes; | |||
private CTGroupShape _spTree; | |||
private XSLFTheme _theme; | |||
private List<XSLFTextShape>_placeholders; | |||
private Map<Integer, XSLFSimpleShape> _placeholderByIdMap; | |||
@@ -456,7 +458,36 @@ implements XSLFShapeContainer, Sheet<XSLFShape,XSLFTextParagraph> { | |||
* Sheets that support the notion of themes (slides, masters, layouts, etc.) should override this | |||
* method and return the corresponding package part. | |||
*/ | |||
XSLFTheme getTheme(){ | |||
public XSLFTheme getTheme() { | |||
if (_theme != null || !isSupportTheme()) { | |||
return _theme; | |||
} | |||
final Optional<XSLFTheme> t = | |||
getRelations().stream().filter((p) -> p instanceof XSLFTheme).map((p) -> (XSLFTheme) p).findAny(); | |||
if (t.isPresent()) { | |||
_theme = t.get(); | |||
final CTColorMapping cmap = getColorMapping(); | |||
if (cmap != null) { | |||
_theme.initColorMap(cmap); | |||
} | |||
} | |||
return _theme; | |||
} | |||
/** | |||
* @return {@code true} if this class supports themes | |||
*/ | |||
boolean isSupportTheme() { | |||
return false; | |||
} | |||
/** | |||
* @return the color mapping for this slide type | |||
*/ | |||
CTColorMapping getColorMapping() { | |||
return null; | |||
} | |||
@@ -488,16 +519,16 @@ implements XSLFShapeContainer, Sheet<XSLFShape,XSLFTextParagraph> { | |||
return shape; | |||
} | |||
void initPlaceholders() { | |||
private void initPlaceholders() { | |||
if(_placeholders == null) { | |||
_placeholders = new ArrayList<>(); | |||
_placeholderByIdMap = new HashMap<>(); | |||
_placeholderByTypeMap = new HashMap<>(); | |||
for(XSLFShape sh : getShapes()){ | |||
for(final XSLFShape sh : getShapes()){ | |||
if(sh instanceof XSLFTextShape){ | |||
XSLFTextShape sShape = (XSLFTextShape)sh; | |||
CTPlaceholder ph = sShape.getCTPlaceholder(); | |||
final XSLFTextShape sShape = (XSLFTextShape)sh; | |||
final CTPlaceholder ph = sShape.getPlaceholderDetails().getCTPlaceholder(false); | |||
if(ph != null) { | |||
_placeholders.add(sShape); | |||
if(ph.isSetIdx()) { | |||
@@ -513,7 +544,7 @@ implements XSLFShapeContainer, Sheet<XSLFShape,XSLFTextParagraph> { | |||
} | |||
} | |||
XSLFSimpleShape getPlaceholderById(int id) { | |||
private XSLFSimpleShape getPlaceholderById(int id) { | |||
initPlaceholders(); | |||
return _placeholderByIdMap.get(id); | |||
} | |||
@@ -574,7 +605,7 @@ implements XSLFShapeContainer, Sheet<XSLFShape,XSLFTextParagraph> { | |||
/** | |||
* Render this sheet into the supplied graphics object | |||
* | |||
* @param graphics | |||
* @param graphics the graphics context to draw to | |||
*/ | |||
@Override | |||
public void draw(Graphics2D graphics){ | |||
@@ -645,4 +676,12 @@ implements XSLFShapeContainer, Sheet<XSLFShape,XSLFTextParagraph> { | |||
void removePictureRelation(XSLFPictureShape pictureShape) { | |||
removeRelation(pictureShape.getBlipId()); | |||
} | |||
@Override | |||
public XSLFPlaceholderDetails getPlaceholderDetails(Placeholder placeholder) { | |||
final XSLFSimpleShape ph = getPlaceholder(placeholder); | |||
return (ph == null) ? null : new XSLFPlaceholderDetails(ph); | |||
} | |||
} |
@@ -36,7 +36,6 @@ import org.apache.poi.sl.usermodel.LineDecoration.DecorationShape; | |||
import org.apache.poi.sl.usermodel.LineDecoration.DecorationSize; | |||
import org.apache.poi.sl.usermodel.PaintStyle; | |||
import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint; | |||
import org.apache.poi.sl.usermodel.Placeholder; | |||
import org.apache.poi.sl.usermodel.ShapeType; | |||
import org.apache.poi.sl.usermodel.SimpleShape; | |||
import org.apache.poi.sl.usermodel.StrokeStyle; | |||
@@ -980,11 +979,6 @@ public abstract class XSLFSimpleShape extends XSLFShape | |||
return ds; | |||
} | |||
public boolean isPlaceholder() { | |||
CTPlaceholder ph = getCTPlaceholder(); | |||
return ph != null; | |||
} | |||
@Override | |||
public Guide getAdjustValue(String name) { | |||
XSLFGeometryProperties gp = XSLFPropertiesDelegate.getGeometryDelegate(getShapeProperties()); | |||
@@ -1105,11 +1099,6 @@ public abstract class XSLFSimpleShape extends XSLFShape | |||
} | |||
} | |||
@Override | |||
public void setPlaceholder(Placeholder placeholder) { | |||
super.setPlaceholder(placeholder); | |||
} | |||
@Override | |||
public XSLFHyperlink getHyperlink() { | |||
CTNonVisualDrawingProps cNvPr = getCNvPr(); |
@@ -20,6 +20,8 @@ import static org.apache.poi.POIXMLTypeLoader.DEFAULT_XML_OPTIONS; | |||
import java.awt.Graphics2D; | |||
import java.io.IOException; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import org.apache.poi.POIXMLDocumentPart; | |||
import org.apache.poi.openxml4j.opc.PackagePart; | |||
@@ -38,6 +40,7 @@ import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTPoint2D; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.CTBackground; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.CTComment; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.CTCommonSlideData; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.CTGroupShape; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.CTGroupShapeNonVisual; | |||
@@ -52,6 +55,7 @@ implements Slide<XSLFShape,XSLFTextParagraph> { | |||
private final CTSlide _slide; | |||
private XSLFSlideLayout _layout; | |||
private XSLFComments _comments; | |||
private XSLFCommentAuthors _commentAuthors; | |||
private XSLFNotes _notes; | |||
/** | |||
@@ -155,22 +159,55 @@ implements Slide<XSLFShape,XSLFTextParagraph> { | |||
return getSlideLayout().getSlideMaster(); | |||
} | |||
public XSLFComments getComments() { | |||
/** | |||
* @return the comments part or {@code null} if there weren't any comments | |||
* @since POI 4.0.0 | |||
*/ | |||
public XSLFComments getCommentsPart() { | |||
if(_comments == null) { | |||
for (POIXMLDocumentPart p : getRelations()) { | |||
if (p instanceof XSLFComments) { | |||
_comments = (XSLFComments)p; | |||
break; | |||
} | |||
} | |||
} | |||
if(_comments == null) { | |||
// This slide lacks comments | |||
// Not all have them, sorry... | |||
return null; | |||
} | |||
return _comments; | |||
} | |||
/** | |||
* @return the comment authors part or {@code null} if there weren't any comments | |||
* @since POI 4.0.0 | |||
*/ | |||
public XSLFCommentAuthors getCommentAuthorsPart() { | |||
if(_commentAuthors == null) { | |||
for (POIXMLDocumentPart p : getRelations()) { | |||
if (p instanceof XSLFCommentAuthors) { | |||
_commentAuthors = (XSLFCommentAuthors)p; | |||
return _commentAuthors; | |||
} | |||
} | |||
} | |||
return null; | |||
} | |||
@Override | |||
public List<XSLFComment> getComments() { | |||
final List<XSLFComment> comments = new ArrayList<>(); | |||
final XSLFComments xComments = getCommentsPart(); | |||
final XSLFCommentAuthors xAuthors = getCommentAuthorsPart(); | |||
if (xComments != null) { | |||
for (final CTComment xc : xComments.getCTCommentsList().getCmArray()) { | |||
comments.add(new XSLFComment(xc, xAuthors)); | |||
} | |||
} | |||
return comments; | |||
} | |||
@Override | |||
public XSLFNotes getNotes() { | |||
if(_notes == null) { |
@@ -28,21 +28,15 @@ import org.apache.poi.util.Beta; | |||
import org.apache.poi.util.Internal; | |||
import org.apache.xmlbeans.XmlException; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.CTBackground; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideLayout; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.SldLayoutDocument; | |||
@Beta | |||
public class XSLFSlideLayout extends XSLFSheet | |||
implements MasterSheet<XSLFShape,XSLFTextParagraph> { | |||
private CTSlideLayout _layout; | |||
private final CTSlideLayout _layout; | |||
private XSLFSlideMaster _master; | |||
XSLFSlideLayout() { | |||
super(); | |||
_layout = CTSlideLayout.Factory.newInstance(); | |||
} | |||
/** | |||
* @since POI 3.14-Beta1 | |||
*/ | |||
@@ -111,14 +105,7 @@ implements MasterSheet<XSLFShape,XSLFTextParagraph> { | |||
*/ | |||
@Override | |||
protected boolean canDraw(XSLFShape shape) { | |||
if (shape instanceof XSLFSimpleShape) { | |||
XSLFSimpleShape txt = (XSLFSimpleShape) shape; | |||
CTPlaceholder ph = txt.getCTPlaceholder(); | |||
if (ph != null) { | |||
return false; | |||
} | |||
} | |||
return true; | |||
return !(shape instanceof XSLFSimpleShape) || !shape.isPlaceholder(); | |||
} | |||
@@ -32,7 +32,6 @@ import org.apache.xmlbeans.XmlException; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTColorMapping; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextListStyle; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.CTBackground; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideMaster; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideMasterTextStyles; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.SldMasterDocument; | |||
@@ -43,7 +42,6 @@ import org.openxmlformats.schemas.presentationml.x2006.main.SldMasterDocument; | |||
* Within a slide master slide are contained all elements | |||
* that describe the objects and their corresponding formatting | |||
* for within a presentation slide. | |||
* </p> | |||
* <p> | |||
* Within a slide master slide are two main elements. | |||
* The cSld element specifies the common slide elements such as shapes and | |||
@@ -52,21 +50,12 @@ import org.openxmlformats.schemas.presentationml.x2006.main.SldMasterDocument; | |||
* within a slide master slide specify other properties for within a presentation slide | |||
* such as color information, headers and footers, as well as timing and | |||
* transition information for all corresponding presentation slides. | |||
* </p> | |||
* | |||
* @author Yegor Kozlov | |||
*/ | |||
@Beta | |||
public class XSLFSlideMaster extends XSLFSheet | |||
implements MasterSheet<XSLFShape,XSLFTextParagraph> { | |||
private CTSlideMaster _slide; | |||
private Map<String, XSLFSlideLayout> _layouts; | |||
private XSLFTheme _theme; | |||
XSLFSlideMaster() { | |||
super(); | |||
_slide = CTSlideMaster.Factory.newInstance(); | |||
} | |||
/** | |||
* @since POI 3.14-Beta1 | |||
@@ -142,23 +131,9 @@ import org.openxmlformats.schemas.presentationml.x2006.main.SldMasterDocument; | |||
} | |||
@Override | |||
public XSLFTheme getTheme(){ | |||
if(_theme == null){ | |||
for (POIXMLDocumentPart p : getRelations()) { | |||
if (p instanceof XSLFTheme){ | |||
_theme = (XSLFTheme)p; | |||
CTColorMapping cmap = _slide.getClrMap(); | |||
if(cmap != null){ | |||
_theme.initColorMap(cmap); | |||
} | |||
break; | |||
} | |||
} | |||
} | |||
return _theme; | |||
} | |||
@SuppressWarnings(value = "unused") | |||
protected CTTextListStyle getTextProperties(Placeholder textType) { | |||
CTTextListStyle props; | |||
CTSlideMasterTextStyles txStyles = getXmlObject().getTxStyles(); | |||
@@ -183,15 +158,8 @@ import org.openxmlformats.schemas.presentationml.x2006.main.SldMasterDocument; | |||
* | |||
*/ | |||
@Override | |||
protected boolean canDraw(XSLFShape shape){ | |||
if(shape instanceof XSLFSimpleShape){ | |||
XSLFSimpleShape txt = (XSLFSimpleShape)shape; | |||
CTPlaceholder ph = txt.getCTPlaceholder(); | |||
if(ph != null) { | |||
return false; | |||
} | |||
} | |||
return true; | |||
protected boolean canDraw(XSLFShape shape) { | |||
return !(shape instanceof XSLFSimpleShape) || !shape.isPlaceholder(); | |||
} | |||
@Override | |||
@@ -203,4 +171,14 @@ import org.openxmlformats.schemas.presentationml.x2006.main.SldMasterDocument; | |||
return null; | |||
} | |||
} | |||
@Override | |||
boolean isSupportTheme() { | |||
return true; | |||
} | |||
@Override | |||
CTColorMapping getColorMapping() { | |||
return _slide.getClrMap(); | |||
} | |||
} |
@@ -20,6 +20,9 @@ import java.awt.Color; | |||
import java.util.ArrayList; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import java.util.Objects; | |||
import java.util.function.Function; | |||
import java.util.function.Supplier; | |||
import org.apache.poi.sl.draw.DrawPaint; | |||
import org.apache.poi.sl.usermodel.AutoNumberingScheme; | |||
@@ -49,6 +52,12 @@ public class XSLFTextParagraph implements TextParagraph<XSLFShape,XSLFTextParagr | |||
private final List<XSLFTextRun> _runs; | |||
private final XSLFTextShape _shape; | |||
@FunctionalInterface | |||
private interface Procedure { | |||
void accept(); | |||
} | |||
XSLFTextParagraph(CTTextParagraph p, XSLFTextShape shape){ | |||
_p = p; | |||
_runs = new ArrayList<>(); | |||
@@ -79,14 +88,6 @@ public class XSLFTextParagraph implements TextParagraph<XSLFShape,XSLFTextParagr | |||
return out.toString(); | |||
} | |||
String getRenderableText(){ | |||
StringBuilder out = new StringBuilder(); | |||
for (XSLFTextRun r : _runs) { | |||
out.append(r.getRenderableText()); | |||
} | |||
return out.toString(); | |||
} | |||
@Internal | |||
public CTTextParagraph getXmlObject(){ | |||
return _p; | |||
@@ -125,6 +126,7 @@ public class XSLFTextParagraph implements TextParagraph<XSLFShape,XSLFTextParagr | |||
* | |||
* @return text run representing this line break ('\n') | |||
*/ | |||
@SuppressWarnings("WeakerAccess") | |||
public XSLFTextRun addLineBreak(){ | |||
XSLFLineBreak run = new XSLFLineBreak(_p.addNewBr(), this); | |||
CTTextCharacterProperties brProps = run.getRPr(true); | |||
@@ -200,6 +202,7 @@ public class XSLFTextParagraph implements TextParagraph<XSLFShape,XSLFTextParagr | |||
/** | |||
* @return the font to be used on bullet characters within a given paragraph | |||
*/ | |||
@SuppressWarnings("WeakerAccess") | |||
public String getBulletFont(){ | |||
ParagraphPropertyFetcher<String> fetcher = new ParagraphPropertyFetcher<String>(getIndentLevel()){ | |||
public boolean fetch(CTTextParagraphProperties props){ | |||
@@ -214,6 +217,7 @@ public class XSLFTextParagraph implements TextParagraph<XSLFShape,XSLFTextParagr | |||
return fetcher.getValue(); | |||
} | |||
@SuppressWarnings("WeakerAccess") | |||
public void setBulletFont(String typeface){ | |||
CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); | |||
CTTextFont font = pr.isSetBuFont() ? pr.getBuFont() : pr.addNewBuFont(); | |||
@@ -223,6 +227,7 @@ public class XSLFTextParagraph implements TextParagraph<XSLFShape,XSLFTextParagr | |||
/** | |||
* @return the character to be used in place of the standard bullet point | |||
*/ | |||
@SuppressWarnings("WeakerAccess") | |||
public String getBulletCharacter(){ | |||
ParagraphPropertyFetcher<String> fetcher = new ParagraphPropertyFetcher<String>(getIndentLevel()){ | |||
public boolean fetch(CTTextParagraphProperties props){ | |||
@@ -237,6 +242,7 @@ public class XSLFTextParagraph implements TextParagraph<XSLFShape,XSLFTextParagr | |||
return fetcher.getValue(); | |||
} | |||
@SuppressWarnings("WeakerAccess") | |||
public void setBulletCharacter(String str){ | |||
CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); | |||
CTTextCharBullet c = pr.isSetBuChar() ? pr.getBuChar() : pr.addNewBuChar(); | |||
@@ -248,6 +254,7 @@ public class XSLFTextParagraph implements TextParagraph<XSLFShape,XSLFTextParagr | |||
* @return the color of bullet characters within a given paragraph. | |||
* A <code>null</code> value means to use the text font color. | |||
*/ | |||
@SuppressWarnings("WeakerAccess") | |||
public PaintStyle getBulletFontColor(){ | |||
final XSLFTheme theme = getParentShape().getSheet().getTheme(); | |||
ParagraphPropertyFetcher<Color> fetcher = new ParagraphPropertyFetcher<Color>(getIndentLevel()){ | |||
@@ -265,6 +272,7 @@ public class XSLFTextParagraph implements TextParagraph<XSLFShape,XSLFTextParagr | |||
return (col == null) ? null : DrawPaint.createSolidPaint(col); | |||
} | |||
@SuppressWarnings("WeakerAccess") | |||
public void setBulletFontColor(Color color) { | |||
setBulletFontColor(DrawPaint.createSolidPaint(color)); | |||
} | |||
@@ -275,6 +283,7 @@ public class XSLFTextParagraph implements TextParagraph<XSLFShape,XSLFTextParagr | |||
* | |||
* @param color the bullet color | |||
*/ | |||
@SuppressWarnings("WeakerAccess") | |||
public void setBulletFontColor(PaintStyle color) { | |||
if (!(color instanceof SolidPaint)) { | |||
throw new IllegalArgumentException("Currently XSLF only supports SolidPaint"); | |||
@@ -300,6 +309,7 @@ public class XSLFTextParagraph implements TextParagraph<XSLFShape,XSLFTextParagr | |||
* | |||
* @return the bullet size | |||
*/ | |||
@SuppressWarnings("WeakerAccess") | |||
public Double getBulletFontSize(){ | |||
ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getIndentLevel()){ | |||
public boolean fetch(CTTextParagraphProperties props){ | |||
@@ -326,6 +336,7 @@ public class XSLFTextParagraph implements TextParagraph<XSLFShape,XSLFTextParagr | |||
* If bulletSize < 0, then it specifies the size in points | |||
* </p> | |||
*/ | |||
@SuppressWarnings("WeakerAccess") | |||
public void setBulletFontSize(double bulletSize){ | |||
CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); | |||
@@ -343,6 +354,7 @@ public class XSLFTextParagraph implements TextParagraph<XSLFShape,XSLFTextParagr | |||
/** | |||
* @return the auto numbering scheme, or null if not defined | |||
*/ | |||
@SuppressWarnings("WeakerAccess") | |||
public AutoNumberingScheme getAutoNumberingScheme() { | |||
ParagraphPropertyFetcher<AutoNumberingScheme> fetcher = new ParagraphPropertyFetcher<AutoNumberingScheme>(getIndentLevel()) { | |||
public boolean fetch(CTTextParagraphProperties props) { | |||
@@ -363,6 +375,7 @@ public class XSLFTextParagraph implements TextParagraph<XSLFShape,XSLFTextParagr | |||
/** | |||
* @return the auto numbering starting number, or null if not defined | |||
*/ | |||
@SuppressWarnings("WeakerAccess") | |||
public Integer getAutoNumberingStartAt() { | |||
ParagraphPropertyFetcher<Integer> fetcher = new ParagraphPropertyFetcher<Integer>(getIndentLevel()) { | |||
public boolean fetch(CTTextParagraphProperties props) { | |||
@@ -487,10 +500,11 @@ public class XSLFTextParagraph implements TextParagraph<XSLFShape,XSLFTextParagr | |||
return fetcher.getValue(); | |||
} | |||
public double getTabStop(final int idx){ | |||
@SuppressWarnings("WeakerAccess") | |||
public double getTabStop(final int idx) { | |||
ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getIndentLevel()){ | |||
public boolean fetch(CTTextParagraphProperties props){ | |||
if(props.isSetTabLst()){ | |||
if (props.isSetTabLst()) { | |||
CTTextTabStopList tabStops = props.getTabLst(); | |||
if(idx < tabStops.sizeOfTabArray() ) { | |||
CTTextTabStop ts = tabStops.getTabArray(idx); | |||
@@ -506,6 +520,7 @@ public class XSLFTextParagraph implements TextParagraph<XSLFShape,XSLFTextParagr | |||
return fetcher.getValue() == null ? 0. : fetcher.getValue(); | |||
} | |||
@SuppressWarnings("WeakerAccess") | |||
public void addTabStop(double value){ | |||
CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); | |||
CTTextTabStopList tabStops = pr.isSetTabLst() ? pr.getTabLst() : pr.addNewTabLst(); | |||
@@ -514,45 +529,18 @@ public class XSLFTextParagraph implements TextParagraph<XSLFShape,XSLFTextParagr | |||
@Override | |||
public void setLineSpacing(Double lineSpacing){ | |||
if (lineSpacing == null && !_p.isSetPPr()) return; | |||
CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); | |||
if(lineSpacing == null) { | |||
if (pr.isSetLnSpc()) pr.unsetLnSpc(); | |||
} else { | |||
CTTextSpacing spc = (pr.isSetLnSpc()) ? pr.getLnSpc() : pr.addNewLnSpc(); | |||
if (lineSpacing >= 0) { | |||
(spc.isSetSpcPct() ? spc.getSpcPct() : spc.addNewSpcPct()).setVal((int)(lineSpacing*1000)); | |||
if (spc.isSetSpcPts()) spc.unsetSpcPts(); | |||
} else { | |||
(spc.isSetSpcPts() ? spc.getSpcPts() : spc.addNewSpcPts()).setVal((int)(-lineSpacing*100)); | |||
if (spc.isSetSpcPct()) spc.unsetSpcPct(); | |||
} | |||
} | |||
setSpacing(lineSpacing, props -> props::getLnSpc, props -> props::addNewLnSpc, props -> props::unsetLnSpc); | |||
} | |||
@Override | |||
public Double getLineSpacing(){ | |||
ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getIndentLevel()){ | |||
public boolean fetch(CTTextParagraphProperties props){ | |||
if(props.isSetLnSpc()){ | |||
CTTextSpacing spc = props.getLnSpc(); | |||
if(spc.isSetSpcPct()) setValue( spc.getSpcPct().getVal()*0.001 ); | |||
else if (spc.isSetSpcPts()) setValue( -spc.getSpcPts().getVal()*0.01 ); | |||
return true; | |||
} | |||
return false; | |||
} | |||
}; | |||
fetchParagraphProperty(fetcher); | |||
Double lnSpc = fetcher.getValue(); | |||
final Double lnSpc = getSpacing(props -> props::getLnSpc); | |||
if (lnSpc != null && lnSpc > 0) { | |||
// check if the percentage value is scaled | |||
CTTextNormalAutofit normAutofit = getParentShape().getTextBodyPr().getNormAutofit(); | |||
if(normAutofit != null) { | |||
double scale = 1 - (double)normAutofit.getLnSpcReduction() / 100000; | |||
lnSpc *= scale; | |||
final CTTextNormalAutofit normAutofit = getParentShape().getTextBodyPr().getNormAutofit(); | |||
if (normAutofit != null) { | |||
final double scale = 1 - (double)normAutofit.getLnSpcReduction() / 100000; | |||
return lnSpc * scale; | |||
} | |||
} | |||
@@ -561,84 +549,82 @@ public class XSLFTextParagraph implements TextParagraph<XSLFShape,XSLFTextParagr | |||
@Override | |||
public void setSpaceBefore(Double spaceBefore){ | |||
if (spaceBefore == null && !_p.isSetPPr()) { | |||
return; | |||
} | |||
// unset the space before on null input | |||
if (spaceBefore == null) { | |||
if(_p.getPPr().isSetSpcBef()) { | |||
_p.getPPr().unsetSpcBef(); | |||
} | |||
return; | |||
} | |||
CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); | |||
CTTextSpacing spc = CTTextSpacing.Factory.newInstance(); | |||
if(spaceBefore >= 0) { | |||
spc.addNewSpcPct().setVal((int)(spaceBefore*1000)); | |||
} else { | |||
spc.addNewSpcPts().setVal((int)(-spaceBefore*100)); | |||
} | |||
pr.setSpcBef(spc); | |||
setSpacing(spaceBefore, props -> props::getSpcBef, props -> props::addNewSpcBef, props -> props::unsetSpcBef); | |||
} | |||
@Override | |||
public Double getSpaceBefore(){ | |||
ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getIndentLevel()){ | |||
public boolean fetch(CTTextParagraphProperties props){ | |||
if(props.isSetSpcBef()){ | |||
CTTextSpacing spc = props.getSpcBef(); | |||
if(spc.isSetSpcPct()) setValue( spc.getSpcPct().getVal()*0.001 ); | |||
else if (spc.isSetSpcPts()) setValue( -spc.getSpcPts().getVal()*0.01 ); | |||
return true; | |||
} | |||
return false; | |||
} | |||
}; | |||
fetchParagraphProperty(fetcher); | |||
return fetcher.getValue(); | |||
return getSpacing(props -> props::getSpcBef); | |||
} | |||
@Override | |||
public void setSpaceAfter(Double spaceAfter){ | |||
if (spaceAfter == null && !_p.isSetPPr()) { | |||
setSpacing(spaceAfter, props -> props::getSpcAft, props -> props::addNewSpcAft, props -> props::unsetSpcAft); | |||
} | |||
@Override | |||
public Double getSpaceAfter() { | |||
return getSpacing(props -> props::getSpcAft); | |||
} | |||
private void setSpacing(final Double space, | |||
final Function<CTTextParagraphProperties,Supplier<CTTextSpacing>> getSpc, | |||
final Function<CTTextParagraphProperties,Supplier<CTTextSpacing>> addSpc, | |||
final Function<CTTextParagraphProperties,Procedure> unsetSpc | |||
) { | |||
final CTTextParagraphProperties pPr = (space == null || _p.isSetPPr()) ? _p.getPPr() : _p.addNewPPr(); | |||
if (pPr == null) { | |||
return; | |||
} | |||
// unset the space before on null input | |||
if (spaceAfter == null) { | |||
if(_p.getPPr().isSetSpcAft()) { | |||
_p.getPPr().unsetSpcAft(); | |||
CTTextSpacing spc = getSpc.apply(pPr).get(); | |||
if (space == null) { | |||
if (spc != null) { | |||
// unset the space before on null input | |||
unsetSpc.apply(pPr).accept(); | |||
} | |||
return; | |||
} | |||
CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); | |||
CTTextSpacing spc = CTTextSpacing.Factory.newInstance(); | |||
if (spc == null) { | |||
spc = addSpc.apply(pPr).get(); | |||
} | |||
if(spaceAfter >= 0) { | |||
spc.addNewSpcPct().setVal((int)(spaceAfter*1000)); | |||
if (space >= 0) { | |||
if (spc.isSetSpcPts()) { | |||
spc.unsetSpcPts(); | |||
} | |||
final CTTextSpacingPercent pct = spc.isSetSpcPct() ? spc.getSpcPct() : spc.addNewSpcPct(); | |||
pct.setVal((int)(space*1000)); | |||
} else { | |||
spc.addNewSpcPts().setVal((int)(-spaceAfter*100)); | |||
if (spc.isSetSpcPct()) { | |||
spc.unsetSpcPct(); | |||
} | |||
final CTTextSpacingPoint pts = spc.isSetSpcPts() ? spc.getSpcPts() : spc.addNewSpcPts(); | |||
pts.setVal((int)(-space*100)); | |||
} | |||
pr.setSpcAft(spc); | |||
} | |||
@Override | |||
public Double getSpaceAfter(){ | |||
ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getIndentLevel()){ | |||
public boolean fetch(CTTextParagraphProperties props){ | |||
if(props.isSetSpcAft()){ | |||
CTTextSpacing spc = props.getSpcAft(); | |||
private Double getSpacing(final Function<CTTextParagraphProperties,Supplier<CTTextSpacing>> getSpc) { | |||
final ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getIndentLevel()){ | |||
public boolean fetch(final CTTextParagraphProperties props){ | |||
final CTTextSpacing spc = getSpc.apply(props).get(); | |||
if (spc == null) { | |||
return false; | |||
} | |||
if (spc.isSetSpcPct()) { | |||
setValue( spc.getSpcPct().getVal()*0.001 ); | |||
return true; | |||
} | |||
if(spc.isSetSpcPct()) setValue( spc.getSpcPct().getVal()*0.001 ); | |||
else if (spc.isSetSpcPts()) setValue( -spc.getSpcPts().getVal()*0.01 ); | |||
if (spc.isSetSpcPts()) { | |||
setValue( -spc.getSpcPts().getVal()*0.01 ); | |||
return true; | |||
} | |||
return false; | |||
} | |||
}; | |||
@@ -713,6 +699,7 @@ public class XSLFTextParagraph implements TextParagraph<XSLFShape,XSLFTextParagr | |||
* @param startAt the number that will start number for a given sequence of automatically | |||
numbered bullets (1-based). | |||
*/ | |||
@SuppressWarnings("WeakerAccess") | |||
public void setBulletAutoNumber(AutoNumberingScheme scheme, int startAt) { | |||
if(startAt < 1) throw new IllegalArgumentException("Start Number must be greater or equal that 1") ; | |||
CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); | |||
@@ -732,7 +719,7 @@ public class XSLFTextParagraph implements TextParagraph<XSLFShape,XSLFTextParagr | |||
* there are no master slides or the master slides do not contain a text paragraph | |||
*/ | |||
/* package */ CTTextParagraphProperties getDefaultMasterStyle(){ | |||
CTPlaceholder ph = _shape.getCTPlaceholder(); | |||
CTPlaceholder ph = _shape.getPlaceholderDetails().getCTPlaceholder(false); | |||
String defaultStyleSelector; | |||
switch(ph == null ? -1 : ph.getType().intValue()) { | |||
case STPlaceholderType.INT_TITLE: | |||
@@ -780,37 +767,46 @@ public class XSLFTextParagraph implements TextParagraph<XSLFShape,XSLFTextParagr | |||
return null; | |||
} | |||
private <T> boolean fetchParagraphProperty(ParagraphPropertyFetcher<T> visitor){ | |||
boolean ok = false; | |||
private void fetchParagraphProperty(final ParagraphPropertyFetcher<?> visitor){ | |||
final XSLFTextShape shape = getParentShape(); | |||
final XSLFSheet sheet = shape.getSheet(); | |||
if (!(sheet instanceof XSLFSlideMaster)) { | |||
if(_p.isSetPPr()) ok = visitor.fetch(_p.getPPr()); | |||
if (ok) return true; | |||
if (_p.isSetPPr() && visitor.fetch(_p.getPPr())) { | |||
return; | |||
} | |||
ok = shape.fetchShapeProperty(visitor); | |||
if (ok) return true; | |||
CTPlaceholder ph = shape.getCTPlaceholder(); | |||
if(ph == null){ | |||
// if it is a plain text box then take defaults from presentation.xml | |||
@SuppressWarnings("resource") | |||
XMLSlideShow ppt = sheet.getSlideShow(); | |||
CTTextParagraphProperties themeProps = ppt.getDefaultParagraphStyle(getIndentLevel()); | |||
if (themeProps != null) ok = visitor.fetch(themeProps); | |||
if (shape.fetchShapeProperty(visitor)) { | |||
return; | |||
} | |||
if (fetchThemeProperty(visitor)) { | |||
return; | |||
} | |||
if (ok) return true; | |||
} | |||
fetchMasterProperty(visitor); | |||
} | |||
boolean fetchMasterProperty(final ParagraphPropertyFetcher<?> visitor) { | |||
// defaults for placeholders are defined in the slide master | |||
CTTextParagraphProperties defaultProps = getDefaultMasterStyle(); | |||
final CTTextParagraphProperties defaultProps = getDefaultMasterStyle(); | |||
// TODO: determine master shape | |||
if(defaultProps != null) ok = visitor.fetch(defaultProps); | |||
if (ok) return true; | |||
return defaultProps != null && visitor.fetch(defaultProps); | |||
} | |||
boolean fetchThemeProperty(final ParagraphPropertyFetcher<?> visitor) { | |||
final XSLFTextShape shape = getParentShape(); | |||
if (shape.isPlaceholder()) { | |||
return false; | |||
} | |||
return false; | |||
// if it is a plain text box then take defaults from presentation.xml | |||
@SuppressWarnings("resource") | |||
final XMLSlideShow ppt = shape.getSheet().getSlideShow(); | |||
final CTTextParagraphProperties themeProps = ppt.getDefaultParagraphStyle(getIndentLevel()); | |||
return themeProps != null && visitor.fetch(themeProps); | |||
} | |||
void copy(XSLFTextParagraph other){ | |||
@@ -873,40 +869,40 @@ public class XSLFTextParagraph implements TextParagraph<XSLFShape,XSLFTextParagr | |||
setBulletFontColor(buColor); | |||
} | |||
Double buSize = other.getBulletFontSize(); | |||
if(!doubleEquals(buSize, getBulletFontSize())){ | |||
if(doubleNotEquals(buSize, getBulletFontSize())){ | |||
setBulletFontSize(buSize); | |||
} | |||
} | |||
} | |||
Double leftMargin = other.getLeftMargin(); | |||
if (!doubleEquals(leftMargin, getLeftMargin())){ | |||
if (doubleNotEquals(leftMargin, getLeftMargin())){ | |||
setLeftMargin(leftMargin); | |||
} | |||
Double indent = other.getIndent(); | |||
if (!doubleEquals(indent, getIndent())) { | |||
if (doubleNotEquals(indent, getIndent())) { | |||
setIndent(indent); | |||
} | |||
Double spaceAfter = other.getSpaceAfter(); | |||
if (!doubleEquals(spaceAfter, getSpaceAfter())) { | |||
if (doubleNotEquals(spaceAfter, getSpaceAfter())) { | |||
setSpaceAfter(spaceAfter); | |||
} | |||
Double spaceBefore = other.getSpaceBefore(); | |||
if (!doubleEquals(spaceBefore, getSpaceBefore())) { | |||
if (doubleNotEquals(spaceBefore, getSpaceBefore())) { | |||
setSpaceBefore(spaceBefore); | |||
} | |||
Double lineSpacing = other.getLineSpacing(); | |||
if (!doubleEquals(lineSpacing, getLineSpacing())) { | |||
if (doubleNotEquals(lineSpacing, getLineSpacing())) { | |||
setLineSpacing(lineSpacing); | |||
} | |||
} | |||
private static boolean doubleEquals(Double d1, Double d2) { | |||
return (d1 == d2 || (d1 != null && d1.equals(d2))); | |||
private static boolean doubleNotEquals(Double d1, Double d2) { | |||
return !Objects.equals(d1, d2); | |||
} | |||
@Override | |||
@@ -1072,7 +1068,7 @@ public class XSLFTextParagraph implements TextParagraph<XSLFShape,XSLFTextParagr | |||
@Override | |||
public boolean isHeaderOrFooter() { | |||
CTPlaceholder ph = _shape.getCTPlaceholder(); | |||
CTPlaceholder ph = _shape.getPlaceholderDetails().getCTPlaceholder(false); | |||
int phId = (ph == null ? -1 : ph.getType().intValue()); | |||
switch (phId) { | |||
case STPlaceholderType.INT_SLD_NUM: |
@@ -17,7 +17,6 @@ | |||
package org.apache.poi.xslf.usermodel; | |||
import java.awt.Color; | |||
import java.util.Locale; | |||
import org.apache.poi.common.usermodel.fonts.FontCharset; | |||
import org.apache.poi.common.usermodel.fonts.FontFamily; | |||
@@ -31,7 +30,6 @@ import org.apache.poi.sl.usermodel.PaintStyle; | |||
import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint; | |||
import org.apache.poi.sl.usermodel.TextRun; | |||
import org.apache.poi.util.Beta; | |||
import org.apache.poi.util.LocaleUtil; | |||
import org.apache.poi.xslf.model.CharacterPropertyFetcher; | |||
import org.apache.poi.xslf.usermodel.XSLFPropertiesDelegate.XSLFFillProperties; | |||
import org.apache.xmlbeans.XmlObject; | |||
@@ -47,10 +45,8 @@ import org.openxmlformats.schemas.drawingml.x2006.main.CTTextField; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextFont; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextLineBreak; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextNormalAutofit; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraphProperties; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.STTextStrikeType; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.STTextUnderlineType; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder; | |||
/** | |||
* Represents a run of text within the containing text body. The run element is the | |||
@@ -83,36 +79,6 @@ public class XSLFTextRun implements TextRun { | |||
return ((CTRegularTextRun)_r).getT(); | |||
} | |||
String getRenderableText(){ | |||
if (_r instanceof CTTextField) { | |||
CTTextField tf = (CTTextField)_r; | |||
XSLFSheet sheet = _p.getParentShape().getSheet(); | |||
if ("slidenum".equals(tf.getType()) && sheet instanceof XSLFSlide) { | |||
return Integer.toString(((XSLFSlide)sheet).getSlideNumber()); | |||
} | |||
return tf.getT(); | |||
} else if (_r instanceof CTTextLineBreak) { | |||
return "\n"; | |||
} | |||
return getRenderableText(((CTRegularTextRun)_r).getT()); | |||
} | |||
/* package */ String getRenderableText(final String txt){ | |||
// TODO: finish support for tabs | |||
final String txtSpace = txt.replace("\t", " "); | |||
final Locale loc = LocaleUtil.getUserLocale(); | |||
switch (getTextCap()) { | |||
case ALL: | |||
return txtSpace.toUpperCase(loc); | |||
case SMALL: | |||
return txtSpace.toLowerCase(loc); | |||
default: | |||
return txtSpace; | |||
} | |||
} | |||
@Override | |||
public void setText(String text){ | |||
if (_r instanceof CTTextField) { | |||
@@ -230,6 +196,7 @@ public class XSLFTextRun implements TextRun { | |||
* @return the spacing between characters within a text run, | |||
* If this attribute is omitted than a value of 0 or no adjustment is assumed. | |||
*/ | |||
@SuppressWarnings("WeakerAccess") | |||
public double getCharacterSpacing(){ | |||
CharacterPropertyFetcher<Double> fetcher = new CharacterPropertyFetcher<Double>(_p.getIndentLevel()){ | |||
@@ -255,6 +222,7 @@ public class XSLFTextRun implements TextRun { | |||
* | |||
* @param spc character spacing in points. | |||
*/ | |||
@SuppressWarnings("WeakerAccess") | |||
public void setCharacterSpacing(double spc){ | |||
CTTextCharacterProperties rPr = getRPr(true); | |||
if(spc == 0.0) { | |||
@@ -357,9 +325,8 @@ public class XSLFTextRun implements TextRun { | |||
* The size is specified using a percentage. | |||
* Positive values indicate superscript, negative values indicate subscript. | |||
* </p> | |||
* | |||
* @param baselineOffset | |||
*/ | |||
@SuppressWarnings("WeakerAccess") | |||
public void setBaselineOffset(double baselineOffset){ | |||
getRPr(true).setBaseline((int) baselineOffset * 1000); | |||
} | |||
@@ -370,6 +337,7 @@ public class XSLFTextRun implements TextRun { | |||
* | |||
* @see #setBaselineOffset(double) | |||
*/ | |||
@SuppressWarnings("WeakerAccess") | |||
public void setSuperscript(boolean flag){ | |||
setBaselineOffset(flag ? 30. : 0.); | |||
} | |||
@@ -380,6 +348,7 @@ public class XSLFTextRun implements TextRun { | |||
* | |||
* @see #setBaselineOffset(double) | |||
*/ | |||
@SuppressWarnings("WeakerAccess") | |||
public void setSubscript(boolean flag){ | |||
setBaselineOffset(flag ? -25.0 : 0.); | |||
} | |||
@@ -544,38 +513,23 @@ public class XSLFTextRun implements TextRun { | |||
return new XSLFHyperlink(hl, _p.getParentShape().getSheet()); | |||
} | |||
private boolean fetchCharacterProperty(CharacterPropertyFetcher<?> fetcher){ | |||
private void fetchCharacterProperty(final CharacterPropertyFetcher<?> visitor){ | |||
XSLFTextShape shape = _p.getParentShape(); | |||
XSLFSheet sheet = shape.getSheet(); | |||
CTTextCharacterProperties rPr = getRPr(false); | |||
if (rPr != null && fetcher.fetch(rPr)) { | |||
return true; | |||
} | |||
if (shape.fetchShapeProperty(fetcher)) { | |||
return true; | |||
if (rPr != null && visitor.fetch(rPr)) { | |||
return; | |||
} | |||
CTPlaceholder ph = shape.getCTPlaceholder(); | |||
if (ph == null){ | |||
// if it is a plain text box then take defaults from presentation.xml | |||
@SuppressWarnings("resource") | |||
XMLSlideShow ppt = sheet.getSlideShow(); | |||
// TODO: determine master shape | |||
CTTextParagraphProperties themeProps = ppt.getDefaultParagraphStyle(_p.getIndentLevel()); | |||
if (themeProps != null && fetcher.fetch(themeProps)) { | |||
return true; | |||
} | |||
if (shape.fetchShapeProperty(visitor)) { | |||
return; | |||
} | |||
// TODO: determine master shape | |||
CTTextParagraphProperties defaultProps = _p.getDefaultMasterStyle(); | |||
if(defaultProps != null && fetcher.fetch(defaultProps)) { | |||
return true; | |||
if (_p.fetchThemeProperty(visitor)) { | |||
return; | |||
} | |||
return false; | |||
_p.fetchMasterProperty(visitor); | |||
} | |||
void copy(XSLFTextRun r){ | |||
@@ -630,14 +584,14 @@ public class XSLFTextRun implements TextRun { | |||
} | |||
private class XSLFFontInfo implements FontInfo { | |||
private final class XSLFFontInfo implements FontInfo { | |||
private final FontGroup fontGroup; | |||
private XSLFFontInfo(FontGroup fontGroup) { | |||
this.fontGroup = (fontGroup != null) ? fontGroup : FontGroup.getFontGroupFirst(getRawText()); | |||
} | |||
public void copyFrom(FontInfo fontInfo) { | |||
void copyFrom(FontInfo fontInfo) { | |||
CTTextFont tf = getXmlObject(true); | |||
setTypeface(fontInfo.getTypeface()); | |||
setCharset(fontInfo.getCharset()); |
@@ -590,13 +590,7 @@ public abstract class XSLFTextShape extends XSLFSimpleShape | |||
} | |||
public Placeholder getTextType(){ | |||
CTPlaceholder ph = getCTPlaceholder(); | |||
if (ph == null) { | |||
return null; | |||
} | |||
int val = ph.getType().intValue(); | |||
return Placeholder.lookupOoxml(val); | |||
return getPlaceholder(); | |||
} | |||
@Override |
@@ -19,6 +19,7 @@ package org.apache.poi.xslf.usermodel; | |||
import static org.junit.Assert.assertEquals; | |||
import static org.junit.Assert.assertFalse; | |||
import static org.junit.Assert.assertNotNull; | |||
import static org.junit.Assert.assertNull; | |||
import static org.junit.Assert.assertTrue; | |||
import static org.junit.Assert.fail; | |||
@@ -51,7 +52,7 @@ public class TestXMLSlideShow extends BaseTestSlideShow { | |||
} | |||
@After | |||
public void tearDown() throws IOException { | |||
public void tearDown() { | |||
pack.revert(); | |||
} | |||
@@ -149,7 +150,7 @@ public class TestXMLSlideShow extends BaseTestSlideShow { | |||
assertEquals(null, xml.getCommentAuthors()); | |||
for (XSLFSlide slide : xml.getSlides()) { | |||
assertEquals(null, slide.getComments()); | |||
assertTrue(slide.getComments().isEmpty()); | |||
} | |||
// Try another with comments | |||
@@ -166,17 +167,18 @@ public class TestXMLSlideShow extends BaseTestSlideShow { | |||
i++; | |||
if(i == 0) { | |||
assertNotNull(slide.getComments()); | |||
assertEquals(1, slide.getComments().getNumberOfComments()); | |||
assertEquals("testdoc", slide.getComments().getCommentAt(0).getText()); | |||
assertEquals(0, slide.getComments().getCommentAt(0).getAuthorId()); | |||
assertNotNull(slide.getCommentsPart()); | |||
assertEquals(1, slide.getCommentsPart().getNumberOfComments()); | |||
assertEquals("testdoc", slide.getCommentsPart().getCommentAt(0).getText()); | |||
assertEquals(0, slide.getCommentsPart().getCommentAt(0).getAuthorId()); | |||
} else if (i == 1) { | |||
assertNotNull(slide.getComments()); | |||
assertEquals(1, slide.getComments().getNumberOfComments()); | |||
assertEquals("test phrase", slide.getComments().getCommentAt(0).getText()); | |||
assertEquals(0, slide.getComments().getCommentAt(0).getAuthorId()); | |||
assertEquals(1, slide.getCommentsPart().getNumberOfComments()); | |||
assertEquals("test phrase", slide.getCommentsPart().getCommentAt(0).getText()); | |||
assertEquals(0, slide.getCommentsPart().getCommentAt(0).getAuthorId()); | |||
} else { | |||
assertEquals(null, slide.getComments()); | |||
assertNull(slide.getCommentsPart()); | |||
assertTrue(slide.getComments().isEmpty()); | |||
} | |||
} | |||
@@ -188,7 +190,7 @@ public class TestXMLSlideShow extends BaseTestSlideShow { | |||
return reopen((XMLSlideShow)show); | |||
} | |||
public static XMLSlideShow reopen(XMLSlideShow show) { | |||
private static XMLSlideShow reopen(XMLSlideShow show) { | |||
try { | |||
BufAccessBAOS bos = new BufAccessBAOS(); | |||
show.write(bos); | |||
@@ -200,7 +202,7 @@ public class TestXMLSlideShow extends BaseTestSlideShow { | |||
} | |||
private static class BufAccessBAOS extends ByteArrayOutputStream { | |||
public byte[] getBuf() { | |||
byte[] getBuf() { | |||
return buf; | |||
} | |||
} |
@@ -28,6 +28,7 @@ import static org.mockito.Mockito.when; | |||
import java.awt.Color; | |||
import java.io.IOException; | |||
import org.apache.poi.sl.draw.DrawTextParagraph; | |||
import org.junit.Test; | |||
/** | |||
@@ -65,17 +66,17 @@ public class TestXSLFTextRun { | |||
r.setFontSize(13.0); | |||
assertEquals(13.0, r.getFontSize(), 0); | |||
assertEquals(false, r.isSuperscript()); | |||
assertFalse(r.isSuperscript()); | |||
r.setSuperscript(true); | |||
assertEquals(true, r.isSuperscript()); | |||
assertTrue(r.isSuperscript()); | |||
r.setSuperscript(false); | |||
assertEquals(false, r.isSuperscript()); | |||
assertFalse(r.isSuperscript()); | |||
assertEquals(false, r.isSubscript()); | |||
assertFalse(r.isSubscript()); | |||
r.setSubscript(true); | |||
assertEquals(true, r.isSubscript()); | |||
assertTrue(r.isSubscript()); | |||
r.setSubscript(false); | |||
assertEquals(false, r.isSubscript()); | |||
assertFalse(r.isSubscript()); | |||
ppt.close(); | |||
} | |||
@@ -94,8 +95,11 @@ public class TestXSLFTextRun { | |||
try (XMLSlideShow ppt = new XMLSlideShow()) { | |||
XSLFSlide slide = ppt.createSlide(); | |||
XSLFTextShape sh = slide.createAutoShape(); | |||
XSLFTextRun r = sh.addNewTextParagraph().addNewTextRun(); | |||
assertEquals(unicodeSurrogates, r.getRenderableText(unicodeSurrogates)); | |||
XSLFTextParagraph p = sh.addNewTextParagraph(); | |||
XSLFTextRun r = p.addNewTextRun(); | |||
r.setText(unicodeSurrogates); | |||
assertEquals(unicodeSurrogates, new DrawTextParagraph(p).getRenderableText(r)); | |||
} | |||
} | |||
@@ -83,7 +83,7 @@ public class TestXSLFTextShape { | |||
assertEquals("Title Slide",layout.getName()); | |||
XSLFTextShape shape1 = (XSLFTextShape)shapes.get(0); | |||
CTPlaceholder ph1 = shape1.getCTPlaceholder(); | |||
CTPlaceholder ph1 = shape1.getPlaceholderDetails().getCTPlaceholder(false); | |||
assertEquals(STPlaceholderType.CTR_TITLE, ph1.getType()); | |||
// anchor is not defined in the shape | |||
assertNull(getSpPr(shape1).getXfrm()); | |||
@@ -113,7 +113,7 @@ public class TestXSLFTextShape { | |||
assertTrue(sameColor(Color.black, r1.getFontColor())); | |||
XSLFTextShape shape2 = (XSLFTextShape)shapes.get(1); | |||
CTPlaceholder ph2 = shape2.getCTPlaceholder(); | |||
CTPlaceholder ph2 = shape2.getPlaceholderDetails().getCTPlaceholder(false); | |||
assertEquals(STPlaceholderType.SUB_TITLE, ph2.getType()); | |||
// anchor is not defined in the shape | |||
assertNull(getSpPr(shape2).getXfrm()); | |||
@@ -149,7 +149,7 @@ public class TestXSLFTextShape { | |||
assertEquals("Title and Content",layout.getName()); | |||
XSLFTextShape shape1 = (XSLFTextShape)shapes.get(0); | |||
CTPlaceholder ph1 = shape1.getCTPlaceholder(); | |||
CTPlaceholder ph1 = shape1.getPlaceholderDetails().getCTPlaceholder(false); | |||
assertEquals(STPlaceholderType.TITLE, ph1.getType()); | |||
// anchor is not defined in the shape | |||
assertNull(getSpPr(shape1).getXfrm()); | |||
@@ -182,7 +182,7 @@ public class TestXSLFTextShape { | |||
assertTrue(sameColor(Color.black, r1.getFontColor())); | |||
XSLFTextShape shape2 = (XSLFTextShape)shapes.get(1); | |||
CTPlaceholder ph2 = shape2.getCTPlaceholder(); | |||
CTPlaceholder ph2 = shape2.getPlaceholderDetails().getCTPlaceholder(false); | |||
assertFalse(ph2.isSetType()); // <p:ph idx="1"/> | |||
assertTrue(ph2.isSetIdx()); | |||
assertEquals(1, ph2.getIdx()); | |||
@@ -262,7 +262,7 @@ public class TestXSLFTextShape { | |||
assertEquals("Section Header",layout.getName()); | |||
XSLFTextShape shape1 = (XSLFTextShape)shapes.get(0); | |||
CTPlaceholder ph1 = shape1.getCTPlaceholder(); | |||
CTPlaceholder ph1 = shape1.getPlaceholderDetails().getCTPlaceholder(false); | |||
assertEquals(STPlaceholderType.TITLE, ph1.getType()); | |||
// anchor is not defined in the shape | |||
assertNull(getSpPr(shape1).getXfrm()); | |||
@@ -296,7 +296,7 @@ public class TestXSLFTextShape { | |||
assertFalse(r1.isUnderlined()); | |||
XSLFTextShape shape2 = (XSLFTextShape)shapes.get(1); | |||
CTPlaceholder ph2 = shape2.getCTPlaceholder(); | |||
CTPlaceholder ph2 = shape2.getPlaceholderDetails().getCTPlaceholder(false); | |||
assertEquals(STPlaceholderType.BODY, ph2.getType()); | |||
// anchor is not defined in the shape | |||
assertNull(getSpPr(shape2).getXfrm()); | |||
@@ -333,7 +333,7 @@ public class TestXSLFTextShape { | |||
assertEquals("Two Content",layout.getName()); | |||
XSLFTextShape shape1 = (XSLFTextShape)shapes.get(0); | |||
CTPlaceholder ph1 = shape1.getCTPlaceholder(); | |||
CTPlaceholder ph1 = shape1.getPlaceholderDetails().getCTPlaceholder(false); | |||
assertEquals(STPlaceholderType.TITLE, ph1.getType()); | |||
// anchor is not defined in the shape | |||
assertNull(getSpPr(shape1).getXfrm()); | |||
@@ -367,7 +367,7 @@ public class TestXSLFTextShape { | |||
assertTrue(sameColor(Color.black, r1.getFontColor())); | |||
XSLFTextShape shape2 = (XSLFTextShape)shapes.get(1); | |||
CTPlaceholder ph2 = shape2.getCTPlaceholder(); | |||
CTPlaceholder ph2 = shape2.getPlaceholderDetails().getCTPlaceholder(false); | |||
assertFalse(ph2.isSetType()); | |||
assertTrue(ph2.isSetIdx()); | |||
assertEquals(1, ph2.getIdx()); //<p:ph sz="half" idx="1"/> | |||
@@ -448,7 +448,7 @@ public class TestXSLFTextShape { | |||
assertEquals("Blank",layout.getName()); | |||
XSLFTextShape shape1 = (XSLFTextShape)shapes.get(0); | |||
CTPlaceholder ph1 = shape1.getCTPlaceholder(); | |||
CTPlaceholder ph1 = shape1.getPlaceholderDetails().getCTPlaceholder(false); | |||
assertEquals(STPlaceholderType.TITLE, ph1.getType()); | |||
// anchor is not defined in the shape | |||
assertNull(getSpPr(shape1).getXfrm()); | |||
@@ -516,7 +516,7 @@ public class TestXSLFTextShape { | |||
assertEquals("Content with Caption",layout.getName()); | |||
XSLFTextShape shape1 = (XSLFTextShape)shapes.get(0); | |||
CTPlaceholder ph1 = shape1.getCTPlaceholder(); | |||
CTPlaceholder ph1 = shape1.getPlaceholderDetails().getCTPlaceholder(false); | |||
assertEquals(STPlaceholderType.TITLE, ph1.getType()); | |||
// anchor is not defined in the shape | |||
assertNull(getSpPr(shape1).getXfrm()); | |||
@@ -549,7 +549,7 @@ public class TestXSLFTextShape { | |||
assertTrue(r1.isBold()); | |||
XSLFTextShape shape2 = (XSLFTextShape)shapes.get(1); | |||
CTPlaceholder ph2 = shape2.getCTPlaceholder(); | |||
CTPlaceholder ph2 = shape2.getPlaceholderDetails().getCTPlaceholder(false); | |||
assertFalse(ph2.isSetType()); | |||
assertTrue(ph2.isSetIdx()); | |||
assertEquals(1, ph2.getIdx()); |
@@ -20,47 +20,36 @@ package org.apache.poi.hslf.extractor; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.util.ArrayList; | |||
import java.util.HashSet; | |||
import java.util.List; | |||
import java.util.Set; | |||
import org.apache.poi.POIOLE2TextExtractor; | |||
import org.apache.poi.hslf.model.Comment; | |||
import org.apache.poi.hslf.model.HSLFMetroShape; | |||
import org.apache.poi.hslf.model.HeadersFooters; | |||
import org.apache.poi.hslf.usermodel.HSLFMasterSheet; | |||
import org.apache.poi.hslf.usermodel.HSLFNotes; | |||
import org.apache.poi.hslf.usermodel.HSLFObjectShape; | |||
import org.apache.poi.hslf.usermodel.HSLFShape; | |||
import org.apache.poi.hslf.usermodel.HSLFSlide; | |||
import org.apache.poi.hslf.usermodel.HSLFSlideMaster; | |||
import org.apache.poi.hslf.usermodel.HSLFSlideShow; | |||
import org.apache.poi.hslf.usermodel.HSLFSlideShowImpl; | |||
import org.apache.poi.hslf.usermodel.HSLFTable; | |||
import org.apache.poi.hslf.usermodel.HSLFTableCell; | |||
import org.apache.poi.hslf.usermodel.HSLFTextParagraph; | |||
import org.apache.poi.hslf.usermodel.HSLFTextShape; | |||
import org.apache.poi.hslf.usermodel.HSLFObjectShape; | |||
import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey; | |||
import org.apache.poi.poifs.filesystem.DirectoryNode; | |||
import org.apache.poi.poifs.filesystem.NPOIFSFileSystem; | |||
import org.apache.poi.poifs.filesystem.POIFSFileSystem; | |||
import org.apache.poi.util.POILogFactory; | |||
import org.apache.poi.util.POILogger; | |||
import org.apache.poi.sl.extractor.SlideShowExtractor; | |||
import org.apache.poi.sl.usermodel.SlideShowFactory; | |||
/** | |||
* This class can be used to extract text from a PowerPoint file. Can optionally | |||
* also get the notes from one. | |||
* | |||
* @deprecated in POI 4.0.0, use {@link SlideShowExtractor} instead | |||
*/ | |||
@SuppressWarnings("WeakerAccess") | |||
@Deprecated | |||
public final class PowerPointExtractor extends POIOLE2TextExtractor { | |||
private static final POILogger LOG = POILogFactory.getLogger(PowerPointExtractor.class); | |||
private final HSLFSlideShow _show; | |||
private final List<HSLFSlide> _slides; | |||
private final SlideShowExtractor<HSLFShape,HSLFTextParagraph> delegate; | |||
private boolean _slidesByDefault = true; | |||
private boolean _notesByDefault; | |||
private boolean _commentsByDefault; | |||
private boolean _masterByDefault; | |||
private boolean slidesByDefault = true; | |||
private boolean notesByDefault; | |||
private boolean commentsByDefault; | |||
private boolean masterByDefault; | |||
/** | |||
* Basic extractor. Returns all the text, and optionally all the notes | |||
@@ -92,13 +81,19 @@ public final class PowerPointExtractor extends POIOLE2TextExtractor { | |||
ppe.close(); | |||
} | |||
public PowerPointExtractor(final HSLFSlideShow slideShow) { | |||
super(slideShow.getSlideShowImpl()); | |||
setFilesystem(slideShow); | |||
delegate = new SlideShowExtractor<>(slideShow); | |||
} | |||
/** | |||
* Creates a PowerPointExtractor, from a file | |||
* | |||
* @param fileName The name of the file to extract from | |||
*/ | |||
public PowerPointExtractor(String fileName) throws IOException { | |||
this(new NPOIFSFileSystem(new File(fileName))); | |||
this((HSLFSlideShow)SlideShowFactory.create(new File(fileName), Biff8EncryptionKey.getCurrentUserPassword(), true)); | |||
} | |||
/** | |||
@@ -107,7 +102,7 @@ public final class PowerPointExtractor extends POIOLE2TextExtractor { | |||
* @param iStream The input stream containing the PowerPoint document | |||
*/ | |||
public PowerPointExtractor(InputStream iStream) throws IOException { | |||
this(new POIFSFileSystem(iStream)); | |||
this((HSLFSlideShow)SlideShowFactory.create(iStream, Biff8EncryptionKey.getCurrentUserPassword())); | |||
} | |||
/** | |||
@@ -116,7 +111,7 @@ public final class PowerPointExtractor extends POIOLE2TextExtractor { | |||
* @param fs the POIFSFileSystem containing the PowerPoint document | |||
*/ | |||
public PowerPointExtractor(POIFSFileSystem fs) throws IOException { | |||
this(fs.getRoot()); | |||
this((HSLFSlideShow)SlideShowFactory.create(fs, Biff8EncryptionKey.getCurrentUserPassword())); | |||
} | |||
/** | |||
@@ -125,8 +120,7 @@ public final class PowerPointExtractor extends POIOLE2TextExtractor { | |||
* @param fs the NPOIFSFileSystem containing the PowerPoint document | |||
*/ | |||
public PowerPointExtractor(NPOIFSFileSystem fs) throws IOException { | |||
this(fs.getRoot()); | |||
setFilesystem(fs); | |||
this((HSLFSlideShow)SlideShowFactory.create(fs, Biff8EncryptionKey.getCurrentUserPassword())); | |||
} | |||
/** | |||
@@ -136,7 +130,7 @@ public final class PowerPointExtractor extends POIOLE2TextExtractor { | |||
* @param dir the POIFS Directory containing the PowerPoint document | |||
*/ | |||
public PowerPointExtractor(DirectoryNode dir) throws IOException { | |||
this(new HSLFSlideShowImpl(dir)); | |||
this(new HSLFSlideShow(dir)); | |||
} | |||
/** | |||
@@ -145,37 +139,39 @@ public final class PowerPointExtractor extends POIOLE2TextExtractor { | |||
* @param ss the HSLFSlideShow to extract text from | |||
*/ | |||
public PowerPointExtractor(HSLFSlideShowImpl ss) { | |||
super(ss); | |||
_show = new HSLFSlideShow(ss); | |||
_slides = _show.getSlides(); | |||
this(new HSLFSlideShow(ss)); | |||
} | |||
/** | |||
* Should a call to getText() return slide text? Default is yes | |||
*/ | |||
public void setSlidesByDefault(boolean slidesByDefault) { | |||
this._slidesByDefault = slidesByDefault; | |||
public void setSlidesByDefault(final boolean slidesByDefault) { | |||
this.slidesByDefault = slidesByDefault; | |||
delegate.setSlidesByDefault(slidesByDefault); | |||
} | |||
/** | |||
* Should a call to getText() return notes text? Default is no | |||
*/ | |||
public void setNotesByDefault(boolean notesByDefault) { | |||
this._notesByDefault = notesByDefault; | |||
public void setNotesByDefault(final boolean notesByDefault) { | |||
this.notesByDefault = notesByDefault; | |||
delegate.setNotesByDefault(notesByDefault); | |||
} | |||
/** | |||
* Should a call to getText() return comments text? Default is no | |||
*/ | |||
public void setCommentsByDefault(boolean commentsByDefault) { | |||
this._commentsByDefault = commentsByDefault; | |||
public void setCommentsByDefault(final boolean commentsByDefault) { | |||
this.commentsByDefault = commentsByDefault; | |||
delegate.setCommentsByDefault(commentsByDefault); | |||
} | |||
/** | |||
* Should a call to getText() return text from master? Default is no | |||
*/ | |||
public void setMasterByDefault(boolean masterByDefault) { | |||
this._masterByDefault = masterByDefault; | |||
public void setMasterByDefault(final boolean masterByDefault) { | |||
this.masterByDefault = masterByDefault; | |||
delegate.setMasterByDefault(masterByDefault); | |||
} | |||
/** | |||
@@ -184,28 +180,7 @@ public final class PowerPointExtractor extends POIOLE2TextExtractor { | |||
*/ | |||
@Override | |||
public String getText() { | |||
return getText(_slidesByDefault, _notesByDefault, _commentsByDefault, _masterByDefault); | |||
} | |||
/** | |||
* Fetches all the notes text from the slideshow, but not the slide text | |||
*/ | |||
public String getNotes() { | |||
return getText(false, true); | |||
} | |||
public List<HSLFObjectShape> getOLEShapes() { | |||
List<HSLFObjectShape> list = new ArrayList<>(); | |||
for (HSLFSlide slide : _slides) { | |||
for (HSLFShape shape : slide.getShapes()) { | |||
if (shape instanceof HSLFObjectShape) { | |||
list.add((HSLFObjectShape) shape); | |||
} | |||
} | |||
} | |||
return list; | |||
return delegate.getText(); | |||
} | |||
/** | |||
@@ -217,159 +192,33 @@ public final class PowerPointExtractor extends POIOLE2TextExtractor { | |||
* @param getNoteText fetch note text | |||
*/ | |||
public String getText(boolean getSlideText, boolean getNoteText) { | |||
return getText(getSlideText, getNoteText, _commentsByDefault, _masterByDefault); | |||
return getText(getSlideText,getNoteText,commentsByDefault,masterByDefault); | |||
} | |||
public String getText(boolean getSlideText, boolean getNoteText, boolean getCommentText, boolean getMasterText) { | |||
StringBuffer ret = new StringBuffer(); | |||
if (getSlideText) { | |||
if (getMasterText) { | |||
for (HSLFSlideMaster master : _show.getSlideMasters()) { | |||
for(HSLFShape sh : master.getShapes()){ | |||
if(sh instanceof HSLFTextShape){ | |||
HSLFTextShape hsh = (HSLFTextShape)sh; | |||
final String text = hsh.getText(); | |||
if (text == null || text.isEmpty() || "*".equals(text)) { | |||
continue; | |||
} | |||
if (HSLFMasterSheet.isPlaceholder(sh)) { | |||
// check for metro shape of complex placeholder | |||
boolean isMetro = new HSLFMetroShape<HSLFShape>(sh).hasMetroBlob(); | |||
if (!isMetro) { | |||
// don't bother about boiler plate text on master sheets | |||
LOG.log(POILogger.INFO, "Ignoring boiler plate (placeholder) text on slide master:", text); | |||
continue; | |||
} | |||
} | |||
ret.append(text); | |||
if (!text.endsWith("\n")) { | |||
ret.append("\n"); | |||
} | |||
} | |||
} | |||
} | |||
} | |||
for (HSLFSlide slide : _slides) { | |||
String headerText = ""; | |||
String footerText = ""; | |||
HeadersFooters hf = slide.getHeadersFooters(); | |||
if (hf != null) { | |||
if (hf.isHeaderVisible()) { | |||
headerText = safeLine(hf.getHeaderText()); | |||
} | |||
if (hf.isFooterVisible()) { | |||
footerText = safeLine(hf.getFooterText()); | |||
} | |||
} | |||
// Slide header, if set | |||
ret.append(headerText); | |||
// Slide text | |||
textRunsToText(ret, slide.getTextParagraphs()); | |||
// Table text | |||
for (HSLFShape shape : slide.getShapes()){ | |||
if (shape instanceof HSLFTable){ | |||
extractTableText(ret, (HSLFTable)shape); | |||
} | |||
} | |||
// Slide footer, if set | |||
ret.append(footerText); | |||
// Comments, if requested and present | |||
if (getCommentText) { | |||
for (Comment comment : slide.getComments()) { | |||
ret.append(comment.getAuthor() + " - " + comment.getText() + "\n"); | |||
} | |||
} | |||
} | |||
if (getNoteText) { | |||
ret.append('\n'); | |||
} | |||
delegate.setSlidesByDefault(getSlideText); | |||
delegate.setNotesByDefault(getNoteText); | |||
delegate.setCommentsByDefault(getCommentText); | |||
delegate.setMasterByDefault(getMasterText); | |||
try { | |||
return delegate.getText(); | |||
} finally { | |||
delegate.setSlidesByDefault(slidesByDefault); | |||
delegate.setNotesByDefault(notesByDefault); | |||
delegate.setCommentsByDefault(commentsByDefault); | |||
delegate.setMasterByDefault(masterByDefault); | |||
} | |||
if (getNoteText) { | |||
// Not currently using _notes, as that can have the notes of | |||
// master sheets in. Grab Slide list, then work from there, | |||
// but ensure no duplicates | |||
Set<Integer> seenNotes = new HashSet<>(); | |||
String headerText = ""; | |||
String footerText = ""; | |||
HeadersFooters hf = _show.getNotesHeadersFooters(); | |||
if (hf != null) { | |||
if (hf.isHeaderVisible()) { | |||
headerText = safeLine(hf.getHeaderText()); | |||
} | |||
if (hf.isFooterVisible()) { | |||
footerText = safeLine(hf.getFooterText()); | |||
} | |||
} | |||
for (HSLFSlide slide : _slides) { | |||
HSLFNotes notes = slide.getNotes(); | |||
if (notes == null) { | |||
continue; | |||
} | |||
Integer id = Integer.valueOf(notes._getSheetNumber()); | |||
if (seenNotes.contains(id)) { | |||
continue; | |||
} | |||
seenNotes.add(id); | |||
// Repeat the Notes header, if set | |||
ret.append(headerText); | |||
// Notes text | |||
textRunsToText(ret, notes.getTextParagraphs()); | |||
// Repeat the notes footer, if set | |||
ret.append(footerText); | |||
} | |||
} | |||
return ret.toString(); | |||
} | |||
private static String safeLine(String text) { | |||
return (text == null) ? "" : (text+'\n'); | |||
} | |||
private void extractTableText(StringBuffer ret, HSLFTable table) { | |||
final int nrows = table.getNumberOfRows(); | |||
final int ncols = table.getNumberOfColumns(); | |||
for (int row = 0; row < nrows; row++){ | |||
for (int col = 0; col < ncols; col++){ | |||
HSLFTableCell cell = table.getCell(row, col); | |||
//defensive null checks; don't know if they're necessary | |||
if (cell != null){ | |||
String txt = cell.getText(); | |||
txt = (txt == null) ? "" : txt; | |||
ret.append(txt); | |||
if (col < ncols-1){ | |||
ret.append('\t'); | |||
} | |||
} | |||
} | |||
ret.append('\n'); | |||
} | |||
} | |||
private void textRunsToText(StringBuffer ret, List<List<HSLFTextParagraph>> paragraphs) { | |||
if (paragraphs==null) { | |||
return; | |||
} | |||
/** | |||
* Fetches all the notes text from the slideshow, but not the slide text | |||
*/ | |||
public String getNotes() { | |||
return getText(false, true, false, false); | |||
} | |||
for (List<HSLFTextParagraph> lp : paragraphs) { | |||
ret.append(HSLFTextParagraph.getText(lp)); | |||
if (ret.length() > 0 && ret.charAt(ret.length()-1) != '\n') { | |||
ret.append('\n'); | |||
} | |||
} | |||
} | |||
@SuppressWarnings("unchecked") | |||
public List<HSLFObjectShape> getOLEShapes() { | |||
return (List<HSLFObjectShape>)delegate.getOLEShapes(); | |||
} | |||
} |
@@ -1,75 +0,0 @@ | |||
/* ==================================================================== | |||
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.model; | |||
import org.apache.poi.hslf.record.Comment2000; | |||
/** | |||
* | |||
* @author Nick Burch | |||
*/ | |||
public final class Comment { | |||
private Comment2000 _comment2000; | |||
public Comment(Comment2000 comment2000) { | |||
_comment2000 = comment2000; | |||
} | |||
protected Comment2000 getComment2000() { | |||
return _comment2000; | |||
} | |||
/** | |||
* Get the Author of this comment | |||
*/ | |||
public String getAuthor() { | |||
return _comment2000.getAuthor(); | |||
} | |||
/** | |||
* Set the Author of this comment | |||
*/ | |||
public void setAuthor(String author) { | |||
_comment2000.setAuthor(author); | |||
} | |||
/** | |||
* Get the Author's Initials of this comment | |||
*/ | |||
public String getAuthorInitials() { | |||
return _comment2000.getAuthorInitials(); | |||
} | |||
/** | |||
* Set the Author's Initials of this comment | |||
*/ | |||
public void setAuthorInitials(String initials) { | |||
_comment2000.setAuthorInitials(initials); | |||
} | |||
/** | |||
* Get the text of this comment | |||
*/ | |||
public String getText() { | |||
return _comment2000.getText(); | |||
} | |||
/** | |||
* Set the text of this comment | |||
*/ | |||
public void setText(String text) { | |||
_comment2000.setText(text); | |||
} | |||
} |
@@ -275,4 +275,11 @@ public final class HeadersFooters { | |||
private void setFlag(int type, boolean flag) { | |||
_container.getHeadersFootersAtom().setFlag(type, flag); | |||
} | |||
/** | |||
* @return true, if this is a ppt 2007 document and header/footer are stored as placeholder shapes | |||
*/ | |||
public boolean isPpt2007() { | |||
return _ppt2007; | |||
} | |||
} |
@@ -22,6 +22,7 @@ import java.io.IOException; | |||
import java.io.OutputStream; | |||
import java.util.Map; | |||
import org.apache.poi.EncryptedDocumentException; | |||
import org.apache.poi.poifs.crypt.CipherAlgorithm; | |||
import org.apache.poi.poifs.crypt.EncryptionInfo; | |||
import org.apache.poi.poifs.crypt.EncryptionMode; | |||
@@ -46,15 +47,17 @@ public final class DocumentEncryptionAtom extends PositionDependentRecordAtom { | |||
/** | |||
* For the Document Encryption Atom | |||
*/ | |||
protected DocumentEncryptionAtom(byte[] source, int start, int len) throws IOException { | |||
protected DocumentEncryptionAtom(byte[] source, int start, int len) { | |||
// Get the header | |||
_header = new byte[8]; | |||
System.arraycopy(source,start,_header,0,8); | |||
ByteArrayInputStream bis = new ByteArrayInputStream(source, start+8, len-8); | |||
LittleEndianInputStream leis = new LittleEndianInputStream(bis); | |||
ei = new EncryptionInfo(leis, EncryptionMode.cryptoAPI); | |||
leis.close(); | |||
try (LittleEndianInputStream leis = new LittleEndianInputStream(bis)) { | |||
ei = new EncryptionInfo(leis, EncryptionMode.cryptoAPI); | |||
} catch (IOException e) { | |||
throw new EncryptedDocumentException(e); | |||
} | |||
} | |||
public DocumentEncryptionAtom() { |
@@ -20,7 +20,6 @@ package org.apache.poi.hslf.record; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.IOException; | |||
import java.util.ArrayList; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import org.apache.poi.ddf.EscherClientDataRecord; | |||
@@ -49,12 +48,7 @@ public class HSLFEscherClientDataRecord extends EscherClientDataRecord { | |||
} | |||
public void removeChild(Class<? extends Record> childClass) { | |||
Iterator<Record> iter = _childRecords.iterator(); | |||
while (iter.hasNext()) { | |||
if (childClass.isInstance(iter.next())) { | |||
iter.remove(); | |||
} | |||
} | |||
_childRecords.removeIf(childClass::isInstance); | |||
} | |||
public void addChild(Record childRecord) { | |||
@@ -109,8 +103,10 @@ public class HSLFEscherClientDataRecord extends EscherClientDataRecord { | |||
_childRecords.clear(); | |||
int offset = 0; | |||
while (offset < remainingData.length) { | |||
Record r = Record.buildRecordAtOffset(remainingData, offset); | |||
_childRecords.add(r); | |||
final Record r = Record.buildRecordAtOffset(remainingData, offset); | |||
if (r != null) { | |||
_childRecords.add(r); | |||
} | |||
long rlen = LittleEndian.getUInt(remainingData,offset+4); | |||
offset += 8 + rlen; | |||
} |
@@ -24,6 +24,7 @@ import java.util.List; | |||
import org.apache.poi.hslf.exceptions.CorruptPowerPointFileException; | |||
import org.apache.poi.hslf.exceptions.HSLFException; | |||
import org.apache.poi.hslf.record.RecordTypes.RecordConstructor; | |||
import org.apache.poi.util.LittleEndian; | |||
import org.apache.poi.util.POILogFactory; | |||
import org.apache.poi.util.POILogger; | |||
@@ -122,15 +123,13 @@ public abstract class Record | |||
// Abort if first record is of type 0000 and length FFFF, | |||
// as that's a sign of a screwed up record | |||
if(pos == 0 && type == 0l && rleni == 0xffff) { | |||
if(pos == 0 && type == 0L && rleni == 0xffff) { | |||
throw new CorruptPowerPointFileException("Corrupt document - starts with record of type 0000 and length 0xFFFF"); | |||
} | |||
Record r = createRecordForType(type,b,pos,8+rleni); | |||
if(r != null) { | |||
children.add(r); | |||
} else { | |||
// Record was horribly corrupt | |||
} | |||
pos += 8; | |||
pos += rleni; | |||
@@ -150,43 +149,32 @@ public abstract class Record | |||
* passing in corrected lengths | |||
*/ | |||
public static Record createRecordForType(long type, byte[] b, int start, int len) { | |||
Record toReturn = null; | |||
// Handle case of a corrupt last record, whose claimed length | |||
// would take us passed the end of the file | |||
if(start + len > b.length) { | |||
logger.log(POILogger.WARN, "Warning: Skipping record of type " + type + " at position " + start + " which claims to be longer than the file! (" + len + " vs " + (b.length-start) + ")"); | |||
return null; | |||
} | |||
// We use the RecordTypes class to provide us with the right | |||
// class to use for a given type | |||
// A spot of reflection gets us the (byte[],int,int) constructor | |||
// From there, we instanciate the class | |||
// Any special record handling occurs once we have the class | |||
Class<? extends Record> c = null; | |||
RecordConstructor c = RecordTypes.forTypeID((short)type).recordConstructor; | |||
if (c == null) { | |||
// How odd. RecordTypes normally substitutes in | |||
// a default handler class if it has heard of the record | |||
// type but there's no support for it. Explicitly request | |||
// that now | |||
c = RecordTypes.UnknownRecordPlaceholder.recordConstructor; | |||
} | |||
final Record toReturn; | |||
try { | |||
c = RecordTypes.forTypeID((short)type).handlingClass; | |||
if(c == null) { | |||
// How odd. RecordTypes normally substitutes in | |||
// a default handler class if it has heard of the record | |||
// type but there's no support for it. Explicitly request | |||
// that now | |||
c = RecordTypes.UnknownRecordPlaceholder.handlingClass; | |||
toReturn = c.apply(b, start, len); | |||
} catch(RuntimeException e) { | |||
// Handle case of a corrupt last record, whose claimed length | |||
// would take us passed the end of the file | |||
if(start + len > b.length ) { | |||
logger.log(POILogger.WARN, "Warning: Skipping record of type " + type + " at position " + start + " which claims to be longer than the file! (" + len + " vs " + (b.length-start) + ")"); | |||
return null; | |||
} | |||
// Grab the right constructor | |||
java.lang.reflect.Constructor<? extends Record> con = c.getDeclaredConstructor(new Class[] { byte[].class, Integer.TYPE, Integer.TYPE }); | |||
// Instantiate | |||
toReturn = con.newInstance(new Object[] { b, start, len }); | |||
} catch(InstantiationException ie) { | |||
throw new HSLFException("Couldn't instantiate the class for type with id " + type + " on class " + c + " : " + ie, ie); | |||
} catch(java.lang.reflect.InvocationTargetException ite) { | |||
throw new HSLFException("Couldn't instantiate the class for type with id " + type + " on class " + c + " : " + ite + "\nCause was : " + ite.getCause(), ite); | |||
} catch(IllegalAccessException iae) { | |||
throw new HSLFException("Couldn't access the constructor for type with id " + type + " on class " + c + " : " + iae, iae); | |||
} catch(NoSuchMethodException nsme) { | |||
throw new HSLFException("Couldn't access the constructor for type with id " + type + " on class " + c + " : " + nsme, nsme); | |||
throw new HSLFException("Couldn't instantiate the class for type with id " + type + " on class " + c + " : " + e, e); | |||
} | |||
// Handling for special kinds of records follow |
@@ -29,141 +29,141 @@ import java.util.Map; | |||
*/ | |||
public enum RecordTypes { | |||
Unknown(0,null), | |||
UnknownRecordPlaceholder(-1, UnknownRecordPlaceholder.class), | |||
Document(1000,Document.class), | |||
DocumentAtom(1001,DocumentAtom.class), | |||
UnknownRecordPlaceholder(-1, UnknownRecordPlaceholder::new), | |||
Document(1000,Document::new), | |||
DocumentAtom(1001,DocumentAtom::new), | |||
EndDocument(1002,null), | |||
Slide(1006,Slide.class), | |||
SlideAtom(1007,SlideAtom.class), | |||
Notes(1008,Notes.class), | |||
NotesAtom(1009,NotesAtom.class), | |||
Environment(1010,Environment.class), | |||
SlidePersistAtom(1011,SlidePersistAtom.class), | |||
Slide(1006,Slide::new), | |||
SlideAtom(1007,SlideAtom::new), | |||
Notes(1008,Notes::new), | |||
NotesAtom(1009,NotesAtom::new), | |||
Environment(1010,Environment::new), | |||
SlidePersistAtom(1011,SlidePersistAtom::new), | |||
SSlideLayoutAtom(1015,null), | |||
MainMaster(1016,MainMaster.class), | |||
SSSlideInfoAtom(1017,SSSlideInfoAtom.class), | |||
MainMaster(1016,MainMaster::new), | |||
SSSlideInfoAtom(1017,SSSlideInfoAtom::new), | |||
SlideViewInfo(1018,null), | |||
GuideAtom(1019,null), | |||
ViewInfo(1020,null), | |||
ViewInfoAtom(1021,null), | |||
SlideViewInfoAtom(1022,null), | |||
VBAInfo(1023,VBAInfoContainer.class), | |||
VBAInfoAtom(1024,VBAInfoAtom.class), | |||
VBAInfo(1023,VBAInfoContainer::new), | |||
VBAInfoAtom(1024,VBAInfoAtom::new), | |||
SSDocInfoAtom(1025,null), | |||
Summary(1026,null), | |||
DocRoutingSlip(1030,null), | |||
OutlineViewInfo(1031,null), | |||
SorterViewInfo(1032,null), | |||
ExObjList(1033,ExObjList.class), | |||
ExObjListAtom(1034,ExObjListAtom.class), | |||
PPDrawingGroup(1035,PPDrawingGroup.class), | |||
PPDrawing(1036,PPDrawing.class), | |||
ExObjList(1033,ExObjList::new), | |||
ExObjListAtom(1034,ExObjListAtom::new), | |||
PPDrawingGroup(1035,PPDrawingGroup::new), | |||
PPDrawing(1036,PPDrawing::new), | |||
NamedShows(1040,null), | |||
NamedShow(1041,null), | |||
NamedShowSlides(1042,null), | |||
SheetProperties(1044,null), | |||
RoundTripCustomTableStyles12Atom(1064,null), | |||
List(2000,DocInfoListContainer.class), | |||
FontCollection(2005,FontCollection.class), | |||
List(2000,DocInfoListContainer::new), | |||
FontCollection(2005,FontCollection::new), | |||
BookmarkCollection(2019,null), | |||
SoundCollection(2020,SoundCollection.class), | |||
SoundCollection(2020,SoundCollection::new), | |||
SoundCollAtom(2021,null), | |||
Sound(2022,Sound.class), | |||
SoundData(2023,SoundData.class), | |||
Sound(2022,Sound::new), | |||
SoundData(2023,SoundData::new), | |||
BookmarkSeedAtom(2025,null), | |||
ColorSchemeAtom(2032,ColorSchemeAtom.class), | |||
ExObjRefAtom(3009,ExObjRefAtom.class), | |||
OEPlaceholderAtom(3011,OEPlaceholderAtom.class), | |||
ColorSchemeAtom(2032,ColorSchemeAtom::new), | |||
ExObjRefAtom(3009,ExObjRefAtom::new), | |||
OEPlaceholderAtom(3011,OEPlaceholderAtom::new), | |||
GPopublicintAtom(3024,null), | |||
GRatioAtom(3031,null), | |||
OutlineTextRefAtom(3998,OutlineTextRefAtom.class), | |||
TextHeaderAtom(3999,TextHeaderAtom.class), | |||
TextCharsAtom(4000,TextCharsAtom.class), | |||
StyleTextPropAtom(4001, StyleTextPropAtom.class),//0x0fa1 RT_StyleTextPropAtom | |||
MasterTextPropAtom(4002, MasterTextPropAtom.class), | |||
TxMasterStyleAtom(4003,TxMasterStyleAtom.class), | |||
OutlineTextRefAtom(3998,OutlineTextRefAtom::new), | |||
TextHeaderAtom(3999,TextHeaderAtom::new), | |||
TextCharsAtom(4000,TextCharsAtom::new), | |||
StyleTextPropAtom(4001, StyleTextPropAtom::new),//0x0fa1 RT_StyleTextPropAtom | |||
MasterTextPropAtom(4002, MasterTextPropAtom::new), | |||
TxMasterStyleAtom(4003,TxMasterStyleAtom::new), | |||
TxCFStyleAtom(4004,null), | |||
TxPFStyleAtom(4005,null), | |||
TextRulerAtom(4006,TextRulerAtom.class), | |||
TextRulerAtom(4006,TextRulerAtom::new), | |||
TextBookmarkAtom(4007,null), | |||
TextBytesAtom(4008,TextBytesAtom.class), | |||
TextBytesAtom(4008,TextBytesAtom::new), | |||
TxSIStyleAtom(4009,null), | |||
TextSpecInfoAtom(4010, TextSpecInfoAtom.class), | |||
TextSpecInfoAtom(4010, TextSpecInfoAtom::new), | |||
DefaultRulerAtom(4011,null), | |||
StyleTextProp9Atom(4012, StyleTextProp9Atom.class), //0x0FAC RT_StyleTextProp9Atom | |||
FontEntityAtom(4023,FontEntityAtom.class), | |||
StyleTextProp9Atom(4012, StyleTextProp9Atom::new), //0x0FAC RT_StyleTextProp9Atom | |||
FontEntityAtom(4023,FontEntityAtom::new), | |||
FontEmbeddedData(4024,null), | |||
CString(4026,CString.class), | |||
CString(4026,CString::new), | |||
MetaFile(4033,null), | |||
ExOleObjAtom(4035,ExOleObjAtom.class), | |||
ExOleObjAtom(4035,ExOleObjAtom::new), | |||
SrKinsoku(4040,null), | |||
HandOut(4041,DummyPositionSensitiveRecordWithChildren.class), | |||
ExEmbed(4044,ExEmbed.class), | |||
ExEmbedAtom(4045,ExEmbedAtom.class), | |||
HandOut(4041,DummyPositionSensitiveRecordWithChildren::new), | |||
ExEmbed(4044,ExEmbed::new), | |||
ExEmbedAtom(4045,ExEmbedAtom::new), | |||
ExLink(4046,null), | |||
BookmarkEntityAtom(4048,null), | |||
ExLinkAtom(4049,null), | |||
SrKinsokuAtom(4050,null), | |||
ExHyperlinkAtom(4051,ExHyperlinkAtom.class), | |||
ExHyperlink(4055,ExHyperlink.class), | |||
ExHyperlinkAtom(4051,ExHyperlinkAtom::new), | |||
ExHyperlink(4055,ExHyperlink::new), | |||
SlideNumberMCAtom(4056,null), | |||
HeadersFooters(4057,HeadersFootersContainer.class), | |||
HeadersFootersAtom(4058,HeadersFootersAtom.class), | |||
TxInteractiveInfoAtom(4063,TxInteractiveInfoAtom.class), | |||
HeadersFooters(4057,HeadersFootersContainer::new), | |||
HeadersFootersAtom(4058,HeadersFootersAtom::new), | |||
TxInteractiveInfoAtom(4063,TxInteractiveInfoAtom::new), | |||
CharFormatAtom(4066,null), | |||
ParaFormatAtom(4067,null), | |||
RecolorInfoAtom(4071,null), | |||
ExQuickTimeMovie(4074,null), | |||
ExQuickTimeMovieData(4075,null), | |||
ExControl(4078,ExControl.class), | |||
SlideListWithText(4080,SlideListWithText.class), | |||
InteractiveInfo(4082,InteractiveInfo.class), | |||
InteractiveInfoAtom(4083,InteractiveInfoAtom.class), | |||
UserEditAtom(4085,UserEditAtom.class), | |||
ExControl(4078,ExControl::new), | |||
SlideListWithText(4080,SlideListWithText::new), | |||
InteractiveInfo(4082,InteractiveInfo::new), | |||
InteractiveInfoAtom(4083,InteractiveInfoAtom::new), | |||
UserEditAtom(4085,UserEditAtom::new), | |||
CurrentUserAtom(4086,null), | |||
DateTimeMCAtom(4087,null), | |||
GenericDateMCAtom(4088,null), | |||
FooterMCAtom(4090,null), | |||
ExControlAtom(4091,ExControlAtom.class), | |||
ExMediaAtom(4100,ExMediaAtom.class), | |||
ExVideoContainer(4101,ExVideoContainer.class), | |||
ExAviMovie(4102,ExAviMovie.class), | |||
ExMCIMovie(4103,ExMCIMovie.class), | |||
ExControlAtom(4091,ExControlAtom::new), | |||
ExMediaAtom(4100,ExMediaAtom::new), | |||
ExVideoContainer(4101,ExVideoContainer::new), | |||
ExAviMovie(4102,ExAviMovie::new), | |||
ExMCIMovie(4103,ExMCIMovie::new), | |||
ExMIDIAudio(4109,null), | |||
ExCDAudio(4110,null), | |||
ExWAVAudioEmbedded(4111,null), | |||
ExWAVAudioLink(4112,null), | |||
ExOleObjStg(4113,ExOleObjStg.class), | |||
ExOleObjStg(4113,ExOleObjStg::new), | |||
ExCDAudioAtom(4114,null), | |||
ExWAVAudioEmbeddedAtom(4115,null), | |||
AnimationInfo(4116,AnimationInfo.class), | |||
AnimationInfoAtom(4081,AnimationInfoAtom.class), | |||
AnimationInfo(4116,AnimationInfo::new), | |||
AnimationInfoAtom(4081,AnimationInfoAtom::new), | |||
RTFDateTimeMCAtom(4117,null), | |||
ProgTags(5000,DummyPositionSensitiveRecordWithChildren.class), | |||
ProgTags(5000,DummyPositionSensitiveRecordWithChildren::new), | |||
ProgStringTag(5001,null), | |||
ProgBinaryTag(5002,DummyPositionSensitiveRecordWithChildren.class), | |||
BinaryTagData(5003, BinaryTagDataBlob.class),//0x138b RT_BinaryTagDataBlob | |||
ProgBinaryTag(5002,DummyPositionSensitiveRecordWithChildren::new), | |||
BinaryTagData(5003, BinaryTagDataBlob::new),//0x138b RT_BinaryTagDataBlob | |||
PrpublicintOptions(6000,null), | |||
PersistPtrFullBlock(6001,PersistPtrHolder.class), | |||
PersistPtrIncrementalBlock(6002,PersistPtrHolder.class), | |||
PersistPtrFullBlock(6001,PersistPtrHolder::new), | |||
PersistPtrIncrementalBlock(6002,PersistPtrHolder::new), | |||
GScalingAtom(10001,null), | |||
GRColorAtom(10002,null), | |||
// Records ~12000 seem to be related to the Comments used in PPT 2000/XP | |||
// (Comments in PPT97 are normal Escher text boxes) | |||
Comment2000(12000,Comment2000.class), | |||
Comment2000Atom(12001,Comment2000Atom.class), | |||
Comment2000(12000,Comment2000::new), | |||
Comment2000Atom(12001,Comment2000Atom::new), | |||
Comment2000Summary(12004,null), | |||
Comment2000SummaryAtom(12005,null), | |||
// Records ~12050 seem to be related to Document Encryption | |||
DocumentEncryptionAtom(12052,DocumentEncryptionAtom.class), | |||
DocumentEncryptionAtom(12052,DocumentEncryptionAtom::new), | |||
OriginalMainMasterId(1052,null), | |||
CompositeMasterId(1052,null), | |||
RoundTripContentMasterInfo12(1054,null), | |||
RoundTripShapeId12(1055,null), | |||
RoundTripHFPlaceholder12(1056,RoundTripHFPlaceholder12.class), | |||
RoundTripHFPlaceholder12(1056,RoundTripHFPlaceholder12::new), | |||
RoundTripContentMasterId(1058,null), | |||
RoundTripOArtTextStyles12(1059,null), | |||
RoundTripShapeCheckSumForCustomLayouts12(1062,null), | |||
@@ -207,6 +207,11 @@ public enum RecordTypes { | |||
// same as EscherTertiaryOptRecord.RECORD_ID | |||
EscherUserDefined(0xf122,null); | |||
@FunctionalInterface | |||
public interface RecordConstructor<T extends Record> { | |||
T apply(byte[] source, int start, int len); | |||
} | |||
private static final Map<Short,RecordTypes> LOOKUP; | |||
static { | |||
@@ -217,85 +222,15 @@ public enum RecordTypes { | |||
} | |||
public final short typeID; | |||
public final Class<? extends Record> handlingClass; | |||
public final RecordConstructor recordConstructor; | |||
private RecordTypes(int typeID, Class<? extends Record> handlingClass) { | |||
RecordTypes(int typeID, RecordConstructor recordConstructor) { | |||
this.typeID = (short)typeID; | |||
this.handlingClass = handlingClass; | |||
this.recordConstructor = recordConstructor; | |||
} | |||
public static RecordTypes forTypeID(int typeID) { | |||
RecordTypes rt = LOOKUP.get((short)typeID); | |||
return (rt != null) ? rt : UnknownRecordPlaceholder; | |||
} | |||
/** | |||
* Returns name of the record by its type | |||
* | |||
* @param type section of the record header | |||
* @return name of the record | |||
*/ | |||
// public static String recordName(int type) { | |||
// String name = typeToName.get(Integer.valueOf(type)); | |||
// return (name == null) ? ("Unknown" + type) : name; | |||
// } | |||
/** | |||
* Returns the class handling a record by its type. | |||
* If given an un-handled PowerPoint record, will return a dummy | |||
* placeholder class. If given an unknown PowerPoint record, or | |||
* and Escher record, will return null. | |||
* | |||
* @param type section of the record header | |||
* @return class to handle the record, or null if an unknown (eg Escher) record | |||
*/ | |||
// public static Class<? extends Record> recordHandlingClass(int type) { | |||
// Class<? extends Record> c = typeToClass.get(Integer.valueOf(type)); | |||
// return c; | |||
// } | |||
// | |||
// static { | |||
// typeToName = new HashMap<Integer,String>(); | |||
// typeToClass = new HashMap<Integer,Class<? extends Record>>(); | |||
// try { | |||
// Field[] f = RecordTypes.class.getFields(); | |||
// for (int i = 0; i < f.length; i++){ | |||
// Object val = f[i].get(null); | |||
// | |||
// // Escher record, only store ID -> Name | |||
// if (val instanceof Integer) { | |||
// typeToName.put((Integer)val, f[i].getName()); | |||
// } | |||
// // PowerPoint record, store ID -> Name and ID -> Class | |||
// if (val instanceof Type) { | |||
// Type t = (Type)val; | |||
// Class<? extends Record> c = t.handlingClass; | |||
// Integer id = Integer.valueOf(t.typeID); | |||
// if(c == null) { c = UnknownRecordPlaceholder.class; } | |||
// | |||
// typeToName.put(id, f[i].getName()); | |||
// typeToClass.put(id, c); | |||
// } | |||
// } | |||
// } catch (IllegalAccessException e){ | |||
// throw new HSLFException("Failed to initialize records types"); | |||
// } | |||
// } | |||
/** | |||
* Wrapper for the details of a PowerPoint or Escher record type. | |||
* Contains both the type, and the handling class (if any), and | |||
* offers methods to get either back out. | |||
*/ | |||
// public static class Type { | |||
// public final int typeID; | |||
// public final Class<? extends Record> handlingClass; | |||
// public Type(int typeID, Class<? extends Record> handlingClass) { | |||
// this.typeID = typeID; | |||
// this.handlingClass = handlingClass; | |||
// } | |||
// } | |||
} |
@@ -0,0 +1,104 @@ | |||
/* ==================================================================== | |||
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 org.apache.poi.hslf.record.Comment2000; | |||
import org.apache.poi.sl.usermodel.Comment; | |||
import org.apache.poi.util.Units; | |||
import java.awt.geom.Point2D; | |||
import java.util.Date; | |||
public final class HSLFComment implements Comment { | |||
private Comment2000 _comment2000; | |||
public HSLFComment(Comment2000 comment2000) { | |||
_comment2000 = comment2000; | |||
} | |||
protected Comment2000 getComment2000() { | |||
return _comment2000; | |||
} | |||
/** | |||
* Get the Author of this comment | |||
*/ | |||
public String getAuthor() { | |||
return _comment2000.getAuthor(); | |||
} | |||
/** | |||
* Set the Author of this comment | |||
*/ | |||
public void setAuthor(String author) { | |||
_comment2000.setAuthor(author); | |||
} | |||
/** | |||
* Get the Author's Initials of this comment | |||
*/ | |||
public String getAuthorInitials() { | |||
return _comment2000.getAuthorInitials(); | |||
} | |||
/** | |||
* Set the Author's Initials of this comment | |||
*/ | |||
public void setAuthorInitials(String initials) { | |||
_comment2000.setAuthorInitials(initials); | |||
} | |||
/** | |||
* Get the text of this comment | |||
*/ | |||
public String getText() { | |||
return _comment2000.getText(); | |||
} | |||
/** | |||
* Set the text of this comment | |||
*/ | |||
public void setText(String text) { | |||
_comment2000.setText(text); | |||
} | |||
@Override | |||
public Date getDate() { | |||
return _comment2000.getComment2000Atom().getDate(); | |||
} | |||
@Override | |||
public void setDate(Date date) { | |||
_comment2000.getComment2000Atom().setDate(date); | |||
} | |||
@Override | |||
public Point2D getOffset() { | |||
final double x = Units.masterToPoints(_comment2000.getComment2000Atom().getXOffset()); | |||
final double y = Units.masterToPoints(_comment2000.getComment2000Atom().getYOffset()); | |||
return new Point2D.Double(x, y); | |||
} | |||
@Override | |||
public void setOffset(Point2D offset) { | |||
final int x = Units.pointsToMaster(offset.getX()); | |||
final int y = Units.pointsToMaster(offset.getY()); | |||
_comment2000.getComment2000Atom().setXOffset(x); | |||
_comment2000.getComment2000Atom().setYOffset(y); | |||
} | |||
} |
@@ -21,6 +21,8 @@ import org.apache.poi.hslf.model.textproperties.TextPropCollection; | |||
import org.apache.poi.hslf.record.SheetContainer; | |||
import org.apache.poi.hslf.record.TextHeaderAtom; | |||
import org.apache.poi.sl.usermodel.MasterSheet; | |||
import org.apache.poi.sl.usermodel.SimpleShape; | |||
import org.apache.poi.util.Removal; | |||
/** | |||
* The superclass of all master sheets - Slide masters, Notes masters, etc. | |||
@@ -52,11 +54,13 @@ public abstract class HSLFMasterSheet extends HSLFSheet implements MasterSheet<H | |||
* | |||
* | |||
* @return true if the shape is a placeholder | |||
* | |||
* @deprecated use {@link SimpleShape#isPlaceholder()} | |||
*/ | |||
@Deprecated | |||
@Removal(version="4.1.0") | |||
public static boolean isPlaceholder(HSLFShape shape){ | |||
if(!(shape instanceof HSLFTextShape)) return false; | |||
HSLFTextShape tx = (HSLFTextShape)shape; | |||
return tx.getPlaceholderAtom() != null; | |||
return shape instanceof SimpleShape | |||
&& ((SimpleShape<?,?>)shape).isPlaceholder(); | |||
} | |||
} |
@@ -20,7 +20,10 @@ package org.apache.poi.hslf.usermodel; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import org.apache.poi.hslf.model.HeadersFooters; | |||
import org.apache.poi.hslf.record.HeadersFootersContainer; | |||
import org.apache.poi.sl.usermodel.Notes; | |||
import org.apache.poi.sl.usermodel.Placeholder; | |||
import org.apache.poi.util.POILogFactory; | |||
import org.apache.poi.util.POILogger; | |||
@@ -72,4 +75,28 @@ public final class HSLFNotes extends HSLFSheet implements Notes<HSLFShape,HSLFTe | |||
public HSLFMasterSheet getMasterSheet() { | |||
return null; | |||
} | |||
/** | |||
* Header / Footer settings for this slide. | |||
* | |||
* @return Header / Footer settings for this slide | |||
*/ | |||
@Override | |||
public HeadersFooters getHeadersFooters() { | |||
return new HeadersFooters(this, HeadersFootersContainer.NotesHeadersFootersContainer); | |||
} | |||
@Override | |||
public HSLFPlaceholderDetails getPlaceholderDetails(Placeholder placeholder) { | |||
if (placeholder == null) { | |||
return null; | |||
} | |||
if (placeholder == Placeholder.HEADER || placeholder == Placeholder.FOOTER) { | |||
return new HSLFPlaceholderDetails(this, placeholder); | |||
} else { | |||
return super.getPlaceholderDetails(placeholder); | |||
} | |||
} | |||
} |
@@ -0,0 +1,157 @@ | |||
/* ==================================================================== | |||
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 org.apache.poi.hslf.model.HeadersFooters; | |||
import org.apache.poi.sl.usermodel.Placeholder; | |||
import org.apache.poi.sl.usermodel.PlaceholderDetails; | |||
/** | |||
* Extended placeholder details for HSLF sheets - mainly for headers and footers | |||
* | |||
* @since POI 4.0.0 | |||
*/ | |||
public class HSLFPlaceholderDetails implements PlaceholderDetails { | |||
private final HSLFSheet sheet; | |||
private final Placeholder placeholder; | |||
HSLFPlaceholderDetails(final HSLFSheet sheet, final Placeholder placeholder) { | |||
this.sheet = sheet; | |||
this.placeholder = placeholder; | |||
} | |||
public boolean isVisible() { | |||
final Placeholder ph = getPlaceholder(); | |||
if (ph == null) { | |||
return false; | |||
} | |||
final HeadersFooters headersFooters = sheet.getHeadersFooters(); | |||
switch (ph) { | |||
case HEADER: | |||
return headersFooters.isHeaderVisible(); | |||
case FOOTER: | |||
return headersFooters.isFooterVisible(); | |||
case DATETIME: | |||
return headersFooters.isDateTimeVisible(); | |||
case TITLE: | |||
return headersFooters.isHeaderVisible(); | |||
case SLIDE_NUMBER: | |||
return headersFooters.isSlideNumberVisible(); | |||
default: | |||
return false; | |||
} | |||
} | |||
public void setVisible(final boolean isVisible) { | |||
final Placeholder ph = getPlaceholder(); | |||
if (ph == null) { | |||
return; | |||
} | |||
final HeadersFooters headersFooters = sheet.getHeadersFooters(); | |||
switch (ph) { | |||
case TITLE: | |||
case HEADER: | |||
headersFooters.setHeaderVisible(isVisible); | |||
break; | |||
case FOOTER: | |||
headersFooters.setFooterVisible(isVisible); | |||
break; | |||
case DATETIME: | |||
headersFooters.setDateTimeVisible(isVisible); | |||
break; | |||
case SLIDE_NUMBER: | |||
headersFooters.setSlideNumberVisible(isVisible); | |||
break; | |||
} | |||
} | |||
@Override | |||
public Placeholder getPlaceholder() { | |||
return placeholder; | |||
} | |||
@Override | |||
public void setPlaceholder(Placeholder placeholder) { | |||
throw new UnsupportedOperationException("Only sub class(es) of HSLFPlaceholderDetails allow setting the placeholder"); | |||
} | |||
@Override | |||
public PlaceholderSize getSize() { | |||
return PlaceholderSize.full; | |||
} | |||
@Override | |||
public void setSize(PlaceholderSize size) { | |||
throw new UnsupportedOperationException("Only sub class(es) of HSLFPlaceholderDetails allow setting the size"); | |||
} | |||
@Override | |||
public String getText() { | |||
final Placeholder ph = getPlaceholder(); | |||
if (ph == null) { | |||
return null; | |||
} | |||
final HeadersFooters headersFooters = sheet.getHeadersFooters(); | |||
switch (ph) { | |||
case TITLE: | |||
case HEADER: | |||
return headersFooters.getHeaderText(); | |||
case FOOTER: | |||
return headersFooters.getFooterText(); | |||
case DATETIME: | |||
return headersFooters.getDateTimeText(); | |||
case SLIDE_NUMBER: | |||
default: | |||
return null; | |||
} | |||
} | |||
@Override | |||
public void setText(final String text) { | |||
final Placeholder ph = getPlaceholder(); | |||
if (ph == null) { | |||
return; | |||
} | |||
final HeadersFooters headersFooters = sheet.getHeadersFooters(); | |||
switch (ph) { | |||
case TITLE: | |||
case HEADER: | |||
headersFooters.setHeaderText(text); | |||
break; | |||
case FOOTER: | |||
headersFooters.setFootersText(text); | |||
break; | |||
case DATETIME: | |||
headersFooters.setDateTimeText(text); | |||
break; | |||
case SLIDE_NUMBER: | |||
default: | |||
break; | |||
} | |||
} | |||
} |
@@ -0,0 +1,240 @@ | |||
/* ==================================================================== | |||
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 org.apache.poi.ddf.EscherProperties; | |||
import org.apache.poi.ddf.EscherSpRecord; | |||
import org.apache.poi.hslf.exceptions.HSLFException; | |||
import org.apache.poi.hslf.record.HSLFEscherClientDataRecord; | |||
import org.apache.poi.hslf.record.OEPlaceholderAtom; | |||
import org.apache.poi.hslf.record.Record; | |||
import org.apache.poi.hslf.record.RoundTripHFPlaceholder12; | |||
import org.apache.poi.sl.usermodel.MasterSheet; | |||
import org.apache.poi.sl.usermodel.Placeholder; | |||
/** | |||
* Extended placeholder details for HSLF shapes | |||
* | |||
* @since POI 4.0.0 | |||
*/ | |||
public class HSLFShapePlaceholderDetails extends HSLFPlaceholderDetails { | |||
private enum PlaceholderContainer { | |||
slide, master, notes, notesMaster | |||
} | |||
private final PlaceholderContainer source; | |||
final HSLFSimpleShape shape; | |||
private OEPlaceholderAtom oePlaceholderAtom; | |||
private RoundTripHFPlaceholder12 roundTripHFPlaceholder12; | |||
HSLFShapePlaceholderDetails(final HSLFSimpleShape shape) { | |||
super(shape.getSheet(), null); | |||
this.shape = shape; | |||
final HSLFSheet sheet = shape.getSheet(); | |||
if (sheet instanceof HSLFSlideMaster) { | |||
source = PlaceholderContainer.master; | |||
} else if (sheet instanceof HSLFNotes) { | |||
source = PlaceholderContainer.notes; | |||
} else if (sheet instanceof MasterSheet) { | |||
// notes master aren't yet supported ... | |||
source = PlaceholderContainer.notesMaster; | |||
} else { | |||
source = PlaceholderContainer.slide; | |||
} | |||
} | |||
public Placeholder getPlaceholder() { | |||
updatePlaceholderAtom(false); | |||
final int phId; | |||
if (oePlaceholderAtom != null) { | |||
phId = oePlaceholderAtom.getPlaceholderId(); | |||
} else if (roundTripHFPlaceholder12 != null) { | |||
phId = roundTripHFPlaceholder12.getPlaceholderId(); | |||
} else { | |||
return null; | |||
} | |||
switch (source) { | |||
case slide: | |||
return Placeholder.lookupNativeSlide(phId); | |||
default: | |||
case master: | |||
return Placeholder.lookupNativeSlideMaster(phId); | |||
case notes: | |||
return Placeholder.lookupNativeNotes(phId); | |||
case notesMaster: | |||
return Placeholder.lookupNativeNotesMaster(phId); | |||
} | |||
} | |||
public void setPlaceholder(final Placeholder placeholder) { | |||
final EscherSpRecord spRecord = shape.getEscherChild(EscherSpRecord.RECORD_ID); | |||
int flags = spRecord.getFlags(); | |||
if (placeholder == null) { | |||
flags ^= EscherSpRecord.FLAG_HAVEMASTER; | |||
} else { | |||
flags |= EscherSpRecord.FLAG_HAVEANCHOR | EscherSpRecord.FLAG_HAVEMASTER; | |||
} | |||
spRecord.setFlags(flags); | |||
// Placeholders can't be grouped | |||
shape.setEscherProperty(EscherProperties.PROTECTION__LOCKAGAINSTGROUPING, (placeholder == null ? -1 : 262144)); | |||
if (placeholder == null) { | |||
removePlaceholder(); | |||
return; | |||
} | |||
// init client data | |||
updatePlaceholderAtom(true); | |||
final byte phId = getPlaceholderId(placeholder); | |||
oePlaceholderAtom.setPlaceholderId(phId); | |||
roundTripHFPlaceholder12.setPlaceholderId(phId); | |||
} | |||
public PlaceholderSize getSize() { | |||
final Placeholder ph = getPlaceholder(); | |||
if (ph == null) { | |||
return null; | |||
} | |||
final int size = (oePlaceholderAtom != null) | |||
? oePlaceholderAtom.getPlaceholderSize() | |||
: OEPlaceholderAtom.PLACEHOLDER_HALFSIZE; | |||
switch (size) { | |||
case OEPlaceholderAtom.PLACEHOLDER_FULLSIZE: | |||
return PlaceholderSize.full; | |||
default: | |||
case OEPlaceholderAtom.PLACEHOLDER_HALFSIZE: | |||
return PlaceholderSize.half; | |||
case OEPlaceholderAtom.PLACEHOLDER_QUARTSIZE: | |||
return PlaceholderSize.quarter; | |||
} | |||
} | |||
public void setSize(final PlaceholderSize size) { | |||
final Placeholder ph = getPlaceholder(); | |||
if (ph == null || size == null) { | |||
return; | |||
} | |||
updatePlaceholderAtom(true); | |||
final byte ph_size; | |||
switch (size) { | |||
case full: | |||
ph_size = OEPlaceholderAtom.PLACEHOLDER_FULLSIZE; | |||
break; | |||
default: | |||
case half: | |||
ph_size = OEPlaceholderAtom.PLACEHOLDER_HALFSIZE; | |||
break; | |||
case quarter: | |||
ph_size = OEPlaceholderAtom.PLACEHOLDER_QUARTSIZE; | |||
break; | |||
} | |||
oePlaceholderAtom.setPlaceholderSize(ph_size); | |||
} | |||
private byte getPlaceholderId(final Placeholder placeholder) { | |||
/* | |||
* Extract from MSDN: | |||
* | |||
* There is a special case when the placeholder does not have a position in the layout. | |||
* This occurs when the user has moved the placeholder from its original position. | |||
* In this case the placeholder ID is -1. | |||
*/ | |||
final byte phId; | |||
switch (source) { | |||
default: | |||
case slide: | |||
phId = (byte)placeholder.nativeSlideId; | |||
break; | |||
case master: | |||
phId = (byte)placeholder.nativeSlideMasterId; | |||
break; | |||
case notes: | |||
phId = (byte)placeholder.nativeNotesId; | |||
break; | |||
case notesMaster: | |||
phId = (byte)placeholder.nativeNotesMasterId; | |||
break; | |||
} | |||
if (phId == -2) { | |||
throw new HSLFException("Placeholder "+placeholder.name()+" not supported for this sheet type ("+shape.getSheet().getClass()+")"); | |||
} | |||
return phId; | |||
} | |||
private void removePlaceholder() { | |||
final HSLFEscherClientDataRecord clientData = shape.getClientData(false); | |||
if (clientData != null) { | |||
clientData.removeChild(OEPlaceholderAtom.class); | |||
clientData.removeChild(RoundTripHFPlaceholder12.class); | |||
// remove client data if the placeholder was the only child to be carried | |||
if (clientData.getChildRecords().isEmpty()) { | |||
shape.getSpContainer().removeChildRecord(clientData); | |||
} | |||
} | |||
oePlaceholderAtom = null; | |||
roundTripHFPlaceholder12 = null; | |||
} | |||
private void updatePlaceholderAtom(final boolean create) { | |||
final HSLFEscherClientDataRecord clientData = shape.getClientData(create); | |||
if (clientData == null) { | |||
oePlaceholderAtom = null; | |||
roundTripHFPlaceholder12 = null; | |||
if (!create) { | |||
return; | |||
} | |||
throw new HSLFException("Placeholder aren't allowed for shape type: " + shape.getClass().getSimpleName()); | |||
} | |||
for (Record r : clientData.getHSLFChildRecords()) { | |||
if (r instanceof OEPlaceholderAtom) { | |||
oePlaceholderAtom = (OEPlaceholderAtom)r; | |||
} else if (r instanceof RoundTripHFPlaceholder12) { | |||
//special case for files saved in Office 2007 | |||
roundTripHFPlaceholder12 = (RoundTripHFPlaceholder12)r; | |||
} | |||
} | |||
if (!create) { | |||
return; | |||
} | |||
if (oePlaceholderAtom == null) { | |||
oePlaceholderAtom = new OEPlaceholderAtom(); | |||
oePlaceholderAtom.setPlaceholderSize((byte)OEPlaceholderAtom.PLACEHOLDER_FULLSIZE); | |||
// TODO: placement id only "SHOULD" be unique ... check other placeholders on sheet for unique id | |||
oePlaceholderAtom.setPlacementId(-1); | |||
clientData.addChild(oePlaceholderAtom); | |||
} | |||
if (roundTripHFPlaceholder12 == null) { | |||
roundTripHFPlaceholder12 = new RoundTripHFPlaceholder12(); | |||
clientData.addChild(roundTripHFPlaceholder12); | |||
} | |||
} | |||
} |
@@ -28,8 +28,10 @@ import org.apache.poi.ddf.EscherDgRecord; | |||
import org.apache.poi.ddf.EscherDggRecord; | |||
import org.apache.poi.ddf.EscherRecord; | |||
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.HeadersFootersContainer; | |||
import org.apache.poi.hslf.record.PPDrawing; | |||
import org.apache.poi.hslf.record.RecordContainer; | |||
import org.apache.poi.hslf.record.RecordTypes; | |||
@@ -453,4 +455,20 @@ public abstract class HSLFSheet implements HSLFShapeContainer, Sheet<HSLFShape,H | |||
addShape(s); | |||
return s; | |||
} | |||
/** | |||
* Header / Footer settings for this slide. | |||
* | |||
* @return Header / Footer settings for this slide | |||
*/ | |||
public HeadersFooters getHeadersFooters() { | |||
return new HeadersFooters(this, HeadersFootersContainer.SlideHeadersFootersContainer); | |||
} | |||
@Override | |||
public HSLFPlaceholderDetails getPlaceholderDetails(Placeholder placeholder) { | |||
final HSLFSimpleShape ph = getPlaceholder(placeholder); | |||
return (ph == null) ? null : new HSLFShapePlaceholderDetails(ph); | |||
} | |||
} |
@@ -18,7 +18,6 @@ | |||
package org.apache.poi.hslf.usermodel; | |||
import java.awt.Color; | |||
import java.util.List; | |||
import org.apache.poi.ddf.AbstractEscherOptRecord; | |||
import org.apache.poi.ddf.EscherChildAnchorRecord; | |||
@@ -31,10 +30,6 @@ import org.apache.poi.ddf.EscherRecord; | |||
import org.apache.poi.ddf.EscherSimpleProperty; | |||
import org.apache.poi.ddf.EscherSpRecord; | |||
import org.apache.poi.hslf.exceptions.HSLFException; | |||
import org.apache.poi.hslf.record.HSLFEscherClientDataRecord; | |||
import org.apache.poi.hslf.record.OEPlaceholderAtom; | |||
import org.apache.poi.hslf.record.Record; | |||
import org.apache.poi.hslf.record.RoundTripHFPlaceholder12; | |||
import org.apache.poi.sl.draw.DrawPaint; | |||
import org.apache.poi.sl.draw.geom.CustomGeometry; | |||
import org.apache.poi.sl.draw.geom.Guide; | |||
@@ -42,7 +37,6 @@ import org.apache.poi.sl.draw.geom.PresetGeometries; | |||
import org.apache.poi.sl.usermodel.LineDecoration; | |||
import org.apache.poi.sl.usermodel.LineDecoration.DecorationShape; | |||
import org.apache.poi.sl.usermodel.LineDecoration.DecorationSize; | |||
import org.apache.poi.sl.usermodel.MasterSheet; | |||
import org.apache.poi.sl.usermodel.PaintStyle; | |||
import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint; | |||
import org.apache.poi.sl.usermodel.Placeholder; | |||
@@ -540,145 +534,20 @@ public abstract class HSLFSimpleShape extends HSLFShape implements SimpleShape<H | |||
}; | |||
} | |||
@Override | |||
public HSLFShapePlaceholderDetails getPlaceholderDetails() { | |||
return new HSLFShapePlaceholderDetails(this); | |||
} | |||
@Override | |||
public Placeholder getPlaceholder() { | |||
List<? extends Record> clRecords = getClientRecords(); | |||
if (clRecords == null) { | |||
return null; | |||
} | |||
int phSource; | |||
HSLFSheet sheet = getSheet(); | |||
if (sheet instanceof HSLFSlideMaster) { | |||
phSource = 1; | |||
} else if (sheet instanceof HSLFNotes) { | |||
phSource = 2; | |||
} else if (sheet instanceof MasterSheet) { | |||
// notes master aren't yet supported ... | |||
phSource = 3; | |||
} else { | |||
phSource = 0; | |||
} | |||
for (Record r : clRecords) { | |||
int phId; | |||
if (r instanceof OEPlaceholderAtom) { | |||
phId = ((OEPlaceholderAtom)r).getPlaceholderId(); | |||
} else if (r instanceof RoundTripHFPlaceholder12) { | |||
//special case for files saved in Office 2007 | |||
phId = ((RoundTripHFPlaceholder12)r).getPlaceholderId(); | |||
} else { | |||
continue; | |||
} | |||
switch (phSource) { | |||
case 0: | |||
return Placeholder.lookupNativeSlide(phId); | |||
default: | |||
case 1: | |||
return Placeholder.lookupNativeSlideMaster(phId); | |||
case 2: | |||
return Placeholder.lookupNativeNotes(phId); | |||
case 3: | |||
return Placeholder.lookupNativeNotesMaster(phId); | |||
} | |||
} | |||
return null; | |||
return getPlaceholderDetails().getPlaceholder(); | |||
} | |||
@Override | |||
public void setPlaceholder(Placeholder placeholder) { | |||
EscherSpRecord spRecord = getEscherChild(EscherSpRecord.RECORD_ID); | |||
int flags = spRecord.getFlags(); | |||
if (placeholder == null) { | |||
flags ^= EscherSpRecord.FLAG_HAVEMASTER; | |||
} else { | |||
flags |= EscherSpRecord.FLAG_HAVEANCHOR | EscherSpRecord.FLAG_HAVEMASTER; | |||
} | |||
spRecord.setFlags(flags); | |||
// Placeholders can't be grouped | |||
setEscherProperty(EscherProperties.PROTECTION__LOCKAGAINSTGROUPING, (placeholder == null ? -1 : 262144)); | |||
HSLFEscherClientDataRecord clientData = getClientData(false); | |||
if (placeholder == null) { | |||
if (clientData != null) { | |||
clientData.removeChild(OEPlaceholderAtom.class); | |||
clientData.removeChild(RoundTripHFPlaceholder12.class); | |||
// remove client data if the placeholder was the only child to be carried | |||
if (clientData.getChildRecords().isEmpty()) { | |||
getSpContainer().removeChildRecord(clientData); | |||
} | |||
} | |||
return; | |||
} | |||
if (clientData == null) { | |||
clientData = getClientData(true); | |||
} | |||
// OEPlaceholderAtom tells powerpoint that this shape is a placeholder | |||
OEPlaceholderAtom oep = null; | |||
RoundTripHFPlaceholder12 rtp = null; | |||
for (Record r : clientData.getHSLFChildRecords()) { | |||
if (r instanceof OEPlaceholderAtom) { | |||
oep = (OEPlaceholderAtom)r; | |||
break; | |||
} | |||
if (r instanceof RoundTripHFPlaceholder12) { | |||
rtp = (RoundTripHFPlaceholder12)r; | |||
break; | |||
} | |||
} | |||
/** | |||
* Extract from MSDN: | |||
* | |||
* There is a special case when the placeholder does not have a position in the layout. | |||
* This occurs when the user has moved the placeholder from its original position. | |||
* In this case the placeholder ID is -1. | |||
*/ | |||
byte phId; | |||
HSLFSheet sheet = getSheet(); | |||
// TODO: implement/switch NotesMaster | |||
if (sheet instanceof HSLFSlideMaster) { | |||
phId = (byte)placeholder.nativeSlideMasterId; | |||
} else if (sheet instanceof HSLFNotes) { | |||
phId = (byte)placeholder.nativeNotesId; | |||
} else { | |||
phId = (byte)placeholder.nativeSlideId; | |||
} | |||
if (phId == -2) { | |||
throw new HSLFException("Placeholder "+placeholder.name()+" not supported for this sheet type ("+sheet.getClass()+")"); | |||
} | |||
switch (placeholder) { | |||
case HEADER: | |||
case FOOTER: | |||
if (rtp == null) { | |||
rtp = new RoundTripHFPlaceholder12(); | |||
rtp.setPlaceholderId(phId); | |||
clientData.addChild(rtp); | |||
} | |||
if (oep != null) { | |||
clientData.removeChild(OEPlaceholderAtom.class); | |||
} | |||
break; | |||
default: | |||
if (rtp != null) { | |||
clientData.removeChild(RoundTripHFPlaceholder12.class); | |||
} | |||
if (oep == null) { | |||
oep = new OEPlaceholderAtom(); | |||
oep.setPlaceholderSize((byte)OEPlaceholderAtom.PLACEHOLDER_FULLSIZE); | |||
// TODO: placement id only "SHOULD" be unique ... check other placeholders on sheet for unique id | |||
oep.setPlacementId(-1); | |||
oep.setPlaceholderId(phId); | |||
clientData.addChild(oep); | |||
} | |||
break; | |||
} | |||
getPlaceholderDetails().setPlaceholder(placeholder); | |||
} | |||
@@ -727,4 +596,10 @@ public abstract class HSLFSimpleShape extends HSLFShape implements SimpleShape<H | |||
protected void setHyperlink(HSLFHyperlink link) { | |||
_hyperlink = link; | |||
} | |||
@Override | |||
public boolean isPlaceholder() { | |||
// currently we only identify TextShapes as placeholders | |||
return false; | |||
} | |||
} |
@@ -26,12 +26,12 @@ import org.apache.poi.ddf.EscherDgRecord; | |||
import org.apache.poi.ddf.EscherDggRecord; | |||
import org.apache.poi.ddf.EscherSpRecord; | |||
import org.apache.poi.hslf.exceptions.HSLFException; | |||
import org.apache.poi.hslf.model.Comment; | |||
import org.apache.poi.hslf.model.HeadersFooters; | |||
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.Record; | |||
import org.apache.poi.hslf.record.RecordContainer; | |||
import org.apache.poi.hslf.record.RecordTypes; | |||
import org.apache.poi.hslf.record.SSSlideInfoAtom; | |||
@@ -87,8 +87,6 @@ public final class HSLFSlide extends HSLFSheet implements Slide<HSLFShape,HSLFTe | |||
if (_paragraphs.isEmpty()) { | |||
throw new HSLFException("No text records found for slide"); | |||
} | |||
} else { | |||
// No text on the slide, must just be pictures | |||
} | |||
// Grab text from slide's PPDrawing | |||
@@ -366,8 +364,9 @@ public final class HSLFSlide extends HSLFSheet implements Slide<HSLFShape,HSLFTe | |||
*/ | |||
@Override | |||
public HSLFBackground getBackground() { | |||
if(getFollowMasterBackground()) { | |||
return getMasterSheet().getBackground(); | |||
if (getFollowMasterBackground()) { | |||
final HSLFMasterSheet ms = getMasterSheet(); | |||
return (ms == null) ? null : ms.getBackground(); | |||
} | |||
return super.getBackground(); | |||
} | |||
@@ -377,63 +376,44 @@ public final class HSLFSlide extends HSLFSheet implements Slide<HSLFShape,HSLFTe | |||
*/ | |||
@Override | |||
public ColorSchemeAtom getColorScheme() { | |||
if(getFollowMasterScheme()){ | |||
return getMasterSheet().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. | |||
*/ | |||
public Comment[] getComments() { | |||
public List<HSLFComment> getComments() { | |||
final List<HSLFComment> comments = new ArrayList<>(); | |||
// If there are any, they're in | |||
// ProgTags -> ProgBinaryTag -> BinaryTagData | |||
RecordContainer progTags = (RecordContainer) | |||
getSheetContainer().findFirstOfType( | |||
RecordTypes.ProgTags.typeID | |||
); | |||
if(progTags != null) { | |||
RecordContainer progBinaryTag = (RecordContainer) | |||
progTags.findFirstOfType( | |||
RecordTypes.ProgBinaryTag.typeID | |||
); | |||
if(progBinaryTag != null) { | |||
RecordContainer binaryTags = (RecordContainer) | |||
progBinaryTag.findFirstOfType( | |||
RecordTypes.BinaryTagData.typeID | |||
); | |||
if(binaryTags != null) { | |||
// This is where they'll be | |||
int count = 0; | |||
for(int i=0; i<binaryTags.getChildRecords().length; i++) { | |||
if(binaryTags.getChildRecords()[i] instanceof Comment2000) { | |||
count++; | |||
} | |||
} | |||
// Now build | |||
Comment[] comments = new Comment[count]; | |||
count = 0; | |||
for(int i=0; i<binaryTags.getChildRecords().length; i++) { | |||
if(binaryTags.getChildRecords()[i] instanceof Comment2000) { | |||
comments[i] = new Comment( | |||
(Comment2000)binaryTags.getChildRecords()[i] | |||
); | |||
count++; | |||
} | |||
} | |||
return comments; | |||
} | |||
} | |||
} | |||
// None found | |||
return new Comment[0]; | |||
final RecordContainer binaryTags = | |||
selectContainer(getSheetContainer(), 0, | |||
RecordTypes.ProgTags, RecordTypes.ProgBinaryTag, RecordTypes.BinaryTagData); | |||
if (binaryTags != null) { | |||
for (final Record record : binaryTags.getChildRecords()) { | |||
if (record instanceof Comment2000) { | |||
comments.add(new HSLFComment((Comment2000)record)); | |||
} | |||
} | |||
} | |||
return comments; | |||
} | |||
/** | |||
@@ -478,9 +458,7 @@ public final class HSLFSlide extends HSLFSheet implements Slide<HSLFShape,HSLFTe | |||
public boolean isHidden() { | |||
SSSlideInfoAtom slideInfo = | |||
(SSSlideInfoAtom)getSlideRecord().findFirstOfType(RecordTypes.SSSlideInfoAtom.typeID); | |||
return (slideInfo == null) | |||
? false | |||
: slideInfo.getEffectTransitionFlagByBit(SSSlideInfoAtom.HIDDEN_BIT); | |||
return (slideInfo != null) && slideInfo.getEffectTransitionFlagByBit(SSSlideInfoAtom.HIDDEN_BIT); | |||
} | |||
@Override | |||
@@ -505,25 +483,22 @@ public final class HSLFSlide extends HSLFSheet implements Slide<HSLFShape,HSLFTe | |||
} | |||
@Override | |||
public boolean getDisplayPlaceholder(Placeholder placeholder) { | |||
HeadersFooters hf = getHeadersFooters(); | |||
SlideLayoutType slt = getSlideRecord().getSlideAtom().getSSlideLayoutAtom().getGeometryType(); | |||
boolean isTitle = | |||
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); | |||
if (hf != null) { | |||
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: | |||
break; | |||
} | |||
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; | |||
} | |||
return false; | |||
} | |||
} |
@@ -38,6 +38,7 @@ import org.apache.poi.ddf.EscherContainerRecord; | |||
import org.apache.poi.ddf.EscherOptRecord; | |||
import org.apache.poi.hpsf.ClassID; | |||
import org.apache.poi.hpsf.ClassIDPredefined; | |||
import org.apache.poi.hpsf.extractor.HPSFPropertiesExtractor; | |||
import org.apache.poi.hslf.exceptions.CorruptPowerPointFileException; | |||
import org.apache.poi.hslf.exceptions.HSLFException; | |||
import org.apache.poi.hslf.model.HeadersFooters; | |||
@@ -1047,6 +1048,11 @@ public final class HSLFSlideShow implements SlideShow<HSLFShape,HSLFTextParagrap | |||
return objectId; | |||
} | |||
@Override | |||
public HPSFPropertiesExtractor getMetadataTextExtractor() { | |||
return new HPSFPropertiesExtractor(getSlideShowImpl()); | |||
} | |||
protected int addToObjListAtom(RecordContainer exObj) { | |||
ExObjList lst = getDocumentRecord().getExObjList(true); | |||
ExObjListAtom objAtom = lst.getExObjListAtom(); |
@@ -272,7 +272,7 @@ implements TextShape<HSLFShape,HSLFTextParagraph> { | |||
} | |||
final String text = ((tba != null) ? tba.getText() : tca.getText()); | |||
StyleTextPropAtom sta = (StyleTextPropAtom)_txtbox.findFirstOfType(StyleTextPropAtom._type); | |||
TextPropCollection paraStyle = null, charStyle = null; | |||
if (sta == null) { | |||
@@ -305,7 +305,7 @@ implements TextShape<HSLFShape,HSLFTextParagraph> { | |||
htp.setParagraphStyle(paraStyle); | |||
htp.setParentShape(this); | |||
_paragraphs.add(htp); | |||
HSLFTextRun htr = new HSLFTextRun(htp); | |||
htr.setCharacterStyle(charStyle); | |||
htr.setText(text); | |||
@@ -317,7 +317,7 @@ implements TextShape<HSLFShape,HSLFTextParagraph> { | |||
public Rectangle2D resizeToFitText() { | |||
return resizeToFitText(null); | |||
} | |||
@Override | |||
public Rectangle2D resizeToFitText(Graphics2D graphics) { | |||
Rectangle2D anchor = getAnchor(); | |||
@@ -649,7 +649,7 @@ implements TextShape<HSLFShape,HSLFTextParagraph> { | |||
} else { | |||
_paragraphs = pList; | |||
} | |||
if (_paragraphs.isEmpty()) { | |||
LOG.log(POILogger.WARN, "TextRecord didn't contained any text lines"); | |||
} | |||
@@ -701,18 +701,13 @@ implements TextShape<HSLFShape,HSLFTextParagraph> { | |||
@Override | |||
public boolean isPlaceholder() { | |||
OEPlaceholderAtom oep = getPlaceholderAtom(); | |||
if (oep != null) { | |||
return true; | |||
} | |||
//special case for files saved in Office 2007 | |||
RoundTripHFPlaceholder12 hldr = getHFPlaceholderAtom(); | |||
if (hldr != null) { | |||
return true; | |||
} | |||
return false; | |||
return | |||
((getPlaceholderAtom() != null) || | |||
//special case for files saved in Office 2007 | |||
(getHFPlaceholderAtom() != null)) && | |||
// check for metro shape of complex placeholder | |||
(!new HSLFMetroShape<HSLFShape>(this).hasMetroBlob()) | |||
; | |||
} | |||
@@ -738,7 +733,7 @@ implements TextShape<HSLFShape,HSLFTextParagraph> { | |||
public double getTextHeight() { | |||
return getTextHeight(null); | |||
} | |||
@Override | |||
public double getTextHeight(Graphics2D graphics) { | |||
DrawFactory drawFact = DrawFactory.getInstance(graphics); |
@@ -33,9 +33,9 @@ import java.io.InputStream; | |||
import java.util.List; | |||
import org.apache.poi.POIDataSamples; | |||
import org.apache.poi.hslf.usermodel.HSLFObjectShape; | |||
import org.apache.poi.hslf.usermodel.HSLFSlideShow; | |||
import org.apache.poi.hslf.usermodel.HSLFSlideShowImpl; | |||
import org.apache.poi.hslf.usermodel.HSLFObjectShape; | |||
import org.apache.poi.hssf.usermodel.HSSFWorkbook; | |||
import org.apache.poi.hwpf.HWPFDocument; | |||
import org.apache.poi.poifs.filesystem.DirectoryNode; | |||
@@ -89,12 +89,12 @@ public final class TestExtractor { | |||
public void testReadSheetText() throws IOException { | |||
// Basic 2 page example | |||
PowerPointExtractor ppe = openExtractor("basic_test_ppt_file.ppt"); | |||
ensureTwoStringsTheSame(expectText, ppe.getText()); | |||
assertEquals(expectText, ppe.getText()); | |||
ppe.close(); | |||
// 1 page example with text boxes | |||
PowerPointExtractor ppe2 = openExtractor("with_textbox.ppt"); | |||
ensureTwoStringsTheSame(expectText2, ppe2.getText()); | |||
assertEquals(expectText2, ppe2.getText()); | |||
ppe2.close(); | |||
} | |||
@@ -103,15 +103,15 @@ public final class TestExtractor { | |||
// Basic 2 page example | |||
PowerPointExtractor ppe = openExtractor("basic_test_ppt_file.ppt"); | |||
String notesText = ppe.getNotes(); | |||
String expText = "These are the notes for page 1\nThese are the notes on page two, again lacking formatting\n"; | |||
ensureTwoStringsTheSame(expText, notesText); | |||
String expText = "\nThese are the notes for page 1\n\nThese are the notes on page two, again lacking formatting\n"; | |||
assertEquals(expText, notesText); | |||
ppe.close(); | |||
// Other one doesn't have notes | |||
PowerPointExtractor ppe2 = openExtractor("with_textbox.ppt"); | |||
notesText = ppe2.getNotes(); | |||
expText = ""; | |||
ensureTwoStringsTheSame(expText, notesText); | |||
assertEquals(expText, notesText); | |||
ppe2.close(); | |||
} | |||
@@ -122,8 +122,8 @@ public final class TestExtractor { | |||
"This is the title on page 2\nThis is page two\nIt has several blocks of text\nNone of them have formatting\n" | |||
}; | |||
String[] ntText = new String[]{ | |||
"These are the notes for page 1\n", | |||
"These are the notes on page two, again lacking formatting\n" | |||
"\nThese are the notes for page 1\n", | |||
"\nThese are the notes on page two, again lacking formatting\n" | |||
}; | |||
PowerPointExtractor ppe = openExtractor("basic_test_ppt_file.ppt"); | |||
@@ -137,7 +137,7 @@ public final class TestExtractor { | |||
ppe.setSlidesByDefault(true); | |||
ppe.setNotesByDefault(true); | |||
assertEquals(slText[0] + slText[1] + "\n" + ntText[0] + ntText[1], ppe.getText()); | |||
assertEquals(slText[0] + ntText[0] + slText[1] + ntText[1], ppe.getText()); | |||
ppe.close(); | |||
} | |||
@@ -166,16 +166,6 @@ public final class TestExtractor { | |||
ppe.close(); | |||
} | |||
private void ensureTwoStringsTheSame(String exp, String act) { | |||
assertEquals(exp.length(), act.length()); | |||
char[] expC = exp.toCharArray(); | |||
char[] actC = act.toCharArray(); | |||
for (int i = 0; i < expC.length; i++) { | |||
assertEquals("Char " + i, expC[i], actC[i]); | |||
} | |||
assertEquals(exp, act); | |||
} | |||
@Test | |||
public void testExtractFromEmbeded() throws IOException { | |||
InputStream is = POIDataSamples.getSpreadSheetInstance().openResourceAsStream("excel_with_embeded.xls"); | |||
@@ -454,4 +444,38 @@ public final class TestExtractor { | |||
assertContains(text, "Prague"); | |||
ppe.close(); | |||
} | |||
@Test | |||
public void testExtractGroupedShapeText() throws Exception { | |||
try (final PowerPointExtractor ppe = openExtractor("bug62092.ppt")) { | |||
final String text = ppe.getText(); | |||
//this tests that we're ignoring text shapes at depth=0 | |||
//i.e. POI has already included them in the slide's getTextParagraphs() | |||
assertContains(text, "Text box1"); | |||
assertEquals(1, countMatches(text,"Text box1")); | |||
//the WordArt and text box count tests will fail | |||
//if this content is available via getTextParagraphs() of the slide in POI | |||
//i.e. when POI is fixed, these tests will fail, and | |||
//we'll have to remove the workaround in HSLFExtractor's extractGroupText(...) | |||
assertEquals(1, countMatches(text,"WordArt1")); | |||
assertEquals(1, countMatches(text,"WordArt2")); | |||
assertEquals(1, countMatches(text,"Ungrouped text box"));//should only be 1 | |||
assertContains(text, "Text box2"); | |||
assertContains(text, "Text box3"); | |||
assertContains(text, "Text box4"); | |||
assertContains(text, "Text box5"); | |||
//see below -- need to extract hyperlinks | |||
assertContains(text, "tika"); | |||
assertContains(text, "MyTitle"); | |||
} | |||
} | |||
private static int countMatches(final String base, final String find) { | |||
return base.split(find).length-1; | |||
} | |||
} |
@@ -18,10 +18,10 @@ | |||
package org.apache.poi.hslf.record; | |||
import static org.junit.Assert.assertEquals; | |||
import org.junit.Test; | |||
import static org.junit.Assert.assertEquals; | |||
/** | |||
* Tests that RecordTypes returns the right records and classes when asked | |||
*/ | |||
@@ -42,20 +42,15 @@ public final class TestRecordTypes { | |||
@Test | |||
public void testPPTClassLookups() { | |||
assertEquals(Slide.class, RecordTypes.Slide.handlingClass); | |||
assertEquals(TextCharsAtom.class, RecordTypes.TextCharsAtom.handlingClass); | |||
assertEquals(TextBytesAtom.class, RecordTypes.TextBytesAtom.handlingClass); | |||
assertEquals(SlideListWithText.class, RecordTypes.SlideListWithText.handlingClass); | |||
// If this record is ever implemented, change to one that isn't! | |||
// This is checking the "unhandled default" stuff works | |||
assertEquals(UnknownRecordPlaceholder.class, RecordTypes.forTypeID(-10).handlingClass); | |||
assertEquals(RecordTypes.UnknownRecordPlaceholder, RecordTypes.forTypeID(-10)); | |||
} | |||
@Test | |||
public void testEscherClassLookups() { | |||
// Should all come back with null, as DDF handles them | |||
assertEquals(null, RecordTypes.EscherDggContainer.handlingClass); | |||
assertEquals(null, RecordTypes.EscherBStoreContainer.handlingClass); | |||
assertEquals(null, RecordTypes.EscherDggContainer.recordConstructor); | |||
assertEquals(null, RecordTypes.EscherBStoreContainer.recordConstructor); | |||
} | |||
} |