String text = textBox1.getText();
HSLFHyperlink link = new HSLFHyperlink();
link.setAddress("http://www.apache.org");
- link.setTitle(textBox1.getText());
+ link.setLabel(textBox1.getText());
int linkId = ppt.addHyperlink(link);
// apply link to the text
//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 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;\r
import java.awt.font.TextLayout;\r
import java.awt.geom.Rectangle2D;\r
+import java.io.InvalidObjectException;\r
import java.text.AttributedCharacterIterator;\r
import java.text.AttributedCharacterIterator.Attribute;\r
import java.text.AttributedString;\r
import java.util.Map;\r
\r
import org.apache.poi.sl.usermodel.AutoNumberingScheme;\r
+import org.apache.poi.sl.usermodel.Hyperlink;\r
import org.apache.poi.sl.usermodel.Insets2D;\r
import org.apache.poi.sl.usermodel.PaintStyle;\r
import org.apache.poi.sl.usermodel.PlaceableShape;\r
import org.apache.poi.util.Units;\r
\r
public class DrawTextParagraph implements Drawable {\r
+ /** Keys for passing hyperlinks to the graphics context */\r
+ public static final XlinkAttribute HYPERLINK_HREF = new XlinkAttribute("href");\r
+ public static final XlinkAttribute HYPERLINK_LABEL = new XlinkAttribute("label");\r
+\r
protected TextParagraph<?,?,?> paragraph;\r
double x, y;\r
protected List<DrawTextFragment> lines = new ArrayList<DrawTextFragment>();\r
*/\r
protected double maxLineHeight;\r
\r
+ /**\r
+ * Defines an attribute used for storing the hyperlink associated with\r
+ * some renderable text.\r
+ */\r
+ private static class XlinkAttribute extends Attribute {\r
+\r
+ XlinkAttribute(String name) {\r
+ super(name);\r
+ }\r
+\r
+ /**\r
+ * Resolves instances being deserialized to the predefined constants.\r
+ */\r
+ protected Object readResolve() throws InvalidObjectException {\r
+ if (HYPERLINK_HREF.getName().equals(getName())) {\r
+ return HYPERLINK_HREF;\r
+ }\r
+ if (HYPERLINK_LABEL.getName().equals(getName())) {\r
+ return HYPERLINK_LABEL;\r
+ }\r
+ throw new InvalidObjectException("unknown attribute name");\r
+ }\r
+ }\r
+\r
+\r
public DrawTextParagraph(TextParagraph<?,?,?> paragraph) {\r
this.paragraph = paragraph;\r
}\r
public void setAutoNumberingIdx(int index) {\r
autoNbrIdx = index;\r
}\r
- \r
+\r
public void draw(Graphics2D graphics){\r
if (lines.isEmpty()) return;\r
- \r
+\r
double penY = y;\r
\r
boolean firstLine = true;\r
// special handling for HSLF\r
indent -= leftMargin;\r
}\r
- \r
+\r
// Double rightMargin = paragraph.getRightMargin();\r
// if (rightMargin == null) {\r
// rightMargin = 0d;\r
//The vertical line spacing\r
Double spacing = paragraph.getLineSpacing();\r
if (spacing == null) spacing = 100d;\r
- \r
+\r
for(DrawTextFragment line : lines){\r
double penX;\r
\r
// TODO: find out character style for empty, but bulleted/numbered lines\r
bullet = getBullet(graphics, line.getAttributedString().getIterator());\r
}\r
- \r
+\r
if (bullet != null){\r
bullet.setPosition(x+leftMargin+indent, penY);\r
bullet.draw(graphics);\r
\r
y = penY - y;\r
}\r
- \r
+\r
public float getFirstLineHeight() {\r
return (lines.isEmpty()) ? 0 : lines.get(0).getHeight();\r
}\r
public boolean isEmptyParagraph() {\r
return (lines.isEmpty() || rawText.trim().isEmpty());\r
}\r
- \r
+\r
public void applyTransform(Graphics2D graphics) {\r
}\r
\r
String buFont = bulletStyle.getBulletFont();\r
if (buFont == null) buFont = paragraph.getDefaultFontFamily();\r
assert(buFont != null);\r
- \r
+\r
PlaceableShape<?,?> ps = getParagraphShape();\r
PaintStyle fgPaintStyle = bulletStyle.getBulletFontColor();\r
Paint fgPaint;\r
\r
float fontSize = (Float)firstLineAttr.getAttribute(TextAttribute.SIZE);\r
Double buSz = bulletStyle.getBulletFontSize();\r
- if (buSz == null) buSz = 100d; \r
+ if (buSz == null) buSz = 100d;\r
if (buSz > 0) fontSize *= buSz* 0.01;\r
else fontSize = (float)-buSz;\r
\r
- \r
+\r
AttributedString str = new AttributedString(mapFontCharset(buCharacter,buFont));\r
str.addAttribute(TextAttribute.FOREGROUND, fgPaint);\r
str.addAttribute(TextAttribute.FAMILY, buFont);\r
case SMALL: c = Character.toLowerCase(c); break;\r
case NONE: break;\r
}\r
- \r
+\r
buf.append(c);\r
break;\r
}\r
\r
return buf.toString();\r
}\r
- \r
+\r
/**\r
* Replace a tab with the effective number of white spaces.\r
*/\r
Double fs = tr.getFontSize();\r
if (fs == null) fs = 12d;\r
string.addAttribute(TextAttribute.SIZE, fs.floatValue());\r
- \r
+\r
TextLayout l = new TextLayout(string.getIterator(), new FontRenderContext(null, true, true));\r
double wspace = l.getAdvance();\r
\r
}\r
return buf.toString();\r
}\r
- \r
+\r
\r
/**\r
* Returns wrapping width to break lines in this paragraph\r
this.endIndex = endIndex;\r
}\r
}\r
- \r
+\r
/**\r
* Helper method for paint style relative to bounds, e.g. gradient paint\r
*/\r
if (text == null) text = new StringBuilder();\r
\r
PlaceableShape<?,?> ps = getParagraphShape();\r
- \r
+\r
DrawFontManager fontHandler = (DrawFontManager)graphics.getRenderingHint(Drawable.FONT_HANDLER);\r
\r
for (TextRun run : paragraph){\r
if (fontFamily == null) {\r
fontFamily = paragraph.getDefaultFontFamily();\r
}\r
- \r
+\r
int beginIndex = text.length();\r
text.append(mapFontCharset(runText,fontFamily));\r
int endIndex = text.length();\r
if(run.isSuperscript()) {\r
attList.add(new AttributedStringData(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUPER, beginIndex, endIndex));\r
}\r
+ \r
+ Hyperlink hl = run.getHyperlink();\r
+ if (hl != null) {\r
+ attList.add(new AttributedStringData(HYPERLINK_HREF, hl.getAddress(), beginIndex, endIndex));\r
+ attList.add(new AttributedStringData(HYPERLINK_LABEL, hl.getLabel(), beginIndex, endIndex));\r
+ }\r
}\r
\r
// ensure that the paragraph contains at least one character\r
}\r
\r
protected boolean isHSLF() {\r
- return paragraph.getClass().getName().contains("HSLF"); \r
+ return paragraph.getClass().getName().contains("HSLF");\r
}\r
- \r
+\r
/**\r
* Map text charset depending on font family.\r
* Currently this only maps for wingdings font (into unicode private use area)\r
* @param text the raw text\r
* @param fontFamily the font family\r
* @return AttributedString with mapped codepoints\r
- * \r
+ *\r
* @see <a href="http://stackoverflow.com/questions/8692095">Drawing exotic fonts in a java applet</a>\r
* @see StringUtil#mapMsCodepointString(String)\r
*/\r
* @return the pitch and family id or -1 if not applicable
*/
byte getPitchAndFamily();
+
+ /**
+ * Return the associated hyperlink
+ *
+ * @return the associated hyperlink or null if no hyperlink was set
+ */
+ Hyperlink getHyperlink();
}
\r
import org.apache.poi.openxml4j.opc.PackageRelationship;\r
import org.apache.poi.openxml4j.opc.TargetMode;\r
+import org.apache.poi.sl.usermodel.Hyperlink;\r
import org.apache.poi.util.Internal;\r
import org.openxmlformats.schemas.drawingml.x2006.main.CTHyperlink;\r
\r
import java.net.URI;\r
\r
-/**\r
- * @author Yegor Kozlov\r
- */\r
-public class XSLFHyperlink {\r
+public class XSLFHyperlink implements Hyperlink {\r
final XSLFTextRun _r;\r
final CTHyperlink _link;\r
\r
return _link;\r
}\r
\r
+ @Override\r
public void setAddress(String address){\r
XSLFSheet sheet = _r.getParentParagraph().getParentShape().getSheet();\r
PackageRelationship rel =\r
sheet.getPackagePart().\r
addExternalRelationship(address, XSLFRelation.HYPERLINK.getRelation());\r
_link.setId(rel.getId());\r
+ }\r
+ \r
+ @Override\r
+ public String getAddress() {\r
+ return getTargetURI().toASCIIString();\r
+ }\r
\r
+ @Override\r
+ public String getLabel() {\r
+ return _link.getTooltip();\r
+ }\r
+ \r
+ @Override\r
+ public void setLabel(String label) {\r
+ _link.setTooltip(label);\r
}\r
\r
+ @Override\r
+ public int getType() {\r
+ // TODO: currently this just returns nonsense\r
+ if ("ppaction://hlinksldjump".equals(_link.getAction())) {\r
+ return LINK_DOCUMENT;\r
+ }\r
+ return LINK_URL;\r
+ }\r
+ \r
public void setAddress(XSLFSlide slide){\r
XSLFSheet sheet = _r.getParentParagraph().getParentShape().getSheet();\r
PackageRelationship rel =\r
return link;\r
}\r
\r
+ @Override\r
public XSLFHyperlink getHyperlink(){\r
if(!_r.getRPr().isSetHlinkClick()) return null;\r
\r
*/
public void drawString(String s, float x, float y) {
HSLFTextBox txt = new HSLFTextBox(_group);
- txt.getTextParagraphs().get(0).supplySheet(_group.getSheet());
+ txt.setSheet(_group.getSheet());
txt.setText(s);
HSLFTextRun rt = txt.getTextParagraphs().get(0).getTextRuns().get(0);
import org.apache.poi.hslf.record.InteractiveInfoAtom;
import org.apache.poi.hslf.record.Record;
import org.apache.poi.hslf.record.TxInteractiveInfoAtom;
+import org.apache.poi.sl.usermodel.Hyperlink;
/**
* 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_PREVIOUSSLIDE = InteractiveInfoAtom.LINK_PreviousSlide;
public static final byte LINK_FIRSTSLIDE = InteractiveInfoAtom.LINK_FirstSlide;
private int id=-1;
private int type;
private String address;
- private String title;
+ private String label;
private int startIndex, endIndex;
/**
* @return the hyperlink URL
* @see InteractiveInfoAtom
*/
+ @Override
public int getType() {
return type;
}
type = val;
switch(type){
case LINK_NEXTSLIDE:
- title = "NEXT";
+ label = "NEXT";
address = "1,-1,NEXT";
break;
case LINK_PREVIOUSSLIDE:
- title = "PREV";
+ label = "PREV";
address = "1,-1,PREV";
break;
case LINK_FIRSTSLIDE:
- title = "FIRST";
+ label = "FIRST";
address = "1,-1,FIRST";
break;
case LINK_LASTSLIDE:
- title = "LAST";
+ label = "LAST";
address = "1,-1,LAST";
break;
case LINK_SLIDENUMBER:
break;
default:
- title = "";
+ label = "";
address = "";
break;
}
}
- /**
- * Gets the hyperlink URL
- *
- * @return the hyperlink URL
- */
+ @Override
public String getAddress() {
return address;
}
public void setAddress(HSLFSlide slide) {
String href = slide._getSheetNumber() + ","+slide.getSlideNumber()+",Slide " + slide.getSlideNumber();
setAddress(href);;
- setTitle("Slide " + slide.getSlideNumber());
+ setLabel("Slide " + slide.getSlideNumber());
setType(HSLFHyperlink.LINK_SLIDENUMBER);
}
+ @Override
public void setAddress(String str) {
address = str;
}
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;
}
+ /**
+ * Sets the beginning character position
+ *
+ * @param startIndex the beginning character position
+ */
+ public void setStartIndex(int startIndex) {
+ this.startIndex = startIndex;
+ }
+
/**
* Gets the ending character position
*
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
*
}
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);
if (iter.hasNext()) {
continue;
}
TxInteractiveInfoAtom txinfo = (TxInteractiveInfoAtom)r;
- link.startIndex = txinfo.getStartIndex();
- link.endIndex = txinfo.getEndIndex();
+ link.setStartIndex(txinfo.getStartIndex());
+ link.setEndIndex(txinfo.getEndIndex());
}
}
}
if (_paragraphs.isEmpty()) {
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();
if (trs == null) return;
for (List<HSLFTextParagraph> ltp : trs) {
- for (HSLFTextParagraph tp : ltp) {
- tp.supplySheet(this);
- }
+ HSLFTextParagraph.supplySheet(ltp, this);
}
}
infoAtom.setHyperlinkType(InteractiveInfoAtom.LINK_SlideNumber);
break;
default:
- logger.log(POILogger.WARN, "Ignore unknown hyperlink type : "+link.getTitle());
+ logger.log(POILogger.WARN, "Ignore unknown hyperlink type : "+link.getLabel());
break;
}
for (List<HSLFTextParagraph> l : HSLFTextParagraph.findTextParagraphs(getPPDrawing(), this)) {
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)) {
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.Map;
+import org.apache.poi.hslf.record.MainMaster;
import org.apache.poi.ddf.EscherBSERecord;
import org.apache.poi.ddf.EscherContainerRecord;
import org.apache.poi.ddf.EscherOptRecord;
import org.apache.poi.hslf.record.FontCollection;
import org.apache.poi.hslf.record.FontEntityAtom;
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.PositionDependentRecord;
import org.apache.poi.hslf.record.PositionDependentRecordContainer;
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.Slide;
import org.apache.poi.hslf.record.SlideListWithText;
import org.apache.poi.hslf.record.SlideListWithText.SlideAtomsSet;
import org.apache.poi.hslf.record.SlidePersistAtom;
private POILogger logger = POILogFactory.getLogger(this.getClass());
- /* ===============================================================
- * Setup Code
- * ===============================================================
- */
-
-
/**
* Constructs a Powerpoint document from the underlying
* HSLFSlideShow object. Finds the model stuff from this
// We always use the latest versions of these records, and use the
// 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
+ 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
- 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
public void write(OutputStream out) throws IOException {
// check for text paragraph modifications
}
}
- /*
- * ===============================================================
- * Accessor Code
- * ===============================================================
- */
-
/**
* Returns an array of the most recent version of all the interesting
* records
return _documentRecord;
}
- /*
- * ===============================================================
- * Re-ordering Code
- * ===============================================================
- */
-
/**
* Re-orders a slide, to a new position.
*
return removedSlide;
}
- /*
- * ===============================================================
- * Addition Code
- * ===============================================================
- */
-
/**
* Create a blank <code>Slide</code>.
*
+ " and identifier " + sp.getSlideIdentifier());
// 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);
sp.setRefID(psrId);
slideRecord.setSheetId(psrId);
} else {
ctrl.setLinkURL(link.getAddress());
}
- ctrl.setLinkTitle(link.getTitle());
+ ctrl.setLinkTitle(link.getLabel());
int objectId = addToObjListAtom(ctrl);
link.setId(objectId);
private StyleTextProp9Atom styleTextProp9Atom;\r
\r
private boolean _dirty = false;\r
+ \r
+ private final List<HSLFTextParagraph> parentList;\r
\r
/**\r
* Constructs a Text Run from a Unicode text block.\r
* @param tha the TextHeaderAtom that defines what's what\r
* @param tba the TextBytesAtom containing the text or null if {@link TextCharsAtom} is provided\r
* @param tca the TextCharsAtom containing the text or null if {@link TextBytesAtom} is provided\r
+ * @param parentList the list which contains this paragraph\r
*/\r
/* package */ HSLFTextParagraph(\r
TextHeaderAtom tha,\r
TextBytesAtom tba,\r
- TextCharsAtom tca\r
+ TextCharsAtom tca,\r
+ List<HSLFTextParagraph> parentList\r
) {\r
if (tha == null) {\r
throw new IllegalArgumentException("TextHeaderAtom must be set.");\r
_headerAtom = tha;\r
_byteAtom = tba;\r
_charAtom = tca;\r
+ this.parentList = parentList;\r
}\r
\r
/* package */HSLFTextParagraph(HSLFTextParagraph other) {\r
_ruler = other._ruler;\r
shapeId = other.shapeId;\r
_paragraphStyle.copy(other._paragraphStyle);\r
+ parentList = other.parentList;\r
}\r
\r
public void addTextRun(HSLFTextRun run) {\r
* Supply the Sheet we belong to, which might have an assigned SlideShow\r
* Also passes it on to our child RichTextRuns\r
*/\r
- public void supplySheet(HSLFSheet sheet) {\r
+ public static void supplySheet(List<HSLFTextParagraph> paragraphs, HSLFSheet sheet) {\r
+ if (paragraphs == null) {\r
+ return;\r
+ }\r
+ for (HSLFTextParagraph p : paragraphs) {\r
+ p.supplySheet(sheet);\r
+ }\r
+\r
+ assert(sheet.getSlideShow() != null);\r
+ applyHyperlinks(paragraphs);\r
+ }\r
+ \r
+ /**\r
+ * Supply the Sheet we belong to, which might have an assigned SlideShow\r
+ * Also passes it on to our child RichTextRuns\r
+ */\r
+ private void supplySheet(HSLFSheet sheet) {\r
this._sheet = sheet;\r
\r
if (_runs == null) return;\r
if (addParagraph && !lastParaEmpty) {\r
TextPropCollection tpc = htp.getParagraphStyle();\r
HSLFTextParagraph prevHtp = htp;\r
- htp = new HSLFTextParagraph(htp._headerAtom, htp._byteAtom, htp._charAtom);\r
+ htp = new HSLFTextParagraph(htp._headerAtom, htp._byteAtom, htp._charAtom, paragraphs);\r
htp.getParagraphStyle().copy(tpc);\r
htp.setParentShape(prevHtp.getParentShape());\r
htp.setShapeId(prevHtp.getShapeId());\r
\r
// split, but keep delimiter\r
for (String para : rawText.split("(?<=\r)")) {\r
- HSLFTextParagraph tpara = new HSLFTextParagraph(header, tbytes, tchars);\r
+ HSLFTextParagraph tpara = new HSLFTextParagraph(header, tbytes, tchars, paragraphs);\r
paragraphs.add(tpara);\r
tpara._ruler = ruler;\r
tpara.getParagraphStyle().updateTextSize(para.length());\r
return paragraphCollection;\r
}\r
\r
+ protected static void applyHyperlinks(List<HSLFTextParagraph> paragraphs) {\r
+ List<HSLFHyperlink> links = HSLFHyperlink.find(paragraphs);\r
+ \r
+ for (HSLFHyperlink h : links) {\r
+ int csIdx = 0;\r
+ for (HSLFTextParagraph p : paragraphs) {\r
+ for (HSLFTextRun r : p) {\r
+ if (h.getStartIndex() <= csIdx && csIdx < h.getEndIndex()) {\r
+ r.setHyperlinkId(h.getId());\r
+ }\r
+ csIdx += r.getLength();\r
+ }\r
+ }\r
+ }\r
+ }\r
+ \r
protected static void applyCharacterStyles(List<HSLFTextParagraph> paragraphs, List<TextPropCollection> charStyles) {\r
int paraIdx = 0, runIdx = 0;\r
HSLFTextRun trun;\r
TextPropCollection charStyle = sta.addCharacterTextPropCollection(1);\r
wrapper.appendChildRecord(sta);\r
\r
- HSLFTextParagraph htp = new HSLFTextParagraph(tha, tba, null);\r
+ List<HSLFTextParagraph> paragraphs = new ArrayList<HSLFTextParagraph>(1);\r
+ HSLFTextParagraph htp = new HSLFTextParagraph(tha, tba, null, paragraphs);\r
htp.setParagraphStyle(paraStyle);\r
+ paragraphs.add(htp);\r
\r
HSLFTextRun htr = new HSLFTextRun(htp);\r
htr.setCharacterStyle(charStyle);\r
htr.setText("");\r
htp.addTextRun(htr);\r
\r
- return Arrays.asList(htp);\r
+ return paragraphs;\r
}\r
\r
public EscherTextboxWrapper getTextboxWrapper() {\r
public boolean isDirty() {\r
return _dirty;\r
}\r
+ \r
+ /**\r
+ * Calculates the start index of the given text run\r
+ *\r
+ * @param textrun the text run to search for\r
+ * @return the start index with the paragraph collection or -1 if not found\r
+ */\r
+ /* package */ int getStartIdxOfTextRun(HSLFTextRun textrun) {\r
+ int idx = 0;\r
+ for (HSLFTextParagraph p : parentList) {\r
+ for (HSLFTextRun r : p) {\r
+ if (r == textrun) {\r
+ return idx;\r
+ }\r
+ idx += r.getLength();\r
+ }\r
+ }\r
+ return -1;\r
+ }\r
}
\ No newline at end of file
import org.apache.poi.hslf.model.textproperties.TextProp;
import org.apache.poi.hslf.model.textproperties.TextPropCollection;
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.usermodel.PaintStyle;
import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint;
private HSLFTextParagraph parentParagraph;
private String _runText = "";
private String _fontFamily;
+ private int hyperlinkId = -1;
/**
* Our paragraph and character style.
public byte getPitchAndFamily() {
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
// is not assigned then, it's only built once we have
// 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);
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/", 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/", rawText.substring(links.get(1).getStartIndex(), links.get(1).getEndIndex()-1));
assertNotNull(links);
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("Jakarta HSSF", rawText.substring(links.get(0).getStartIndex(), links.get(0).getEndIndex()-1));
}
import java.awt.Color;
import java.io.File;
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.Date;
import java.util.HashMap;
import org.apache.poi.hslf.record.SlideListWithText;
import org.apache.poi.hslf.record.SlideListWithText.SlideAtomsSet;
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.DrawTextParagraph;
import org.apache.poi.sl.usermodel.PaintStyle;
import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint;
import org.apache.poi.sl.usermodel.PictureData.PictureType;
/**
* Testcases for bugs entered in bugzilla
* the Test name contains the bugzilla bug id
- *
- * @author Yegor Kozlov
*/
public final class TestBugs {
/**
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 {
File sample = HSLFTestDataSamples.getSampleFile(fileName);