git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1724338 13f79535-47bb-0310-9956-ffa450edef68tags/REL_3_14_FINAL
String text = textBox1.getText(); | String text = textBox1.getText(); | ||||
HSLFHyperlink link = new HSLFHyperlink(); | HSLFHyperlink link = new HSLFHyperlink(); | ||||
link.setAddress("http://www.apache.org"); | link.setAddress("http://www.apache.org"); | ||||
link.setTitle(textBox1.getText()); | |||||
link.setLabel(textBox1.getText()); | |||||
int linkId = ppt.addHyperlink(link); | int linkId = ppt.addHyperlink(link); | ||||
// apply link to the text | // apply link to the text |
//in ppt end index is inclusive | //in ppt end index is inclusive | ||||
String formatStr = "title: %1$s, address: %2$s" + (rawText == null ? "" : ", start: %3$s, end: %4$s, substring: %5$s"); | String formatStr = "title: %1$s, address: %2$s" + (rawText == null ? "" : ", start: %3$s, end: %4$s, substring: %5$s"); | ||||
String substring = (rawText == null) ? "" : rawText.substring(link.getStartIndex(), link.getEndIndex()-1); | String substring = (rawText == null) ? "" : rawText.substring(link.getStartIndex(), link.getEndIndex()-1); | ||||
return String.format(formatStr, link.getTitle(), link.getAddress(), link.getStartIndex(), link.getEndIndex(), substring); | |||||
return String.format(formatStr, link.getLabel(), link.getAddress(), link.getStartIndex(), link.getEndIndex(), substring); | |||||
} | } | ||||
} | } |
import java.awt.font.TextAttribute; | import java.awt.font.TextAttribute; | ||||
import java.awt.font.TextLayout; | import java.awt.font.TextLayout; | ||||
import java.awt.geom.Rectangle2D; | import java.awt.geom.Rectangle2D; | ||||
import java.io.InvalidObjectException; | |||||
import java.text.AttributedCharacterIterator; | import java.text.AttributedCharacterIterator; | ||||
import java.text.AttributedCharacterIterator.Attribute; | import java.text.AttributedCharacterIterator.Attribute; | ||||
import java.text.AttributedString; | import java.text.AttributedString; | ||||
import java.util.Map; | import java.util.Map; | ||||
import org.apache.poi.sl.usermodel.AutoNumberingScheme; | import org.apache.poi.sl.usermodel.AutoNumberingScheme; | ||||
import org.apache.poi.sl.usermodel.Hyperlink; | |||||
import org.apache.poi.sl.usermodel.Insets2D; | import org.apache.poi.sl.usermodel.Insets2D; | ||||
import org.apache.poi.sl.usermodel.PaintStyle; | import org.apache.poi.sl.usermodel.PaintStyle; | ||||
import org.apache.poi.sl.usermodel.PlaceableShape; | import org.apache.poi.sl.usermodel.PlaceableShape; | ||||
import org.apache.poi.util.Units; | import org.apache.poi.util.Units; | ||||
public class DrawTextParagraph implements Drawable { | public class DrawTextParagraph implements Drawable { | ||||
/** Keys for passing hyperlinks to the graphics context */ | |||||
public static final XlinkAttribute HYPERLINK_HREF = new XlinkAttribute("href"); | |||||
public static final XlinkAttribute HYPERLINK_LABEL = new XlinkAttribute("label"); | |||||
protected TextParagraph<?,?,?> paragraph; | protected TextParagraph<?,?,?> paragraph; | ||||
double x, y; | double x, y; | ||||
protected List<DrawTextFragment> lines = new ArrayList<DrawTextFragment>(); | protected List<DrawTextFragment> lines = new ArrayList<DrawTextFragment>(); | ||||
*/ | */ | ||||
protected double maxLineHeight; | protected double maxLineHeight; | ||||
/** | |||||
* Defines an attribute used for storing the hyperlink associated with | |||||
* some renderable text. | |||||
*/ | |||||
private static class XlinkAttribute extends Attribute { | |||||
XlinkAttribute(String name) { | |||||
super(name); | |||||
} | |||||
/** | |||||
* Resolves instances being deserialized to the predefined constants. | |||||
*/ | |||||
protected Object readResolve() throws InvalidObjectException { | |||||
if (HYPERLINK_HREF.getName().equals(getName())) { | |||||
return HYPERLINK_HREF; | |||||
} | |||||
if (HYPERLINK_LABEL.getName().equals(getName())) { | |||||
return HYPERLINK_LABEL; | |||||
} | |||||
throw new InvalidObjectException("unknown attribute name"); | |||||
} | |||||
} | |||||
public DrawTextParagraph(TextParagraph<?,?,?> paragraph) { | public DrawTextParagraph(TextParagraph<?,?,?> paragraph) { | ||||
this.paragraph = paragraph; | this.paragraph = paragraph; | ||||
} | } | ||||
public void setAutoNumberingIdx(int index) { | public void setAutoNumberingIdx(int index) { | ||||
autoNbrIdx = index; | autoNbrIdx = index; | ||||
} | } | ||||
public void draw(Graphics2D graphics){ | public void draw(Graphics2D graphics){ | ||||
if (lines.isEmpty()) return; | if (lines.isEmpty()) return; | ||||
double penY = y; | double penY = y; | ||||
boolean firstLine = true; | boolean firstLine = true; | ||||
// special handling for HSLF | // special handling for HSLF | ||||
indent -= leftMargin; | indent -= leftMargin; | ||||
} | } | ||||
// Double rightMargin = paragraph.getRightMargin(); | // Double rightMargin = paragraph.getRightMargin(); | ||||
// if (rightMargin == null) { | // if (rightMargin == null) { | ||||
// rightMargin = 0d; | // rightMargin = 0d; | ||||
//The vertical line spacing | //The vertical line spacing | ||||
Double spacing = paragraph.getLineSpacing(); | Double spacing = paragraph.getLineSpacing(); | ||||
if (spacing == null) spacing = 100d; | if (spacing == null) spacing = 100d; | ||||
for(DrawTextFragment line : lines){ | for(DrawTextFragment line : lines){ | ||||
double penX; | double penX; | ||||
// TODO: find out character style for empty, but bulleted/numbered lines | // TODO: find out character style for empty, but bulleted/numbered lines | ||||
bullet = getBullet(graphics, line.getAttributedString().getIterator()); | bullet = getBullet(graphics, line.getAttributedString().getIterator()); | ||||
} | } | ||||
if (bullet != null){ | if (bullet != null){ | ||||
bullet.setPosition(x+leftMargin+indent, penY); | bullet.setPosition(x+leftMargin+indent, penY); | ||||
bullet.draw(graphics); | bullet.draw(graphics); | ||||
y = penY - y; | y = penY - y; | ||||
} | } | ||||
public float getFirstLineHeight() { | public float getFirstLineHeight() { | ||||
return (lines.isEmpty()) ? 0 : lines.get(0).getHeight(); | return (lines.isEmpty()) ? 0 : lines.get(0).getHeight(); | ||||
} | } | ||||
public boolean isEmptyParagraph() { | public boolean isEmptyParagraph() { | ||||
return (lines.isEmpty() || rawText.trim().isEmpty()); | return (lines.isEmpty() || rawText.trim().isEmpty()); | ||||
} | } | ||||
public void applyTransform(Graphics2D graphics) { | public void applyTransform(Graphics2D graphics) { | ||||
} | } | ||||
String buFont = bulletStyle.getBulletFont(); | String buFont = bulletStyle.getBulletFont(); | ||||
if (buFont == null) buFont = paragraph.getDefaultFontFamily(); | if (buFont == null) buFont = paragraph.getDefaultFontFamily(); | ||||
assert(buFont != null); | assert(buFont != null); | ||||
PlaceableShape<?,?> ps = getParagraphShape(); | PlaceableShape<?,?> ps = getParagraphShape(); | ||||
PaintStyle fgPaintStyle = bulletStyle.getBulletFontColor(); | PaintStyle fgPaintStyle = bulletStyle.getBulletFontColor(); | ||||
Paint fgPaint; | Paint fgPaint; | ||||
float fontSize = (Float)firstLineAttr.getAttribute(TextAttribute.SIZE); | float fontSize = (Float)firstLineAttr.getAttribute(TextAttribute.SIZE); | ||||
Double buSz = bulletStyle.getBulletFontSize(); | Double buSz = bulletStyle.getBulletFontSize(); | ||||
if (buSz == null) buSz = 100d; | |||||
if (buSz == null) buSz = 100d; | |||||
if (buSz > 0) fontSize *= buSz* 0.01; | if (buSz > 0) fontSize *= buSz* 0.01; | ||||
else fontSize = (float)-buSz; | else fontSize = (float)-buSz; | ||||
AttributedString str = new AttributedString(mapFontCharset(buCharacter,buFont)); | AttributedString str = new AttributedString(mapFontCharset(buCharacter,buFont)); | ||||
str.addAttribute(TextAttribute.FOREGROUND, fgPaint); | str.addAttribute(TextAttribute.FOREGROUND, fgPaint); | ||||
str.addAttribute(TextAttribute.FAMILY, buFont); | str.addAttribute(TextAttribute.FAMILY, buFont); | ||||
case SMALL: c = Character.toLowerCase(c); break; | case SMALL: c = Character.toLowerCase(c); break; | ||||
case NONE: break; | case NONE: break; | ||||
} | } | ||||
buf.append(c); | buf.append(c); | ||||
break; | break; | ||||
} | } | ||||
return buf.toString(); | return buf.toString(); | ||||
} | } | ||||
/** | /** | ||||
* Replace a tab with the effective number of white spaces. | * Replace a tab with the effective number of white spaces. | ||||
*/ | */ | ||||
Double fs = tr.getFontSize(); | Double fs = tr.getFontSize(); | ||||
if (fs == null) fs = 12d; | if (fs == null) fs = 12d; | ||||
string.addAttribute(TextAttribute.SIZE, fs.floatValue()); | string.addAttribute(TextAttribute.SIZE, fs.floatValue()); | ||||
TextLayout l = new TextLayout(string.getIterator(), new FontRenderContext(null, true, true)); | TextLayout l = new TextLayout(string.getIterator(), new FontRenderContext(null, true, true)); | ||||
double wspace = l.getAdvance(); | double wspace = l.getAdvance(); | ||||
} | } | ||||
return buf.toString(); | return buf.toString(); | ||||
} | } | ||||
/** | /** | ||||
* Returns wrapping width to break lines in this paragraph | * Returns wrapping width to break lines in this paragraph | ||||
this.endIndex = endIndex; | this.endIndex = endIndex; | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* Helper method for paint style relative to bounds, e.g. gradient paint | * Helper method for paint style relative to bounds, e.g. gradient paint | ||||
*/ | */ | ||||
if (text == null) text = new StringBuilder(); | if (text == null) text = new StringBuilder(); | ||||
PlaceableShape<?,?> ps = getParagraphShape(); | PlaceableShape<?,?> ps = getParagraphShape(); | ||||
DrawFontManager fontHandler = (DrawFontManager)graphics.getRenderingHint(Drawable.FONT_HANDLER); | DrawFontManager fontHandler = (DrawFontManager)graphics.getRenderingHint(Drawable.FONT_HANDLER); | ||||
for (TextRun run : paragraph){ | for (TextRun run : paragraph){ | ||||
if (fontFamily == null) { | if (fontFamily == null) { | ||||
fontFamily = paragraph.getDefaultFontFamily(); | fontFamily = paragraph.getDefaultFontFamily(); | ||||
} | } | ||||
int beginIndex = text.length(); | int beginIndex = text.length(); | ||||
text.append(mapFontCharset(runText,fontFamily)); | text.append(mapFontCharset(runText,fontFamily)); | ||||
int endIndex = text.length(); | int endIndex = text.length(); | ||||
if(run.isSuperscript()) { | if(run.isSuperscript()) { | ||||
attList.add(new AttributedStringData(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUPER, beginIndex, endIndex)); | attList.add(new AttributedStringData(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUPER, beginIndex, endIndex)); | ||||
} | } | ||||
Hyperlink hl = run.getHyperlink(); | |||||
if (hl != null) { | |||||
attList.add(new AttributedStringData(HYPERLINK_HREF, hl.getAddress(), beginIndex, endIndex)); | |||||
attList.add(new AttributedStringData(HYPERLINK_LABEL, hl.getLabel(), beginIndex, endIndex)); | |||||
} | |||||
} | } | ||||
// ensure that the paragraph contains at least one character | // ensure that the paragraph contains at least one character | ||||
} | } | ||||
protected boolean isHSLF() { | protected boolean isHSLF() { | ||||
return paragraph.getClass().getName().contains("HSLF"); | |||||
return paragraph.getClass().getName().contains("HSLF"); | |||||
} | } | ||||
/** | /** | ||||
* Map text charset depending on font family. | * Map text charset depending on font family. | ||||
* Currently this only maps for wingdings font (into unicode private use area) | * Currently this only maps for wingdings font (into unicode private use area) | ||||
* @param text the raw text | * @param text the raw text | ||||
* @param fontFamily the font family | * @param fontFamily the font family | ||||
* @return AttributedString with mapped codepoints | * @return AttributedString with mapped codepoints | ||||
* | |||||
* | |||||
* @see <a href="http://stackoverflow.com/questions/8692095">Drawing exotic fonts in a java applet</a> | * @see <a href="http://stackoverflow.com/questions/8692095">Drawing exotic fonts in a java applet</a> | ||||
* @see StringUtil#mapMsCodepointString(String) | * @see StringUtil#mapMsCodepointString(String) | ||||
*/ | */ |
* @return the pitch and family id or -1 if not applicable | * @return the pitch and family id or -1 if not applicable | ||||
*/ | */ | ||||
byte getPitchAndFamily(); | byte getPitchAndFamily(); | ||||
/** | |||||
* Return the associated hyperlink | |||||
* | |||||
* @return the associated hyperlink or null if no hyperlink was set | |||||
*/ | |||||
Hyperlink getHyperlink(); | |||||
} | } |
import org.apache.poi.openxml4j.opc.PackageRelationship; | import org.apache.poi.openxml4j.opc.PackageRelationship; | ||||
import org.apache.poi.openxml4j.opc.TargetMode; | import org.apache.poi.openxml4j.opc.TargetMode; | ||||
import org.apache.poi.sl.usermodel.Hyperlink; | |||||
import org.apache.poi.util.Internal; | import org.apache.poi.util.Internal; | ||||
import org.openxmlformats.schemas.drawingml.x2006.main.CTHyperlink; | import org.openxmlformats.schemas.drawingml.x2006.main.CTHyperlink; | ||||
import java.net.URI; | import java.net.URI; | ||||
/** | |||||
* @author Yegor Kozlov | |||||
*/ | |||||
public class XSLFHyperlink { | |||||
public class XSLFHyperlink implements Hyperlink { | |||||
final XSLFTextRun _r; | final XSLFTextRun _r; | ||||
final CTHyperlink _link; | final CTHyperlink _link; | ||||
return _link; | return _link; | ||||
} | } | ||||
@Override | |||||
public void setAddress(String address){ | public void setAddress(String address){ | ||||
XSLFSheet sheet = _r.getParentParagraph().getParentShape().getSheet(); | XSLFSheet sheet = _r.getParentParagraph().getParentShape().getSheet(); | ||||
PackageRelationship rel = | PackageRelationship rel = | ||||
sheet.getPackagePart(). | sheet.getPackagePart(). | ||||
addExternalRelationship(address, XSLFRelation.HYPERLINK.getRelation()); | addExternalRelationship(address, XSLFRelation.HYPERLINK.getRelation()); | ||||
_link.setId(rel.getId()); | _link.setId(rel.getId()); | ||||
} | |||||
@Override | |||||
public String getAddress() { | |||||
return getTargetURI().toASCIIString(); | |||||
} | |||||
@Override | |||||
public String getLabel() { | |||||
return _link.getTooltip(); | |||||
} | |||||
@Override | |||||
public void setLabel(String label) { | |||||
_link.setTooltip(label); | |||||
} | } | ||||
@Override | |||||
public int getType() { | |||||
// TODO: currently this just returns nonsense | |||||
if ("ppaction://hlinksldjump".equals(_link.getAction())) { | |||||
return LINK_DOCUMENT; | |||||
} | |||||
return LINK_URL; | |||||
} | |||||
public void setAddress(XSLFSlide slide){ | public void setAddress(XSLFSlide slide){ | ||||
XSLFSheet sheet = _r.getParentParagraph().getParentShape().getSheet(); | XSLFSheet sheet = _r.getParentParagraph().getParentShape().getSheet(); | ||||
PackageRelationship rel = | PackageRelationship rel = |
return link; | return link; | ||||
} | } | ||||
@Override | |||||
public XSLFHyperlink getHyperlink(){ | public XSLFHyperlink getHyperlink(){ | ||||
if(!_r.getRPr().isSetHlinkClick()) return null; | if(!_r.getRPr().isSetHlinkClick()) return null; | ||||
*/ | */ | ||||
public void drawString(String s, float x, float y) { | public void drawString(String s, float x, float y) { | ||||
HSLFTextBox txt = new HSLFTextBox(_group); | HSLFTextBox txt = new HSLFTextBox(_group); | ||||
txt.getTextParagraphs().get(0).supplySheet(_group.getSheet()); | |||||
txt.setSheet(_group.getSheet()); | |||||
txt.setText(s); | txt.setText(s); | ||||
HSLFTextRun rt = txt.getTextParagraphs().get(0).getTextRuns().get(0); | HSLFTextRun rt = txt.getTextParagraphs().get(0).getTextRuns().get(0); |
import org.apache.poi.hslf.record.InteractiveInfoAtom; | import org.apache.poi.hslf.record.InteractiveInfoAtom; | ||||
import org.apache.poi.hslf.record.Record; | import org.apache.poi.hslf.record.Record; | ||||
import org.apache.poi.hslf.record.TxInteractiveInfoAtom; | import org.apache.poi.hslf.record.TxInteractiveInfoAtom; | ||||
import org.apache.poi.sl.usermodel.Hyperlink; | |||||
/** | /** | ||||
* Represents a hyperlink in a PowerPoint document | * Represents a hyperlink in a PowerPoint document | ||||
* | |||||
* @author Yegor Kozlov | |||||
*/ | */ | ||||
public final class HSLFHyperlink { | |||||
public final class HSLFHyperlink implements Hyperlink { | |||||
public static final byte LINK_NEXTSLIDE = InteractiveInfoAtom.LINK_NextSlide; | public static final byte LINK_NEXTSLIDE = InteractiveInfoAtom.LINK_NextSlide; | ||||
public static final byte LINK_PREVIOUSSLIDE = InteractiveInfoAtom.LINK_PreviousSlide; | public static final byte LINK_PREVIOUSSLIDE = InteractiveInfoAtom.LINK_PreviousSlide; | ||||
public static final byte LINK_FIRSTSLIDE = InteractiveInfoAtom.LINK_FirstSlide; | public static final byte LINK_FIRSTSLIDE = InteractiveInfoAtom.LINK_FirstSlide; | ||||
private int id=-1; | private int id=-1; | ||||
private int type; | private int type; | ||||
private String address; | private String address; | ||||
private String title; | |||||
private String label; | |||||
private int startIndex, endIndex; | private int startIndex, endIndex; | ||||
/** | /** | ||||
* @return the hyperlink URL | * @return the hyperlink URL | ||||
* @see InteractiveInfoAtom | * @see InteractiveInfoAtom | ||||
*/ | */ | ||||
@Override | |||||
public int getType() { | public int getType() { | ||||
return type; | return type; | ||||
} | } | ||||
type = val; | type = val; | ||||
switch(type){ | switch(type){ | ||||
case LINK_NEXTSLIDE: | case LINK_NEXTSLIDE: | ||||
title = "NEXT"; | |||||
label = "NEXT"; | |||||
address = "1,-1,NEXT"; | address = "1,-1,NEXT"; | ||||
break; | break; | ||||
case LINK_PREVIOUSSLIDE: | case LINK_PREVIOUSSLIDE: | ||||
title = "PREV"; | |||||
label = "PREV"; | |||||
address = "1,-1,PREV"; | address = "1,-1,PREV"; | ||||
break; | break; | ||||
case LINK_FIRSTSLIDE: | case LINK_FIRSTSLIDE: | ||||
title = "FIRST"; | |||||
label = "FIRST"; | |||||
address = "1,-1,FIRST"; | address = "1,-1,FIRST"; | ||||
break; | break; | ||||
case LINK_LASTSLIDE: | case LINK_LASTSLIDE: | ||||
title = "LAST"; | |||||
label = "LAST"; | |||||
address = "1,-1,LAST"; | address = "1,-1,LAST"; | ||||
break; | break; | ||||
case LINK_SLIDENUMBER: | case LINK_SLIDENUMBER: | ||||
break; | break; | ||||
default: | default: | ||||
title = ""; | |||||
label = ""; | |||||
address = ""; | address = ""; | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
/** | |||||
* Gets the hyperlink URL | |||||
* | |||||
* @return the hyperlink URL | |||||
*/ | |||||
@Override | |||||
public String getAddress() { | public String getAddress() { | ||||
return address; | return address; | ||||
} | } | ||||
public void setAddress(HSLFSlide slide) { | public void setAddress(HSLFSlide slide) { | ||||
String href = slide._getSheetNumber() + ","+slide.getSlideNumber()+",Slide " + slide.getSlideNumber(); | String href = slide._getSheetNumber() + ","+slide.getSlideNumber()+",Slide " + slide.getSlideNumber(); | ||||
setAddress(href);; | setAddress(href);; | ||||
setTitle("Slide " + slide.getSlideNumber()); | |||||
setLabel("Slide " + slide.getSlideNumber()); | |||||
setType(HSLFHyperlink.LINK_SLIDENUMBER); | setType(HSLFHyperlink.LINK_SLIDENUMBER); | ||||
} | } | ||||
@Override | |||||
public void setAddress(String str) { | public void setAddress(String str) { | ||||
address = str; | address = str; | ||||
} | } | ||||
this.id = id; | this.id = id; | ||||
} | } | ||||
/** | |||||
* Gets the hyperlink user-friendly title (if different from URL) | |||||
* | |||||
* @return the hyperlink user-friendly title | |||||
*/ | |||||
public String getTitle() { | |||||
return title; | |||||
@Override | |||||
public String getLabel() { | |||||
return label; | |||||
} | } | ||||
public void setTitle(String str) { | |||||
title = str; | |||||
@Override | |||||
public void setLabel(String str) { | |||||
label = str; | |||||
} | } | ||||
/** | /** | ||||
return startIndex; | return startIndex; | ||||
} | } | ||||
/** | |||||
* Sets the beginning character position | |||||
* | |||||
* @param startIndex the beginning character position | |||||
*/ | |||||
public void setStartIndex(int startIndex) { | |||||
this.startIndex = startIndex; | |||||
} | |||||
/** | /** | ||||
* Gets the ending character position | * Gets the ending character position | ||||
* | * | ||||
return endIndex; | return endIndex; | ||||
} | } | ||||
/** | |||||
* Sets the ending character position | |||||
* | |||||
* @param endIndex the ending character position | |||||
*/ | |||||
public void setEndIndex(int endIndex) { | |||||
this.endIndex = endIndex; | |||||
} | |||||
/** | /** | ||||
* Find hyperlinks in a text shape | * Find hyperlinks in a text shape | ||||
* | * | ||||
} | } | ||||
HSLFHyperlink link = new HSLFHyperlink(); | HSLFHyperlink link = new HSLFHyperlink(); | ||||
link.title = linkRecord.getLinkTitle(); | |||||
link.address = linkRecord.getLinkURL(); | |||||
link.type = info.getAction(); | |||||
link.setId(id); | |||||
link.setType(info.getAction()); | |||||
link.setLabel(linkRecord.getLinkTitle()); | |||||
link.setAddress(linkRecord.getLinkURL()); | |||||
out.add(link); | out.add(link); | ||||
if (iter.hasNext()) { | if (iter.hasNext()) { | ||||
continue; | continue; | ||||
} | } | ||||
TxInteractiveInfoAtom txinfo = (TxInteractiveInfoAtom)r; | TxInteractiveInfoAtom txinfo = (TxInteractiveInfoAtom)r; | ||||
link.startIndex = txinfo.getStartIndex(); | |||||
link.endIndex = txinfo.getEndIndex(); | |||||
link.setStartIndex(txinfo.getStartIndex()); | |||||
link.setEndIndex(txinfo.getEndIndex()); | |||||
} | } | ||||
} | } | ||||
} | } |
if (_paragraphs.isEmpty()) { | if (_paragraphs.isEmpty()) { | ||||
logger.log(POILogger.WARN, "No text records found for notes sheet"); | logger.log(POILogger.WARN, "No text records found for notes sheet"); | ||||
} | } | ||||
// Set the sheet on each TextRun | |||||
for (List<HSLFTextParagraph> ltp : _paragraphs) { | |||||
for (HSLFTextParagraph tp : ltp) { | |||||
tp.supplySheet(this); | |||||
} | |||||
} | |||||
} | } | ||||
/** | /** |
List<List<HSLFTextParagraph>> trs = getTextParagraphs(); | List<List<HSLFTextParagraph>> trs = getTextParagraphs(); | ||||
if (trs == null) return; | if (trs == null) return; | ||||
for (List<HSLFTextParagraph> ltp : trs) { | for (List<HSLFTextParagraph> ltp : trs) { | ||||
for (HSLFTextParagraph tp : ltp) { | |||||
tp.supplySheet(this); | |||||
} | |||||
HSLFTextParagraph.supplySheet(ltp, this); | |||||
} | } | ||||
} | } | ||||
infoAtom.setHyperlinkType(InteractiveInfoAtom.LINK_SlideNumber); | infoAtom.setHyperlinkType(InteractiveInfoAtom.LINK_SlideNumber); | ||||
break; | break; | ||||
default: | default: | ||||
logger.log(POILogger.WARN, "Ignore unknown hyperlink type : "+link.getTitle()); | |||||
logger.log(POILogger.WARN, "Ignore unknown hyperlink type : "+link.getLabel()); | |||||
break; | break; | ||||
} | } | ||||
for (List<HSLFTextParagraph> l : HSLFTextParagraph.findTextParagraphs(getPPDrawing(), this)) { | for (List<HSLFTextParagraph> l : HSLFTextParagraph.findTextParagraphs(getPPDrawing(), this)) { | ||||
if (!_paragraphs.contains(l)) _paragraphs.add(l); | if (!_paragraphs.contains(l)) _paragraphs.add(l); | ||||
} | } | ||||
for(List<HSLFTextParagraph> ltp : _paragraphs) { | |||||
for (HSLFTextParagraph tp : ltp) { | |||||
tp.supplySheet(this); | |||||
} | |||||
} | |||||
} | } | ||||
/** | /** |
for (List<HSLFTextParagraph> l : HSLFTextParagraph.findTextParagraphs(getPPDrawing(), this)) { | for (List<HSLFTextParagraph> l : HSLFTextParagraph.findTextParagraphs(getPPDrawing(), this)) { | ||||
if (!_paragraphs.contains(l)) _paragraphs.add(l); | if (!_paragraphs.contains(l)) _paragraphs.add(l); | ||||
} | } | ||||
for (List<HSLFTextParagraph> p : _paragraphs) { | |||||
for (HSLFTextParagraph htp : p) { | |||||
htp.supplySheet(this); | |||||
} | |||||
} | |||||
} | } | ||||
/** | /** |
import java.util.List; | import java.util.List; | ||||
import java.util.Map; | import java.util.Map; | ||||
import org.apache.poi.hslf.record.MainMaster; | |||||
import org.apache.poi.ddf.EscherBSERecord; | import org.apache.poi.ddf.EscherBSERecord; | ||||
import org.apache.poi.ddf.EscherContainerRecord; | import org.apache.poi.ddf.EscherContainerRecord; | ||||
import org.apache.poi.ddf.EscherOptRecord; | import org.apache.poi.ddf.EscherOptRecord; | ||||
import org.apache.poi.hslf.record.FontCollection; | import org.apache.poi.hslf.record.FontCollection; | ||||
import org.apache.poi.hslf.record.FontEntityAtom; | import org.apache.poi.hslf.record.FontEntityAtom; | ||||
import org.apache.poi.hslf.record.HeadersFootersContainer; | import org.apache.poi.hslf.record.HeadersFootersContainer; | ||||
import org.apache.poi.hslf.record.Notes; | |||||
import org.apache.poi.hslf.record.PersistPtrHolder; | import org.apache.poi.hslf.record.PersistPtrHolder; | ||||
import org.apache.poi.hslf.record.PositionDependentRecord; | import org.apache.poi.hslf.record.PositionDependentRecord; | ||||
import org.apache.poi.hslf.record.PositionDependentRecordContainer; | import org.apache.poi.hslf.record.PositionDependentRecordContainer; | ||||
import org.apache.poi.hslf.record.Record; | import org.apache.poi.hslf.record.Record; | ||||
import org.apache.poi.hslf.record.RecordContainer; | import org.apache.poi.hslf.record.RecordContainer; | ||||
import org.apache.poi.hslf.record.RecordTypes; | import org.apache.poi.hslf.record.RecordTypes; | ||||
import org.apache.poi.hslf.record.Slide; | |||||
import org.apache.poi.hslf.record.SlideListWithText; | import org.apache.poi.hslf.record.SlideListWithText; | ||||
import org.apache.poi.hslf.record.SlideListWithText.SlideAtomsSet; | import org.apache.poi.hslf.record.SlideListWithText.SlideAtomsSet; | ||||
import org.apache.poi.hslf.record.SlidePersistAtom; | import org.apache.poi.hslf.record.SlidePersistAtom; | ||||
private POILogger logger = POILogFactory.getLogger(this.getClass()); | private POILogger logger = POILogFactory.getLogger(this.getClass()); | ||||
/* =============================================================== | |||||
* Setup Code | |||||
* =============================================================== | |||||
*/ | |||||
/** | /** | ||||
* Constructs a Powerpoint document from the underlying | * Constructs a Powerpoint document from the underlying | ||||
* HSLFSlideShow object. Finds the model stuff from this | * HSLFSlideShow object. Finds the model stuff from this | ||||
// We always use the latest versions of these records, and use the | // We always use the latest versions of these records, and use the | ||||
// SlideAtom/NotesAtom to match them with the StyleAtomSet | // SlideAtom/NotesAtom to match them with the StyleAtomSet | ||||
SlideListWithText masterSLWT = _documentRecord.getMasterSlideListWithText(); | |||||
SlideListWithText slidesSLWT = _documentRecord.getSlideSlideListWithText(); | |||||
SlideListWithText notesSLWT = _documentRecord.getNotesSlideListWithText(); | |||||
// Find master slides | |||||
// These can be MainMaster records, but oddly they can also be | |||||
// Slides or Notes, and possibly even other odd stuff.... | |||||
// About the only thing you can say is that the master details are in | |||||
// the first SLWT. | |||||
if (masterSLWT != null) { | |||||
for (SlideAtomsSet sas : masterSLWT.getSlideAtomsSets()) { | |||||
Record r = getCoreRecordForSAS(sas); | |||||
int sheetNo = sas.getSlidePersistAtom().getSlideIdentifier(); | |||||
if (r instanceof org.apache.poi.hslf.record.Slide) { | |||||
HSLFTitleMaster master = new HSLFTitleMaster((org.apache.poi.hslf.record.Slide) r, | |||||
sheetNo); | |||||
master.setSlideShow(this); | |||||
_titleMasters.add(master); | |||||
} else if (r instanceof org.apache.poi.hslf.record.MainMaster) { | |||||
HSLFSlideMaster master = new HSLFSlideMaster((org.apache.poi.hslf.record.MainMaster) r, | |||||
sheetNo); | |||||
master.setSlideShow(this); | |||||
_masters.add(master); | |||||
} | |||||
} | |||||
} | |||||
findMasterSlides(); | |||||
// Having sorted out the masters, that leaves the notes and slides | // Having sorted out the masters, that leaves the notes and slides | ||||
Map<Integer,Integer> slideIdToNotes = new HashMap<Integer,Integer>(); | |||||
// Start by finding the notes records to go with the entries in | |||||
// notesSLWT | |||||
org.apache.poi.hslf.record.Notes[] notesRecords; | |||||
Map<Integer,Integer> slideIdToNotes = new HashMap<Integer,Integer>(); | |||||
if (notesSLWT == null) { | |||||
// None | |||||
notesRecords = new org.apache.poi.hslf.record.Notes[0]; | |||||
} else { | |||||
// Match up the records and the SlideAtomSets | |||||
List<org.apache.poi.hslf.record.Notes> notesRecordsL = | |||||
new ArrayList<org.apache.poi.hslf.record.Notes>(); | |||||
int idx = -1; | |||||
for (SlideAtomsSet notesSet : notesSLWT.getSlideAtomsSets()) { | |||||
idx++; | |||||
// Get the right core record | |||||
Record r = getCoreRecordForSAS(notesSet); | |||||
SlidePersistAtom spa = notesSet.getSlidePersistAtom(); | |||||
String loggerLoc = "A Notes SlideAtomSet at "+idx+" said its record was at refID "+spa.getRefID(); | |||||
// Ensure it really is a notes record | |||||
if (r == null || r instanceof org.apache.poi.hslf.record.Notes) { | |||||
if (r == null) { | |||||
logger.log(POILogger.WARN, loggerLoc+", but that record didn't exist - record ignored."); | |||||
} | |||||
// we need to add also null-records, otherwise the index references to other existing | |||||
// don't work anymore | |||||
org.apache.poi.hslf.record.Notes notesRecord = (org.apache.poi.hslf.record.Notes) r; | |||||
notesRecordsL.add(notesRecord); | |||||
// Record the match between slide id and these notes | |||||
int slideId = spa.getSlideIdentifier(); | |||||
slideIdToNotes.put(slideId, idx); | |||||
} else { | |||||
logger.log(POILogger.ERROR, loggerLoc+", but that was actually a " + r); | |||||
} | |||||
} | |||||
notesRecords = new org.apache.poi.hslf.record.Notes[notesRecordsL.size()]; | |||||
notesRecords = notesRecordsL.toArray(notesRecords); | |||||
} | |||||
// Start by finding the notes records | |||||
findNotesSlides(slideIdToNotes); | |||||
// Now, do the same thing for our slides | // Now, do the same thing for our slides | ||||
org.apache.poi.hslf.record.Slide[] slidesRecords; | |||||
SlideAtomsSet[] slidesSets = new SlideAtomsSet[0]; | |||||
if (slidesSLWT == null) { | |||||
// None | |||||
slidesRecords = new org.apache.poi.hslf.record.Slide[0]; | |||||
} else { | |||||
// Match up the records and the SlideAtomSets | |||||
slidesSets = slidesSLWT.getSlideAtomsSets(); | |||||
slidesRecords = new org.apache.poi.hslf.record.Slide[slidesSets.length]; | |||||
for (int i = 0; i < slidesSets.length; i++) { | |||||
// Get the right core record | |||||
Record r = getCoreRecordForSAS(slidesSets[i]); | |||||
// Ensure it really is a slide record | |||||
if (r instanceof org.apache.poi.hslf.record.Slide) { | |||||
slidesRecords[i] = (org.apache.poi.hslf.record.Slide) r; | |||||
} else { | |||||
logger.log(POILogger.ERROR, "A Slide SlideAtomSet at " + i | |||||
+ " said its record was at refID " | |||||
+ slidesSets[i].getSlidePersistAtom().getRefID() | |||||
+ ", but that was actually a " + r); | |||||
} | |||||
} | |||||
} | |||||
findSlides(slideIdToNotes); | |||||
} | |||||
// Finally, generate model objects for everything | |||||
// Notes first | |||||
for (org.apache.poi.hslf.record.Notes n : notesRecords) { | |||||
HSLFNotes hn = null; | |||||
if (n != null) { | |||||
hn = new HSLFNotes(n); | |||||
hn.setSlideShow(this); | |||||
} | |||||
_notes.add(hn); | |||||
} | |||||
// Then slides | |||||
for (int i = 0; i < slidesRecords.length; i++) { | |||||
SlideAtomsSet sas = slidesSets[i]; | |||||
int slideIdentifier = sas.getSlidePersistAtom().getSlideIdentifier(); | |||||
// Do we have a notes for this? | |||||
HSLFNotes notes = null; | |||||
// Slide.SlideAtom.notesId references the corresponding notes slide. | |||||
// 0 if slide has no notes. | |||||
int noteId = slidesRecords[i].getSlideAtom().getNotesID(); | |||||
if (noteId != 0) { | |||||
Integer notesPos = slideIdToNotes.get(noteId); | |||||
if (notesPos != null) { | |||||
notes = _notes.get(notesPos); | |||||
} else { | |||||
logger.log(POILogger.ERROR, "Notes not found for noteId=" + noteId); | |||||
} | |||||
} | |||||
/** | |||||
* Find master slides | |||||
* These can be MainMaster records, but oddly they can also be | |||||
* Slides or Notes, and possibly even other odd stuff.... | |||||
* About the only thing you can say is that the master details are in the first SLWT. | |||||
*/ | |||||
private void findMasterSlides() { | |||||
SlideListWithText masterSLWT = _documentRecord.getMasterSlideListWithText(); | |||||
if (masterSLWT == null) { | |||||
return; | |||||
} | |||||
for (SlideAtomsSet sas : masterSLWT.getSlideAtomsSets()) { | |||||
Record r = getCoreRecordForSAS(sas); | |||||
int sheetNo = sas.getSlidePersistAtom().getSlideIdentifier(); | |||||
if (r instanceof Slide) { | |||||
HSLFTitleMaster master = new HSLFTitleMaster((Slide)r, sheetNo); | |||||
master.setSlideShow(this); | |||||
_titleMasters.add(master); | |||||
} else if (r instanceof MainMaster) { | |||||
HSLFSlideMaster master = new HSLFSlideMaster((MainMaster)r, sheetNo); | |||||
master.setSlideShow(this); | |||||
_masters.add(master); | |||||
} | |||||
} | |||||
} | |||||
private void findNotesSlides(Map<Integer,Integer> slideIdToNotes) { | |||||
SlideListWithText notesSLWT = _documentRecord.getNotesSlideListWithText(); | |||||
// Now, build our slide | |||||
HSLFSlide hs = new HSLFSlide(slidesRecords[i], notes, sas, slideIdentifier, (i + 1)); | |||||
hs.setSlideShow(this); | |||||
_slides.add(hs); | |||||
} | |||||
if (notesSLWT == null) { | |||||
return; | |||||
} | |||||
// Match up the records and the SlideAtomSets | |||||
int idx = -1; | |||||
for (SlideAtomsSet notesSet : notesSLWT.getSlideAtomsSets()) { | |||||
idx++; | |||||
// Get the right core record | |||||
Record r = getCoreRecordForSAS(notesSet); | |||||
SlidePersistAtom spa = notesSet.getSlidePersistAtom(); | |||||
String loggerLoc = "A Notes SlideAtomSet at "+idx+" said its record was at refID "+spa.getRefID(); | |||||
// we need to add null-records, otherwise the index references to other existing don't work anymore | |||||
if (r == null) { | |||||
logger.log(POILogger.WARN, loggerLoc+", but that record didn't exist - record ignored."); | |||||
continue; | |||||
} | |||||
// Ensure it really is a notes record | |||||
if (!(r instanceof Notes)) { | |||||
logger.log(POILogger.ERROR, loggerLoc+", but that was actually a " + r); | |||||
continue; | |||||
} | |||||
Notes notesRecord = (Notes) r; | |||||
// Record the match between slide id and these notes | |||||
int slideId = spa.getSlideIdentifier(); | |||||
slideIdToNotes.put(slideId, idx); | |||||
HSLFNotes hn = new HSLFNotes(notesRecord); | |||||
hn.setSlideShow(this); | |||||
_notes.add(hn); | |||||
} | |||||
} | } | ||||
private void findSlides(Map<Integer,Integer> slideIdToNotes) { | |||||
SlideListWithText slidesSLWT = _documentRecord.getSlideSlideListWithText(); | |||||
if (slidesSLWT == null) { | |||||
return; | |||||
} | |||||
// Match up the records and the SlideAtomSets | |||||
int idx = -1; | |||||
for (SlideAtomsSet sas : slidesSLWT.getSlideAtomsSets()) { | |||||
idx++; | |||||
// Get the right core record | |||||
SlidePersistAtom spa = sas.getSlidePersistAtom(); | |||||
Record r = getCoreRecordForSAS(sas); | |||||
// Ensure it really is a slide record | |||||
if (!(r instanceof Slide)) { | |||||
logger.log(POILogger.ERROR, "A Slide SlideAtomSet at " + idx | |||||
+ " said its record was at refID " | |||||
+ spa.getRefID() | |||||
+ ", but that was actually a " + r); | |||||
continue; | |||||
} | |||||
Slide slide = (Slide)r; | |||||
// Do we have a notes for this? | |||||
HSLFNotes notes = null; | |||||
// Slide.SlideAtom.notesId references the corresponding notes slide. | |||||
// 0 if slide has no notes. | |||||
int noteId = slide.getSlideAtom().getNotesID(); | |||||
if (noteId != 0) { | |||||
Integer notesPos = slideIdToNotes.get(noteId); | |||||
if (notesPos != null && 0 <= notesPos && notesPos < _notes.size()) { | |||||
notes = _notes.get(notesPos); | |||||
} else { | |||||
logger.log(POILogger.ERROR, "Notes not found for noteId=" + noteId); | |||||
} | |||||
} | |||||
// Now, build our slide | |||||
int slideIdentifier = spa.getSlideIdentifier(); | |||||
HSLFSlide hs = new HSLFSlide(slide, notes, sas, slideIdentifier, (idx + 1)); | |||||
hs.setSlideShow(this); | |||||
_slides.add(hs); | |||||
} | |||||
} | |||||
@Override | @Override | ||||
public void write(OutputStream out) throws IOException { | public void write(OutputStream out) throws IOException { | ||||
// check for text paragraph modifications | // check for text paragraph modifications | ||||
} | } | ||||
} | } | ||||
/* | |||||
* =============================================================== | |||||
* Accessor Code | |||||
* =============================================================== | |||||
*/ | |||||
/** | /** | ||||
* Returns an array of the most recent version of all the interesting | * Returns an array of the most recent version of all the interesting | ||||
* records | * records | ||||
return _documentRecord; | return _documentRecord; | ||||
} | } | ||||
/* | |||||
* =============================================================== | |||||
* Re-ordering Code | |||||
* =============================================================== | |||||
*/ | |||||
/** | /** | ||||
* Re-orders a slide, to a new position. | * Re-orders a slide, to a new position. | ||||
* | * | ||||
return removedSlide; | return removedSlide; | ||||
} | } | ||||
/* | |||||
* =============================================================== | |||||
* Addition Code | |||||
* =============================================================== | |||||
*/ | |||||
/** | /** | ||||
* Create a blank <code>Slide</code>. | * Create a blank <code>Slide</code>. | ||||
* | * | ||||
+ " and identifier " + sp.getSlideIdentifier()); | + " and identifier " + sp.getSlideIdentifier()); | ||||
// Add the core records for this new Slide to the record tree | // Add the core records for this new Slide to the record tree | ||||
org.apache.poi.hslf.record.Slide slideRecord = slide.getSlideRecord(); | |||||
Slide slideRecord = slide.getSlideRecord(); | |||||
int psrId = addPersistentObject(slideRecord); | int psrId = addPersistentObject(slideRecord); | ||||
sp.setRefID(psrId); | sp.setRefID(psrId); | ||||
slideRecord.setSheetId(psrId); | slideRecord.setSheetId(psrId); | ||||
} else { | } else { | ||||
ctrl.setLinkURL(link.getAddress()); | ctrl.setLinkURL(link.getAddress()); | ||||
} | } | ||||
ctrl.setLinkTitle(link.getTitle()); | |||||
ctrl.setLinkTitle(link.getLabel()); | |||||
int objectId = addToObjListAtom(ctrl); | int objectId = addToObjListAtom(ctrl); | ||||
link.setId(objectId); | link.setId(objectId); |
private StyleTextProp9Atom styleTextProp9Atom; | private StyleTextProp9Atom styleTextProp9Atom; | ||||
private boolean _dirty = false; | private boolean _dirty = false; | ||||
private final List<HSLFTextParagraph> parentList; | |||||
/** | /** | ||||
* Constructs a Text Run from a Unicode text block. | * Constructs a Text Run from a Unicode text block. | ||||
* @param tha the TextHeaderAtom that defines what's what | * @param tha the TextHeaderAtom that defines what's what | ||||
* @param tba the TextBytesAtom containing the text or null if {@link TextCharsAtom} is provided | * @param tba the TextBytesAtom containing the text or null if {@link TextCharsAtom} is provided | ||||
* @param tca the TextCharsAtom containing the text or null if {@link TextBytesAtom} is provided | * @param tca the TextCharsAtom containing the text or null if {@link TextBytesAtom} is provided | ||||
* @param parentList the list which contains this paragraph | |||||
*/ | */ | ||||
/* package */ HSLFTextParagraph( | /* package */ HSLFTextParagraph( | ||||
TextHeaderAtom tha, | TextHeaderAtom tha, | ||||
TextBytesAtom tba, | TextBytesAtom tba, | ||||
TextCharsAtom tca | |||||
TextCharsAtom tca, | |||||
List<HSLFTextParagraph> parentList | |||||
) { | ) { | ||||
if (tha == null) { | if (tha == null) { | ||||
throw new IllegalArgumentException("TextHeaderAtom must be set."); | throw new IllegalArgumentException("TextHeaderAtom must be set."); | ||||
_headerAtom = tha; | _headerAtom = tha; | ||||
_byteAtom = tba; | _byteAtom = tba; | ||||
_charAtom = tca; | _charAtom = tca; | ||||
this.parentList = parentList; | |||||
} | } | ||||
/* package */HSLFTextParagraph(HSLFTextParagraph other) { | /* package */HSLFTextParagraph(HSLFTextParagraph other) { | ||||
_ruler = other._ruler; | _ruler = other._ruler; | ||||
shapeId = other.shapeId; | shapeId = other.shapeId; | ||||
_paragraphStyle.copy(other._paragraphStyle); | _paragraphStyle.copy(other._paragraphStyle); | ||||
parentList = other.parentList; | |||||
} | } | ||||
public void addTextRun(HSLFTextRun run) { | public void addTextRun(HSLFTextRun run) { | ||||
* Supply the Sheet we belong to, which might have an assigned SlideShow | * Supply the Sheet we belong to, which might have an assigned SlideShow | ||||
* Also passes it on to our child RichTextRuns | * Also passes it on to our child RichTextRuns | ||||
*/ | */ | ||||
public void supplySheet(HSLFSheet sheet) { | |||||
public static void supplySheet(List<HSLFTextParagraph> paragraphs, HSLFSheet sheet) { | |||||
if (paragraphs == null) { | |||||
return; | |||||
} | |||||
for (HSLFTextParagraph p : paragraphs) { | |||||
p.supplySheet(sheet); | |||||
} | |||||
assert(sheet.getSlideShow() != null); | |||||
applyHyperlinks(paragraphs); | |||||
} | |||||
/** | |||||
* Supply the Sheet we belong to, which might have an assigned SlideShow | |||||
* Also passes it on to our child RichTextRuns | |||||
*/ | |||||
private void supplySheet(HSLFSheet sheet) { | |||||
this._sheet = sheet; | this._sheet = sheet; | ||||
if (_runs == null) return; | if (_runs == null) return; | ||||
if (addParagraph && !lastParaEmpty) { | if (addParagraph && !lastParaEmpty) { | ||||
TextPropCollection tpc = htp.getParagraphStyle(); | TextPropCollection tpc = htp.getParagraphStyle(); | ||||
HSLFTextParagraph prevHtp = htp; | HSLFTextParagraph prevHtp = htp; | ||||
htp = new HSLFTextParagraph(htp._headerAtom, htp._byteAtom, htp._charAtom); | |||||
htp = new HSLFTextParagraph(htp._headerAtom, htp._byteAtom, htp._charAtom, paragraphs); | |||||
htp.getParagraphStyle().copy(tpc); | htp.getParagraphStyle().copy(tpc); | ||||
htp.setParentShape(prevHtp.getParentShape()); | htp.setParentShape(prevHtp.getParentShape()); | ||||
htp.setShapeId(prevHtp.getShapeId()); | htp.setShapeId(prevHtp.getShapeId()); | ||||
// split, but keep delimiter | // split, but keep delimiter | ||||
for (String para : rawText.split("(?<=\r)")) { | for (String para : rawText.split("(?<=\r)")) { | ||||
HSLFTextParagraph tpara = new HSLFTextParagraph(header, tbytes, tchars); | |||||
HSLFTextParagraph tpara = new HSLFTextParagraph(header, tbytes, tchars, paragraphs); | |||||
paragraphs.add(tpara); | paragraphs.add(tpara); | ||||
tpara._ruler = ruler; | tpara._ruler = ruler; | ||||
tpara.getParagraphStyle().updateTextSize(para.length()); | tpara.getParagraphStyle().updateTextSize(para.length()); | ||||
return paragraphCollection; | return paragraphCollection; | ||||
} | } | ||||
protected static void applyHyperlinks(List<HSLFTextParagraph> paragraphs) { | |||||
List<HSLFHyperlink> links = HSLFHyperlink.find(paragraphs); | |||||
for (HSLFHyperlink h : links) { | |||||
int csIdx = 0; | |||||
for (HSLFTextParagraph p : paragraphs) { | |||||
for (HSLFTextRun r : p) { | |||||
if (h.getStartIndex() <= csIdx && csIdx < h.getEndIndex()) { | |||||
r.setHyperlinkId(h.getId()); | |||||
} | |||||
csIdx += r.getLength(); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
protected static void applyCharacterStyles(List<HSLFTextParagraph> paragraphs, List<TextPropCollection> charStyles) { | protected static void applyCharacterStyles(List<HSLFTextParagraph> paragraphs, List<TextPropCollection> charStyles) { | ||||
int paraIdx = 0, runIdx = 0; | int paraIdx = 0, runIdx = 0; | ||||
HSLFTextRun trun; | HSLFTextRun trun; | ||||
TextPropCollection charStyle = sta.addCharacterTextPropCollection(1); | TextPropCollection charStyle = sta.addCharacterTextPropCollection(1); | ||||
wrapper.appendChildRecord(sta); | wrapper.appendChildRecord(sta); | ||||
HSLFTextParagraph htp = new HSLFTextParagraph(tha, tba, null); | |||||
List<HSLFTextParagraph> paragraphs = new ArrayList<HSLFTextParagraph>(1); | |||||
HSLFTextParagraph htp = new HSLFTextParagraph(tha, tba, null, paragraphs); | |||||
htp.setParagraphStyle(paraStyle); | htp.setParagraphStyle(paraStyle); | ||||
paragraphs.add(htp); | |||||
HSLFTextRun htr = new HSLFTextRun(htp); | HSLFTextRun htr = new HSLFTextRun(htp); | ||||
htr.setCharacterStyle(charStyle); | htr.setCharacterStyle(charStyle); | ||||
htr.setText(""); | htr.setText(""); | ||||
htp.addTextRun(htr); | htp.addTextRun(htr); | ||||
return Arrays.asList(htp); | |||||
return paragraphs; | |||||
} | } | ||||
public EscherTextboxWrapper getTextboxWrapper() { | public EscherTextboxWrapper getTextboxWrapper() { | ||||
public boolean isDirty() { | public boolean isDirty() { | ||||
return _dirty; | return _dirty; | ||||
} | } | ||||
/** | |||||
* Calculates the start index of the given text run | |||||
* | |||||
* @param textrun the text run to search for | |||||
* @return the start index with the paragraph collection or -1 if not found | |||||
*/ | |||||
/* package */ int getStartIdxOfTextRun(HSLFTextRun textrun) { | |||||
int idx = 0; | |||||
for (HSLFTextParagraph p : parentList) { | |||||
for (HSLFTextRun r : p) { | |||||
if (r == textrun) { | |||||
return idx; | |||||
} | |||||
idx += r.getLength(); | |||||
} | |||||
} | |||||
return -1; | |||||
} | |||||
} | } |
import org.apache.poi.hslf.model.textproperties.TextProp; | import org.apache.poi.hslf.model.textproperties.TextProp; | ||||
import org.apache.poi.hslf.model.textproperties.TextPropCollection; | import org.apache.poi.hslf.model.textproperties.TextPropCollection; | ||||
import org.apache.poi.hslf.model.textproperties.TextPropCollection.TextPropType; | import org.apache.poi.hslf.model.textproperties.TextPropCollection.TextPropType; | ||||
import org.apache.poi.hslf.record.ExHyperlink; | |||||
import org.apache.poi.hslf.record.InteractiveInfo; | |||||
import org.apache.poi.hslf.record.InteractiveInfoAtom; | |||||
import org.apache.poi.hslf.record.Record; | |||||
import org.apache.poi.sl.draw.DrawPaint; | import org.apache.poi.sl.draw.DrawPaint; | ||||
import org.apache.poi.sl.usermodel.PaintStyle; | import org.apache.poi.sl.usermodel.PaintStyle; | ||||
import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint; | import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint; | ||||
private HSLFTextParagraph parentParagraph; | private HSLFTextParagraph parentParagraph; | ||||
private String _runText = ""; | private String _runText = ""; | ||||
private String _fontFamily; | private String _fontFamily; | ||||
private int hyperlinkId = -1; | |||||
/** | /** | ||||
* Our paragraph and character style. | * Our paragraph and character style. | ||||
public byte getPitchAndFamily() { | public byte getPitchAndFamily() { | ||||
return 0; | return 0; | ||||
} | } | ||||
/** | |||||
* Sets the associated hyperlink id - currently this is only used while parsing and | |||||
* can't be used for update a ppt | |||||
* | |||||
* @param hyperlink the id or -1 to unset it | |||||
*/ | |||||
public void setHyperlinkId(int hyperlinkId) { | |||||
this.hyperlinkId = hyperlinkId; | |||||
} | |||||
/** | |||||
* Returns the associated hyperlink id | |||||
* | |||||
* @return the hyperlink id | |||||
*/ | |||||
public int getHyperlinkId() { | |||||
return hyperlinkId; | |||||
} | |||||
@Override | |||||
public HSLFHyperlink getHyperlink() { | |||||
if (hyperlinkId == -1) { | |||||
return null; | |||||
} | |||||
ExHyperlink linkRecord = parentParagraph.getSheet().getSlideShow().getDocumentRecord().getExObjList().get(hyperlinkId); | |||||
if (linkRecord == null) { | |||||
return null; | |||||
} | |||||
InteractiveInfoAtom info = null; | |||||
for (Record r : parentParagraph.getRecords()) { | |||||
if (r instanceof InteractiveInfo) { | |||||
InteractiveInfo ii = (InteractiveInfo)r; | |||||
InteractiveInfoAtom iia = ii.getInteractiveInfoAtom(); | |||||
if (iia.getHyperlinkID() == hyperlinkId) { | |||||
info = iia; | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
if (info == null) { | |||||
return null; | |||||
} | |||||
// TODO: check previous/next sibling runs for same hyperlink id and return the whole string length | |||||
int startIdx = parentParagraph.getStartIdxOfTextRun(this); | |||||
HSLFHyperlink link = new HSLFHyperlink(); | |||||
link.setId(hyperlinkId); | |||||
link.setType(info.getAction()); | |||||
link.setLabel(linkRecord.getLinkTitle()); | |||||
link.setAddress(linkRecord.getLinkURL()); | |||||
link.setStartIndex(startIdx); | |||||
link.setEndIndex(startIdx+getLength()); | |||||
return link; | |||||
} | |||||
} | } |
// (We can't do it in the constructor because the sheet | // (We can't do it in the constructor because the sheet | ||||
// is not assigned then, it's only built once we have | // is not assigned then, it's only built once we have | ||||
// all the records) | // all the records) | ||||
List<HSLFTextParagraph> paras = getTextParagraphs(); | |||||
if (paras != null) { | |||||
for (HSLFTextParagraph htp : paras) { | |||||
// Supply the sheet to our child RichTextRuns | |||||
htp.supplySheet(_sheet); | |||||
} | |||||
} | |||||
List<HSLFTextParagraph> ltp = getTextParagraphs(); | |||||
HSLFTextParagraph.supplySheet(ltp, sheet); | |||||
} | } | ||||
/** | /** |
assertNotNull(links); | assertNotNull(links); | ||||
assertEquals(2, links.size()); | assertEquals(2, links.size()); | ||||
assertEquals("http://jakarta.apache.org/poi/", links.get(0).getTitle()); | |||||
assertEquals("http://jakarta.apache.org/poi/", links.get(0).getLabel()); | |||||
assertEquals("http://jakarta.apache.org/poi/", links.get(0).getAddress()); | assertEquals("http://jakarta.apache.org/poi/", links.get(0).getAddress()); | ||||
assertEquals("http://jakarta.apache.org/poi/", rawText.substring(links.get(0).getStartIndex(), links.get(0).getEndIndex()-1)); | assertEquals("http://jakarta.apache.org/poi/", rawText.substring(links.get(0).getStartIndex(), links.get(0).getEndIndex()-1)); | ||||
assertEquals("http://slashdot.org/", links.get(1).getTitle()); | |||||
assertEquals("http://slashdot.org/", links.get(1).getLabel()); | |||||
assertEquals("http://slashdot.org/", links.get(1).getAddress()); | assertEquals("http://slashdot.org/", links.get(1).getAddress()); | ||||
assertEquals("http://slashdot.org/", rawText.substring(links.get(1).getStartIndex(), links.get(1).getEndIndex()-1)); | assertEquals("http://slashdot.org/", rawText.substring(links.get(1).getStartIndex(), links.get(1).getEndIndex()-1)); | ||||
assertNotNull(links); | assertNotNull(links); | ||||
assertEquals(1, links.size()); | assertEquals(1, links.size()); | ||||
assertEquals("http://jakarta.apache.org/poi/hssf/", links.get(0).getTitle()); | |||||
assertEquals("Open Jakarta POI HSSF module test ", links.get(0).getLabel()); | |||||
assertEquals("http://jakarta.apache.org/poi/hssf/", links.get(0).getAddress()); | assertEquals("http://jakarta.apache.org/poi/hssf/", links.get(0).getAddress()); | ||||
assertEquals("Jakarta HSSF", rawText.substring(links.get(0).getStartIndex(), links.get(0).getEndIndex()-1)); | assertEquals("Jakarta HSSF", rawText.substring(links.get(0).getStartIndex(), links.get(0).getEndIndex()-1)); | ||||
} | } |
import java.awt.Color; | import java.awt.Color; | ||||
import java.io.File; | import java.io.File; | ||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.io.OutputStream; | |||||
import java.io.PrintStream; | |||||
import java.text.AttributedCharacterIterator; | |||||
import java.text.AttributedCharacterIterator.Attribute; | |||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.Date; | import java.util.Date; | ||||
import java.util.HashMap; | import java.util.HashMap; | ||||
import org.apache.poi.hslf.record.SlideListWithText; | import org.apache.poi.hslf.record.SlideListWithText; | ||||
import org.apache.poi.hslf.record.SlideListWithText.SlideAtomsSet; | import org.apache.poi.hslf.record.SlideListWithText.SlideAtomsSet; | ||||
import org.apache.poi.hslf.record.TextHeaderAtom; | import org.apache.poi.hslf.record.TextHeaderAtom; | ||||
import org.apache.poi.hssf.usermodel.DummyGraphics2d; | |||||
import org.apache.poi.sl.draw.DrawPaint; | import org.apache.poi.sl.draw.DrawPaint; | ||||
import org.apache.poi.sl.draw.DrawTextParagraph; | |||||
import org.apache.poi.sl.usermodel.PaintStyle; | import org.apache.poi.sl.usermodel.PaintStyle; | ||||
import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint; | import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint; | ||||
import org.apache.poi.sl.usermodel.PictureData.PictureType; | import org.apache.poi.sl.usermodel.PictureData.PictureType; | ||||
/** | /** | ||||
* Testcases for bugs entered in bugzilla | * Testcases for bugs entered in bugzilla | ||||
* the Test name contains the bugzilla bug id | * the Test name contains the bugzilla bug id | ||||
* | |||||
* @author Yegor Kozlov | |||||
*/ | */ | ||||
public final class TestBugs { | public final class TestBugs { | ||||
/** | /** | ||||
ppt.close(); | ppt.close(); | ||||
} | } | ||||
@Test | |||||
public void bug57796() throws IOException { | |||||
HSLFSlideShow ppt = open("WithLinks.ppt"); | |||||
HSLFSlide slide = ppt.getSlides().get(0); | |||||
HSLFTextShape shape = (HSLFTextShape)slide.getShapes().get(1); | |||||
List<HSLFHyperlink> hlList = HSLFHyperlink.find(shape); | |||||
HSLFHyperlink hlShape = hlList.get(0); | |||||
HSLFTextRun r = shape.getTextParagraphs().get(1).getTextRuns().get(0); | |||||
HSLFHyperlink hlRun = r.getHyperlink(); | |||||
assertEquals(hlRun.getId(), hlShape.getId()); | |||||
assertEquals(hlRun.getAddress(), hlShape.getAddress()); | |||||
assertEquals(hlRun.getLabel(), hlShape.getLabel()); | |||||
assertEquals(hlRun.getType(), hlShape.getType()); | |||||
assertEquals(hlRun.getStartIndex(), hlShape.getStartIndex()); | |||||
assertEquals(hlRun.getEndIndex(), hlShape.getEndIndex()); | |||||
OutputStream nullOutput = new OutputStream(){ | |||||
public void write(int b) throws IOException {} | |||||
}; | |||||
final boolean found[] = { false }; | |||||
DummyGraphics2d dgfx = new DummyGraphics2d(new PrintStream(nullOutput)){ | |||||
public void drawString(AttributedCharacterIterator iterator, float x, float y) { | |||||
// For the test file, common sl draws textruns one by one and not mixed | |||||
// so we evaluate the whole iterator | |||||
Map<Attribute, Object> attributes = null; | |||||
StringBuffer sb = new StringBuffer(); | |||||
for (char c = iterator.first(); | |||||
c != AttributedCharacterIterator.DONE; | |||||
c = iterator.next()) { | |||||
sb.append(c); | |||||
attributes = iterator.getAttributes(); | |||||
} | |||||
if ("Jakarta HSSF".equals(sb.toString())) { | |||||
// this is a test for a manually modified ppt, for real hyperlink label | |||||
// one would need to access the screen tip record | |||||
String href = (String)attributes.get(DrawTextParagraph.HYPERLINK_HREF); | |||||
String label = (String)attributes.get(DrawTextParagraph.HYPERLINK_LABEL); | |||||
assertEquals("http://jakarta.apache.org/poi/hssf/", href); | |||||
assertEquals("Open Jakarta POI HSSF module test ", label); | |||||
found[0] = true; | |||||
} | |||||
} | |||||
}; | |||||
ppt.getSlides().get(1).draw(dgfx); | |||||
assertTrue(found[0]); | |||||
ppt.close(); | |||||
} | |||||
private static HSLFSlideShow open(String fileName) throws IOException { | private static HSLFSlideShow open(String fileName) throws IOException { | ||||
File sample = HSLFTestDataSamples.getSampleFile(fileName); | File sample = HSLFTestDataSamples.getSampleFile(fileName); |