From f97d6f6c34459972ba0909f6febb14bfac50585e Mon Sep 17 00:00:00 2001
From: Andreas Beeker
Date: Wed, 17 Jun 2015 22:21:13 +0000
Subject: [PATCH] Rendering fixes
git-svn-id: https://svn.apache.org/repos/asf/poi/branches/common_sl@1686117 13f79535-47bb-0310-9956-ffa450edef68
---
.../apache/poi/hslf/examples/BulletsDemo.java | 4 +-
.../apache/poi/xslf/usermodel/Tutorial2.java | 8 +-
.../apache/poi/xslf/usermodel/Tutorial7.java | 4 +-
src/java/org/apache/poi/util/Units.java | 31 +
.../poi/xslf/usermodel/XSLFTextParagraph.java | 286 ++--
.../poi/xslf/usermodel/XSLFTextRun.java | 48 +-
.../poi/xslf/usermodel/TestXSLFAutoShape.java | 26 +-
.../xslf/usermodel/TestXSLFTextParagraph.java | 2 +-
.../textproperties/FontAlignmentProp.java | 3 +-
.../textproperties/TextPropCollection.java | 71 +-
.../poi/hslf/record/FontCollection.java | 5 +-
.../poi/hslf/usermodel/HSLFSlideMaster.java | 58 +-
.../poi/hslf/usermodel/HSLFTextParagraph.java | 1490 ++++++++---------
.../poi/hslf/usermodel/HSLFTextRun.java | 57 +-
.../apache/poi/sl/draw/DrawTextParagraph.java | 89 +-
.../org/apache/poi/sl/draw/DrawTextShape.java | 6 +-
.../poi/sl/usermodel/TextParagraph.java | 115 +-
.../org/apache/poi/sl/usermodel/TextRun.java | 8 +-
.../poi/hslf/usermodel/TestPicture.java | 62 +-
.../poi/hslf/usermodel/TestRichTextRun.java | 14 +-
.../poi/hslf/usermodel/TestTextRun.java | 2 +-
test-data/slideshow/alterman_security2.pptx | Bin 0 -> 425540 bytes
22 files changed, 1192 insertions(+), 1197 deletions(-)
create mode 100644 test-data/slideshow/alterman_security2.pptx
diff --git a/src/examples/src/org/apache/poi/hslf/examples/BulletsDemo.java b/src/examples/src/org/apache/poi/hslf/examples/BulletsDemo.java
index d95f970ce..0032bc809 100644
--- a/src/examples/src/org/apache/poi/hslf/examples/BulletsDemo.java
+++ b/src/examples/src/org/apache/poi/hslf/examples/BulletsDemo.java
@@ -39,8 +39,8 @@ public final class BulletsDemo {
HSLFTextParagraph rt = shape.getTextParagraphs().get(0);
rt.getTextRuns().get(0).setFontSize(42);
rt.setBullet(true);
- rt.setIndent(0); //bullet offset
- rt.setLeftMargin(50); //text offset (should be greater than bullet offset)
+ rt.setIndent(0d); //bullet offset
+ rt.setLeftMargin(50d); //text offset (should be greater than bullet offset)
rt.setBulletChar('\u263A'); //bullet character
shape.setText(
"January\r" +
diff --git a/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial2.java b/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial2.java
index 373f01f33..91366b9d0 100644
--- a/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial2.java
+++ b/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial2.java
@@ -48,8 +48,8 @@ public class Tutorial2 {
XSLFTextParagraph p2 = shape1.addNewTextParagraph();
// If spaceBefore >= 0, then space is a percentage of normal line height.
// If spaceBefore < 0, the absolute value of linespacing is the spacing in points
- p2.setSpaceBefore(-20); // 20 pt from the previous paragraph
- p2.setSpaceAfter(300); // 3 lines after the paragraph
+ p2.setSpaceBefore(-20d); // 20 pt from the previous paragraph
+ p2.setSpaceAfter(300d); // 3 lines after the paragraph
XSLFTextRun r2 = p2.addNewTextRun();
r2.setText("Paragraph properties apply to all text residing within the corresponding paragraph.");
r2.setFontSize(16);
@@ -62,8 +62,8 @@ public class Tutorial2 {
r3.setFontColor(new Color(85, 142, 213));
XSLFTextParagraph p4 = shape1.addNewTextParagraph();
- p4.setSpaceBefore(-20); // 20 pt from the previous paragraph
- p4.setSpaceAfter(300); // 3 lines after the paragraph
+ p4.setSpaceBefore(-20d); // 20 pt from the previous paragraph
+ p4.setSpaceAfter(300d); // 3 lines after the paragraph
XSLFTextRun r4 = p4.addNewTextRun();
r4.setFontSize(16);
r4.setText(
diff --git a/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial7.java b/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial7.java
index a80f23cad..95252d72f 100644
--- a/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial7.java
+++ b/src/examples/src/org/apache/poi/xslf/usermodel/Tutorial7.java
@@ -45,9 +45,9 @@ public class Tutorial7 {
XSLFTextParagraph p2 = shape.addNewTextParagraph();
// indentation before text
- p2.setLeftMargin(60);
+ p2.setLeftMargin(60d);
// the bullet is set 40 pt before the text
- p2.setIndent(-40);
+ p2.setIndent(-40d);
p2.setBullet(true);
// customize bullets
p2.setBulletFontColor(Color.red);
diff --git a/src/java/org/apache/poi/util/Units.java b/src/java/org/apache/poi/util/Units.java
index bc643fd97..107a9b583 100644
--- a/src/java/org/apache/poi/util/Units.java
+++ b/src/java/org/apache/poi/util/Units.java
@@ -16,6 +16,8 @@
==================================================================== */
package org.apache.poi.util;
+import org.apache.poi.hslf.usermodel.HSLFShape;
+
/**
* @author Yegor Kozlov
*/
@@ -23,6 +25,22 @@ public class Units {
public static final int EMU_PER_PIXEL = 9525;
public static final int EMU_PER_POINT = 12700;
+ /**
+ * Master DPI (576 pixels per inch).
+ * Used by the reference coordinate system in PowerPoint (HSLF)
+ */
+ public static final int MASTER_DPI = 576;
+
+ /**
+ * Pixels DPI (96 pixels per inch)
+ */
+ public static final int PIXEL_DPI = 96;
+
+ /**
+ * Points DPI (72 pixels per inch)
+ */
+ public static final int POINT_DPI = 72;
+
/**
* Converts points to EMUs
* @param points points
@@ -70,4 +88,17 @@ public class Units {
int fixedPoint = (i << 16) | (f & 0xFFFF);
return fixedPoint;
}
+
+ public static double masterToPoints(int masterDPI) {
+ double points = masterDPI;
+ points *= HSLFShape.POINT_DPI;
+ points /= HSLFShape.MASTER_DPI;
+ return points;
+ }
+
+ public static int pointsToMaster(double points) {
+ points *= HSLFShape.MASTER_DPI;
+ points /= HSLFShape.POINT_DPI;
+ return (int)points;
+ }
}
diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java
index aef03197d..f0cf3a78a 100644
--- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java
+++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java
@@ -137,8 +137,10 @@ public class XSLFTextParagraph implements TextParagraph {
/**
* Returns the alignment that is applied to the paragraph.
*
- * If this attribute is omitted, then a value of left is implied.
- * @return ??? alignment that is applied to the paragraph
+ * If this attribute is omitted, then null is returned.
+ * User code can imply the value {@link TextAlign#LEFT} then.
+ *
+ * @return alignment that is applied to the paragraph
*/
public TextAlign getTextAlign(){
ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getLevel()){
@@ -152,7 +154,7 @@ public class XSLFTextParagraph implements TextParagraph {
}
};
fetchParagraphProperty(fetcher);
- return fetcher.getValue() == null ? TextAlign.LEFT : fetcher.getValue();
+ return fetcher.getValue();
}
/**
@@ -184,7 +186,7 @@ public class XSLFTextParagraph implements TextParagraph {
}
};
fetchParagraphProperty(fetcher);
- return fetcher.getValue() == null ? FontAlign.AUTO : fetcher.getValue();
+ return fetcher.getValue();
}
/**
@@ -294,7 +296,7 @@ public class XSLFTextParagraph implements TextParagraph {
*
* @return the bullet size
*/
- public double getBulletFontSize(){
+ public Double getBulletFontSize(){
ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getLevel()){
public boolean fetch(CTTextParagraphProperties props){
if(props.isSetBuSzPct()){
@@ -309,7 +311,7 @@ public class XSLFTextParagraph implements TextParagraph {
}
};
fetchParagraphProperty(fetcher);
- return fetcher.getValue() == null ? 100 : fetcher.getValue();
+ return fetcher.getValue();
}
/**
@@ -334,27 +336,19 @@ public class XSLFTextParagraph implements TextParagraph {
}
}
- /**
- * Specifies the indent size that will be applied to the first line of text in the paragraph.
- *
- * @param value the indent in points.
- */
@Override
- public void setIndent(double value){
+ public void setIndent(Double indent){
+ if (indent == null && !_p.isSetPPr()) return;
CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
- if(value == -1) {
+ if(indent == -1) {
if(pr.isSetIndent()) pr.unsetIndent();
} else {
- pr.setIndent(Units.toEMU(value));
+ pr.setIndent(Units.toEMU(indent));
}
}
- /**
- *
- * @return the indent applied to the first line of text in the paragraph.
- */
@Override
- public double getIndent(){
+ public Double getIndent() {
ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getLevel()){
public boolean fetch(CTTextParagraphProperties props){
@@ -367,32 +361,26 @@ public class XSLFTextParagraph implements TextParagraph {
};
fetchParagraphProperty(fetcher);
- return fetcher.getValue() == null ? 0 : fetcher.getValue();
+ return fetcher.getValue();
}
- /**
- * Specifies the left margin of the paragraph. This is specified in addition to the text body
- * inset and applies only to this text paragraph. That is the text body Inset and the LeftMargin
- * attributes are additive with respect to the text position.
- *
- * @param value the left margin (in points) of the paragraph
- */
@Override
- public void setLeftMargin(double value){
+ public void setLeftMargin(Double leftMargin){
+ if (leftMargin == null && !_p.isSetPPr()) return;
CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
- if(value == -1) {
+ if (leftMargin == null) {
if(pr.isSetMarL()) pr.unsetMarL();
} else {
- pr.setMarL(Units.toEMU(value));
+ pr.setMarL(Units.toEMU(leftMargin));
}
}
/**
- * @return the left margin (in points) of the paragraph
+ * @return the left margin (in points) of the paragraph, null if unset
*/
@Override
- public double getLeftMargin(){
+ public Double getLeftMargin(){
ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getLevel()){
public boolean fetch(CTTextParagraphProperties props){
if(props.isSetMarL()){
@@ -405,32 +393,26 @@ public class XSLFTextParagraph implements TextParagraph {
};
fetchParagraphProperty(fetcher);
// if the marL attribute is omitted, then a value of 347663 is implied
- return fetcher.getValue() == null ? 0 : fetcher.getValue();
+ return fetcher.getValue();
}
- /**
- * Specifies the right margin of the paragraph. This is specified in addition to the text body
- * inset and applies only to this text paragraph. That is the text body Inset and the RightMargin
- * attributes are additive with respect to the text position.
- *
- * @param value the right margin (in points) of the paragraph
- */
@Override
- public void setRightMargin(double value){
+ public void setRightMargin(Double rightMargin){
+ if (rightMargin == null && !_p.isSetPPr()) return;
CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
- if(value == -1) {
+ if(rightMargin == -1) {
if(pr.isSetMarR()) pr.unsetMarR();
} else {
- pr.setMarR(Units.toEMU(value));
+ pr.setMarR(Units.toEMU(rightMargin));
}
}
/**
*
- * @return the right margin of the paragraph
+ * @return the right margin of the paragraph, null if unset
*/
@Override
- public double getRightMargin(){
+ public Double getRightMargin(){
ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getLevel()){
public boolean fetch(CTTextParagraphProperties props){
if(props.isSetMarR()){
@@ -443,14 +425,11 @@ public class XSLFTextParagraph implements TextParagraph {
};
fetchParagraphProperty(fetcher);
// if the marL attribute is omitted, then a value of 347663 is implied
- return fetcher.getValue() == null ? 0 : fetcher.getValue();
+ return fetcher.getValue();
}
- /**
- *
- * @return the default size for a tab character within this paragraph in points
- */
- public double getDefaultTabSize(){
+ @Override
+ public Double getDefaultTabSize(){
ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getLevel()){
public boolean fetch(CTTextParagraphProperties props){
if(props.isSetDefTabSz()){
@@ -462,7 +441,7 @@ public class XSLFTextParagraph implements TextParagraph {
}
};
fetchParagraphProperty(fetcher);
- return fetcher.getValue() == null ? 0 : fetcher.getValue();
+ return fetcher.getValue();
}
public double getTabStop(final int idx){
@@ -491,16 +470,25 @@ public class XSLFTextParagraph implements TextParagraph {
}
@Override
- public void setLineSpacing(double linespacing){
+ public void setLineSpacing(Double lineSpacing){
+ if (lineSpacing == null && !_p.isSetPPr()) return;
CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
- CTTextSpacing spc = CTTextSpacing.Factory.newInstance();
- if(linespacing >= 0) spc.addNewSpcPct().setVal((int)(linespacing*1000));
- else spc.addNewSpcPts().setVal((int)(-linespacing*100));
- pr.setLnSpc(spc);
+ if(lineSpacing == null) {
+ if (pr.isSetLnSpc()) pr.unsetLnSpc();
+ } else {
+ CTTextSpacing spc = (pr.isSetLnSpc()) ? pr.getLnSpc() : pr.addNewLnSpc();
+ if (lineSpacing >= 0) {
+ (spc.isSetSpcPct() ? spc.getSpcPct() : spc.addNewSpcPct()).setVal((int)(lineSpacing*1000));
+ if (spc.isSetSpcPts()) spc.unsetSpcPts();
+ } else {
+ (spc.isSetSpcPts() ? spc.getSpcPts() : spc.addNewSpcPts()).setVal((int)(-lineSpacing*100));
+ if (spc.isSetSpcPct()) spc.unsetSpcPct();
+ }
+ }
}
@Override
- public double getLineSpacing(){
+ public Double getLineSpacing(){
ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getLevel()){
public boolean fetch(CTTextParagraphProperties props){
if(props.isSetLnSpc()){
@@ -515,8 +503,8 @@ public class XSLFTextParagraph implements TextParagraph {
};
fetchParagraphProperty(fetcher);
- double lnSpc = fetcher.getValue() == null ? 100 : fetcher.getValue();
- if(lnSpc > 0) {
+ Double lnSpc = fetcher.getValue();
+ if (lnSpc != null && lnSpc > 0) {
// check if the percentage value is scaled
CTTextNormalAutofit normAutofit = getParentShape().getTextBodyPr().getNormAutofit();
if(normAutofit != null) {
@@ -528,26 +516,9 @@ public class XSLFTextParagraph implements TextParagraph {
return lnSpc;
}
- /**
- * Set the amount of vertical white space that will be present before the paragraph.
- * This space is specified in either percentage or points:
- *
- * If spaceBefore >= 0, then space is a percentage of normal line height.
- * If spaceBefore < 0, the absolute value of linespacing is the spacing in points
- *
- * Examples:
- *
- * // The paragraph will be formatted to have a spacing before the paragraph text.
- * // The spacing will be 200% of the size of the largest text on each line
- * paragraph.setSpaceBefore(200);
- *
- * // The spacing will be a size of 48 points
- * paragraph.setSpaceBefore(-48.0);
- *
- *
- * @param spaceBefore the vertical white space before the paragraph.
- */
- public void setSpaceBefore(double spaceBefore){
+ @Override
+ public void setSpaceBefore(Double spaceBefore){
+ if (spaceBefore == null && !_p.isSetPPr()) return;
CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
CTTextSpacing spc = CTTextSpacing.Factory.newInstance();
if(spaceBefore >= 0) spc.addNewSpcPct().setVal((int)(spaceBefore*1000));
@@ -555,17 +526,8 @@ public class XSLFTextParagraph implements TextParagraph {
pr.setSpcBef(spc);
}
- /**
- * The amount of vertical white space before the paragraph
- * This may be specified in two different ways, percentage spacing and font point spacing:
- *
- * If spaceBefore >= 0, then space is a percentage of normal line height.
- * If spaceBefore < 0, the absolute value of linespacing is the spacing in points
- *
- *
- * @return the vertical white space before the paragraph
- */
- public double getSpaceBefore(){
+ @Override
+ public Double getSpaceBefore(){
ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getLevel()){
public boolean fetch(CTTextParagraphProperties props){
if(props.isSetSpcBef()){
@@ -580,30 +542,11 @@ public class XSLFTextParagraph implements TextParagraph {
};
fetchParagraphProperty(fetcher);
- double spcBef = fetcher.getValue() == null ? 0 : fetcher.getValue();
- return spcBef;
+ return fetcher.getValue();
}
- /**
- * Set the amount of vertical white space that will be present after the paragraph.
- * This space is specified in either percentage or points:
- *
- * If spaceAfter >= 0, then space is a percentage of normal line height.
- * If spaceAfter < 0, the absolute value of linespacing is the spacing in points
- *
- * Examples:
- *
- * // The paragraph will be formatted to have a spacing after the paragraph text.
- * // The spacing will be 200% of the size of the largest text on each line
- * paragraph.setSpaceAfter(200);
- *
- * // The spacing will be a size of 48 points
- * paragraph.setSpaceAfter(-48.0);
- *
- *
- * @param spaceAfter the vertical white space after the paragraph.
- */
- public void setSpaceAfter(double spaceAfter){
+ @Override
+ public void setSpaceAfter(Double spaceAfter){
CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
CTTextSpacing spc = CTTextSpacing.Factory.newInstance();
if(spaceAfter >= 0) spc.addNewSpcPct().setVal((int)(spaceAfter*1000));
@@ -611,17 +554,8 @@ public class XSLFTextParagraph implements TextParagraph {
pr.setSpcAft(spc);
}
- /**
- * The amount of vertical white space after the paragraph
- * This may be specified in two different ways, percentage spacing and font point spacing:
- *
- * If spaceBefore >= 0, then space is a percentage of normal line height.
- * If spaceBefore < 0, the absolute value of linespacing is the spacing in points
- *
- *
- * @return the vertical white space after the paragraph
- */
- public double getSpaceAfter(){
+ @Override
+ public Double getSpaceAfter(){
ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getLevel()){
public boolean fetch(CTTextParagraphProperties props){
if(props.isSetSpcAft()){
@@ -635,7 +569,7 @@ public class XSLFTextParagraph implements TextParagraph {
}
};
fetchParagraphProperty(fetcher);
- return fetcher.getValue() == null ? 0 : fetcher.getValue();
+ return fetcher.getValue();
}
/**
@@ -647,7 +581,6 @@ public class XSLFTextParagraph implements TextParagraph {
*/
public void setLevel(int level){
CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
-
pr.setLvl(level);
}
@@ -657,10 +590,7 @@ public class XSLFTextParagraph implements TextParagraph {
*/
public int getLevel(){
CTTextParagraphProperties pr = _p.getPPr();
- if(pr == null) return 0;
-
- return pr.getLvl();
-
+ return (pr == null || !pr.isSetLvl()) ? 0 : pr.getLvl();
}
/**
@@ -721,53 +651,70 @@ public class XSLFTextParagraph implements TextParagraph {
}
- CTTextParagraphProperties getDefaultMasterStyle(){
+ /* package */ CTTextParagraphProperties getDefaultMasterStyle(){
CTPlaceholder ph = _shape.getCTPlaceholder();
- String defaultStyleSelector;
- if(ph == null) defaultStyleSelector = "otherStyle"; // no placeholder means plain text box
- else {
- switch(ph.getType().intValue()){
- case STPlaceholderType.INT_TITLE:
- case STPlaceholderType.INT_CTR_TITLE:
- defaultStyleSelector = "titleStyle";
- break;
- case STPlaceholderType.INT_FTR:
- case STPlaceholderType.INT_SLD_NUM:
- case STPlaceholderType.INT_DT:
- defaultStyleSelector = "otherStyle";
- break;
- default:
- defaultStyleSelector = "bodyStyle";
- break;
- }
+ String defaultStyleSelector;
+ switch(ph == null ? -1 : ph.getType().intValue()) {
+ case STPlaceholderType.INT_TITLE:
+ case STPlaceholderType.INT_CTR_TITLE:
+ defaultStyleSelector = "titleStyle";
+ break;
+ case -1: // no placeholder means plain text box
+ case STPlaceholderType.INT_FTR:
+ case STPlaceholderType.INT_SLD_NUM:
+ case STPlaceholderType.INT_DT:
+ defaultStyleSelector = "otherStyle";
+ break;
+ default:
+ defaultStyleSelector = "bodyStyle";
+ break;
}
int level = getLevel();
// wind up and find the root master sheet which must be slide master
XSLFSheet masterSheet = _shape.getSheet();
- while (masterSheet.getMasterSheet() != null){
- masterSheet = (XSLFSheet)masterSheet.getMasterSheet();
+ for (XSLFSheet m = masterSheet; m != null; m = (XSLFSheet)m.getMasterSheet()) {
+ masterSheet = m;
}
- XmlObject[] o = masterSheet.getXmlObject().selectPath(
- "declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main' " +
- "declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' " +
- ".//p:txStyles/p:" + defaultStyleSelector +"/a:lvl" +(level+1)+ "pPr");
- if (o.length == 1){
- return (CTTextParagraphProperties)o[0];
- } else {
- o = masterSheet.getXmlObject().selectPath(
- "declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main' " +
- "declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' " +
- ".//p:notesStyle/a:lvl" +(level+1)+ "pPr");
-
- if (o.length == 1){
+ String nsDecl =
+ "declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main' " +
+ "declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' ";
+ String xpaths[] = {
+ nsDecl+".//p:txStyles/p:" + defaultStyleSelector +"/a:lvl" +(level+1)+ "pPr",
+ nsDecl+".//p:notesStyle/a:lvl" +(level+1)+ "pPr"
+ };
+ XmlObject xo = masterSheet.getXmlObject();
+ for (String xpath : xpaths) {
+ XmlObject[] o = xo.selectPath(xpath);
+ if (o.length == 1) {
return (CTTextParagraphProperties)o[0];
}
-
- throw new IllegalArgumentException("Failed to fetch default style for " +
- defaultStyleSelector + " and level=" + level);
}
+
+// for (CTTextBody txBody : (CTTextBody[])xo.selectPath(nsDecl+".//p:txBody")) {
+// CTTextParagraphProperties defaultPr = null, lastPr = null;
+// boolean hasLvl = false;
+// for (CTTextParagraph p : txBody.getPArray()) {
+// CTTextParagraphProperties pr = p.getPPr();
+// if (pr.isSetLvl()) {
+// hasLvl |= true;
+// lastPr = pr;
+// if (pr.getLvl() == level) return pr;
+// } else {
+// defaultPr = pr;
+// }
+// }
+// if (!hasLvl) continue;
+// if (level == 0 && defaultPr != null) return defaultPr;
+// if (lastPr != null) return lastPr;
+// break;
+// }
+//
+// String err = "Failed to fetch default style for " + defaultStyleSelector + " and level=" + level;
+// throw new IllegalArgumentException(err);
+
+ return null;
}
private boolean fetchParagraphProperty(ParagraphPropertyFetcher visitor){
@@ -860,9 +807,9 @@ public class XSLFTextParagraph implements TextParagraph {
}
@Override
- public double getDefaultFontSize() {
+ public Double getDefaultFontSize() {
CTTextCharacterProperties endPr = _p.getEndParaRPr();
- return (endPr == null || !endPr.isSetSz()) ? 12 : (endPr.getSz() / 100);
+ return (endPr == null || !endPr.isSetSz()) ? 12 : (endPr.getSz() / 100.);
}
@Override
@@ -871,6 +818,7 @@ public class XSLFTextParagraph implements TextParagraph {
}
public BulletStyle getBulletStyle() {
+ if (!isBullet()) return null;
return new BulletStyle(){
public String getBulletCharacter() {
return XSLFTextParagraph.this.getBulletCharacter();
@@ -880,7 +828,7 @@ public class XSLFTextParagraph implements TextParagraph {
return XSLFTextParagraph.this.getBulletFont();
}
- public double getBulletFontSize() {
+ public Double getBulletFontSize() {
return XSLFTextParagraph.this.getBulletFontSize();
}
diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java
index 2b63a5809..d99df7e79 100644
--- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java
+++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java
@@ -17,26 +17,11 @@
package org.apache.poi.xslf.usermodel;
import java.awt.Color;
-import java.awt.font.FontRenderContext;
-import java.awt.font.TextAttribute;
-import java.awt.font.TextLayout;
-import java.text.AttributedString;
import org.apache.poi.sl.usermodel.TextRun;
import org.apache.poi.util.Beta;
import org.apache.poi.xslf.model.CharacterPropertyFetcher;
-import org.openxmlformats.schemas.drawingml.x2006.main.CTRegularTextRun;
-import org.openxmlformats.schemas.drawingml.x2006.main.CTSRgbColor;
-import org.openxmlformats.schemas.drawingml.x2006.main.CTSchemeColor;
-import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeStyle;
-import org.openxmlformats.schemas.drawingml.x2006.main.CTSolidColorFillProperties;
-import org.openxmlformats.schemas.drawingml.x2006.main.CTTextCharacterProperties;
-import org.openxmlformats.schemas.drawingml.x2006.main.CTTextFont;
-import org.openxmlformats.schemas.drawingml.x2006.main.CTTextNormalAutofit;
-import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraphProperties;
-import org.openxmlformats.schemas.drawingml.x2006.main.STSchemeColorVal;
-import org.openxmlformats.schemas.drawingml.x2006.main.STTextStrikeType;
-import org.openxmlformats.schemas.drawingml.x2006.main.STTextUnderlineType;
+import org.openxmlformats.schemas.drawingml.x2006.main.*;
import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder;
/**
@@ -89,28 +74,6 @@ public class XSLFTextRun implements TextRun {
return buf.toString();
}
- /**
- * Replace a tab with the effective number of white spaces.
- */
- private String tab2space(){
- AttributedString string = new AttributedString(" ");
- // user can pass an object to convert fonts via a rendering hint
- string.addAttribute(TextAttribute.FAMILY, getFontFamily());
-
- string.addAttribute(TextAttribute.SIZE, (float)getFontSize());
- TextLayout l = new TextLayout(string.getIterator(), new FontRenderContext(null, true, true));
- double wspace = l.getAdvance();
-
- double tabSz = _p.getDefaultTabSize();
-
- int numSpaces = (int)Math.ceil(tabSz / wspace);
- StringBuffer buf = new StringBuffer();
- for(int i = 0; i < numSpaces; i++) {
- buf.append(' ');
- }
- return buf.toString();
- }
-
public void setText(String text){
_r.setT(text);
}
@@ -175,9 +138,10 @@ public class XSLFTextRun implements TextRun {
}
/**
- * @return font size in points or -1 if font size is not set.
+ * @return font size in points or null if font size is not set.
*/
- public double getFontSize(){
+ @Override
+ public Double getFontSize(){
double scale = 1;
CTTextNormalAutofit afit = getParentParagraph().getParentShape().getTextBodyPr().getNormAutofit();
if(afit != null) scale = (double)afit.getFontScale() / 100000;
@@ -192,7 +156,7 @@ public class XSLFTextRun implements TextRun {
}
};
fetchCharacterProperty(fetcher);
- return fetcher.getValue() == null ? -1 : fetcher.getValue()*scale;
+ return fetcher.getValue() == null ? null : fetcher.getValue()*scale;
}
/**
@@ -514,7 +478,7 @@ public class XSLFTextRun implements TextRun {
return new XSLFHyperlink(_r.getRPr().getHlinkClick(), this);
}
- private boolean fetchCharacterProperty(CharacterPropertyFetcher fetcher){
+ private boolean fetchCharacterProperty(CharacterPropertyFetcher> fetcher){
boolean ok = false;
if(_r.isSetRPr()) ok = fetcher.fetch(getRPr());
diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFAutoShape.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFAutoShape.java
index 6fc5690cc..b3c44f84a 100644
--- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFAutoShape.java
+++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFAutoShape.java
@@ -125,7 +125,7 @@ public class TestXSLFAutoShape {
p.setIndent(2.0);
assertEquals(2.0, p.getIndent(), 0);
assertTrue(p.getXmlObject().getPPr().isSetIndent());
- p.setIndent(-1);
+ p.setIndent(-1d);
assertEquals(0.0, p.getIndent(), 0);
assertFalse(p.getXmlObject().getPPr().isSetIndent());
p.setIndent(10.0);
@@ -149,44 +149,44 @@ public class TestXSLFAutoShape {
assertFalse(p.getXmlObject().getPPr().isSetSpcAft());
- p.setSpaceAfter(200);
+ p.setSpaceAfter(200d);
assertEquals(200000, p.getXmlObject().getPPr().getSpcAft().getSpcPct().getVal());
assertFalse(p.getXmlObject().getPPr().getSpcAft().isSetSpcPts());
- p.setSpaceAfter(100);
+ p.setSpaceAfter(100d);
assertEquals(100000, p.getXmlObject().getPPr().getSpcAft().getSpcPct().getVal());
assertFalse(p.getXmlObject().getPPr().getSpcAft().isSetSpcPts());
- p.setSpaceAfter(-20);
+ p.setSpaceAfter(-20d);
assertEquals(2000, p.getXmlObject().getPPr().getSpcAft().getSpcPts().getVal());
assertFalse(p.getXmlObject().getPPr().getSpcAft().isSetSpcPct());
- p.setSpaceAfter(-10);
+ p.setSpaceAfter(-10d);
assertEquals(1000, p.getXmlObject().getPPr().getSpcAft().getSpcPts().getVal());
assertFalse(p.getXmlObject().getPPr().getSpcAft().isSetSpcPct());
assertFalse(p.getXmlObject().getPPr().isSetSpcBef());
- p.setSpaceBefore(200);
+ p.setSpaceBefore(200d);
assertEquals(200000, p.getXmlObject().getPPr().getSpcBef().getSpcPct().getVal());
assertFalse(p.getXmlObject().getPPr().getSpcBef().isSetSpcPts());
- p.setSpaceBefore(100);
+ p.setSpaceBefore(100d);
assertEquals(100000, p.getXmlObject().getPPr().getSpcBef().getSpcPct().getVal());
assertFalse(p.getXmlObject().getPPr().getSpcBef().isSetSpcPts());
- p.setSpaceBefore(-20);
+ p.setSpaceBefore(-20d);
assertEquals(2000, p.getXmlObject().getPPr().getSpcBef().getSpcPts().getVal());
assertFalse(p.getXmlObject().getPPr().getSpcBef().isSetSpcPct());
- p.setSpaceBefore(-10);
+ p.setSpaceBefore(-10d);
assertEquals(1000, p.getXmlObject().getPPr().getSpcBef().getSpcPts().getVal());
assertFalse(p.getXmlObject().getPPr().getSpcBef().isSetSpcPct());
assertFalse(p.getXmlObject().getPPr().isSetLnSpc());
- p.setLineSpacing(200);
+ p.setLineSpacing(200d);
assertEquals(200000, p.getXmlObject().getPPr().getLnSpc().getSpcPct().getVal());
assertFalse(p.getXmlObject().getPPr().getLnSpc().isSetSpcPts());
- p.setLineSpacing(100);
+ p.setLineSpacing(100d);
assertEquals(100000, p.getXmlObject().getPPr().getLnSpc().getSpcPct().getVal());
assertFalse(p.getXmlObject().getPPr().getLnSpc().isSetSpcPts());
- p.setLineSpacing(-20);
+ p.setLineSpacing(-20d);
assertEquals(2000, p.getXmlObject().getPPr().getLnSpc().getSpcPts().getVal());
assertFalse(p.getXmlObject().getPPr().getLnSpc().isSetSpcPct());
- p.setLineSpacing(-10);
+ p.setLineSpacing(-10d);
assertEquals(1000, p.getXmlObject().getPPr().getLnSpc().getSpcPts().getVal());
assertFalse(p.getXmlObject().getPPr().getLnSpc().isSetSpcPct());
diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTextParagraph.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTextParagraph.java
index 0998ee8e7..37a01a73a 100644
--- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTextParagraph.java
+++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTextParagraph.java
@@ -92,7 +92,7 @@ public class TestXSLFTextParagraph {
assertEquals(expectedWidth, dtp.getWrappingWidth(true, null), 0);
assertEquals(expectedWidth, dtp.getWrappingWidth(false, null), 0);
- p.setLeftMargin(36); // 0.5"
+ p.setLeftMargin(36d); // 0.5"
leftMargin = p.getLeftMargin();
assertEquals(36.0, leftMargin, 0);
expectedWidth = anchor.getWidth() - leftInset - rightInset - leftMargin;
diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/FontAlignmentProp.java b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/FontAlignmentProp.java
index c9c911ccf..904feeedf 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/FontAlignmentProp.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/FontAlignmentProp.java
@@ -21,12 +21,13 @@ package org.apache.poi.hslf.model.textproperties;
* Definition for the font alignment property.
*/
public class FontAlignmentProp extends TextProp {
+ public static final String NAME = "fontAlign";
public static final int BASELINE = 0;
public static final int TOP = 1;
public static final int CENTER = 2;
public static final int BOTTOM = 3;
public FontAlignmentProp() {
- super(2, 0x10000, "fontAlign");
+ super(2, 0x10000, NAME);
}
}
\ No newline at end of file
diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextPropCollection.java b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextPropCollection.java
index 17d0c1d2c..84cfe9940 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextPropCollection.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextPropCollection.java
@@ -31,47 +31,10 @@ import org.apache.poi.util.LittleEndian;
* properties, and the indent level if required.
*/
public class TextPropCollection {
- /*
- private static TextProp paragraphSpecialPropTypes[] = {
- new ParagraphFlagsTextProp(),
- new TextProp(2, 0x80, "bullet.char"),
- new TextProp(2, 0x10, "bullet.font"),
- new TextProp(2, 0x40, "bullet.size"),
- new TextProp(4, 0x20, "bullet.color"),
- new TextProp(2, 0xD00, "alignment"),
- new TextProp(2, 0x1000, "linespacing"),
- new TextProp(2, 0x2000, "spacebefore"),
- new TextProp(2, 0x4000, "spaceafter"),
- new TextProp(2, 0x8000, "text.offset"),
- new TextProp(2, 0x10000, "bullet.offset"),
- new TextProp(2, 0x20000, "defaulttab"),
- new TextProp(2, 0x40000, "para_unknown_2"),
- new TextProp(2, 0x80000, "para_unknown_3"),
- new TextProp(2, 0x100000, "para_unknown_4"),
- new TextProp(2, 0x200000, "para_unknown_5")
- };
-
- private static TextProp characterSpecialPropTypes[] = {
- new CharFlagsTextProp(),
- new TextProp(2, 0x10000, "font.index"),
- new TextProp(2, 0x20000, "char_unknown_1"),
- new TextProp(4, 0x40000, "char_unknown_2"),
- new TextProp(2, 0x80000, "font.size"),
- new TextProp(2, 0x100000, "char_unknown_3"),
- new TextProp(4, 0x200000, "font.color"),
- new TextProp(2, 0x800000, "char_unknown_4")
- };
-*/
-
-
/** All the different kinds of paragraph properties we might handle */
public static final TextProp[] paragraphTextPropTypes = {
// TextProp order is according to 2.9.20 TextPFException,
// bitmask order can be different
-// new TextProp(0, 0x1, "hasBullet"),
-// new TextProp(0, 0x2, "hasBulletFont"),
-// new TextProp(0, 0x4, "hasBulletColor"),
-// new TextProp(0, 0x8, "hasBulletSize"),
new ParagraphFlagsTextProp(),
new TextProp(2, 0x80, "bullet.char"),
new TextProp(2, 0x10, "bullet.font"),
@@ -95,24 +58,9 @@ public class TextPropCollection {
new TextProp(0, 0x2000000, "hasBulletScheme"), // TODO: check size
// 0xFC000000 MUST be zero and MUST be ignored
};
+
/** All the different kinds of character properties we might handle */
public static final TextProp[] characterTextPropTypes = new TextProp[] {
-// new TextProp(0, 0x1, "bold"),
-// new TextProp(0, 0x2, "italic"),
-// new TextProp(0, 0x4, "underline"),
-// new TextProp(0, 0x8, "unused1"),
-// new TextProp(0, 0x10, "shadow"),
-// new TextProp(0, 0x20, "fehint"),
-// new TextProp(0, 0x40, "unused2"),
-// new TextProp(0, 0x80, "kumi"),
-// new TextProp(0, 0x100, "strikethrough"),
-// new TextProp(0, 0x200, "emboss"),
-// new TextProp(0, 0x400, "nibble1"),
-// new TextProp(0, 0x800, "nibble2"),
-// new TextProp(0, 0x1000, "nibble3"),
-// new TextProp(0, 0x2000, "nibble4"),
-// new TextProp(0, 0x4000, "unused4"),
-// new TextProp(0, 0x8000, "unused5"),
new TextProp(0, 0x100000, "pp10ext"),
new TextProp(0, 0x1000000, "newAsian.font.index"), // A bit that specifies whether the newEAFontRef field of the TextCFException10 structure that contains this CFMasks exists.
new TextProp(0, 0x2000000, "cs.font.index"), // A bit that specifies whether the csFontRef field of the TextCFException10 structure that contains this CFMasks exists.
@@ -166,6 +114,19 @@ public class TextPropCollection {
}
return null;
}
+
+ public TextProp removeByName(String name) {
+ Iterator iter = textPropList.iterator();
+ TextProp tp = null;
+ while (iter.hasNext()) {
+ tp = iter.next();
+ if (tp.getName().equals(name)){
+ iter.remove();
+ break;
+ }
+ }
+ return tp;
+ }
/** Add the TextProp with this name to the list */
public TextProp addWithName(String name) {
@@ -192,6 +153,10 @@ public class TextPropCollection {
return textProp;
}
+ public TextPropType getTextPropType() {
+ return textPropType;
+ }
+
private TextProp[] getPotentialProperties() {
return (textPropType == TextPropType.paragraph) ? paragraphTextPropTypes : characterTextPropTypes;
}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/FontCollection.java b/src/scratchpad/src/org/apache/poi/hslf/record/FontCollection.java
index 0f7f05148..b90c698dd 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/record/FontCollection.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/record/FontCollection.java
@@ -17,6 +17,7 @@
package org.apache.poi.hslf.record;
+import org.apache.poi.hslf.model.PPFont;
import org.apache.poi.util.POILogger;
import java.io.*;
@@ -75,9 +76,9 @@ public final class FontCollection extends RecordContainer {
*/
public int addFont(String name) {
int idx = getFontIndex(name);
- if(idx != -1) return idx;
+ if (idx != -1) return idx;
- return addFont(name, 0, 0, 4, 34);
+ return addFont(name, 0, 0, 4, PPFont.FF_SWISS | PPFont.VARIABLE_PITCH);
}
public int addFont(String name, int charset, int flags, int type, int pitch) {
diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideMaster.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideMaster.java
index a6dfedf07..346dab745 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideMaster.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideMaster.java
@@ -76,45 +76,31 @@ public final class HSLFSlideMaster extends HSLFMasterSheet {
* This is the "workhorse" which returns the default style attrubutes.
*/
public TextProp getStyleAttribute(int txtype, int level, String name, boolean isCharacter) {
-
+ if (_txmaster.length <= txtype) return null;
+ TxMasterStyleAtom t = _txmaster[txtype];
+ List styles = isCharacter ? t.getCharacterStyles() : t.getParagraphStyles();
+
TextProp prop = null;
- for (int i = level; i >= 0; i--) {
- List styles =
- isCharacter ? _txmaster[txtype].getCharacterStyles() : _txmaster[txtype].getParagraphStyles();
- if (i < styles.size()) prop = styles.get(i).findByName(name);
- if (prop != null) break;
+ for (int i = Math.min(level, styles.size()-1); prop == null && i >= 0; i--) {
+ prop = styles.get(i).findByName(name);
}
- if (prop == null) {
- if(isCharacter) {
- switch (txtype) {
- case TextHeaderAtom.CENTRE_BODY_TYPE:
- case TextHeaderAtom.HALF_BODY_TYPE:
- case TextHeaderAtom.QUARTER_BODY_TYPE:
- txtype = TextHeaderAtom.BODY_TYPE;
- break;
- case TextHeaderAtom.CENTER_TITLE_TYPE:
- txtype = TextHeaderAtom.TITLE_TYPE;
- break;
- default:
- return null;
- }
- } else {
- switch (txtype) {
- case TextHeaderAtom.CENTRE_BODY_TYPE:
- case TextHeaderAtom.HALF_BODY_TYPE:
- case TextHeaderAtom.QUARTER_BODY_TYPE:
- txtype = TextHeaderAtom.BODY_TYPE;
- break;
- case TextHeaderAtom.CENTER_TITLE_TYPE:
- txtype = TextHeaderAtom.TITLE_TYPE;
- break;
- default:
- return null;
- }
- }
- prop = getStyleAttribute(txtype, level, name, isCharacter);
+
+ if (prop != null) return prop;
+
+ switch (txtype) {
+ case TextHeaderAtom.CENTRE_BODY_TYPE:
+ case TextHeaderAtom.HALF_BODY_TYPE:
+ case TextHeaderAtom.QUARTER_BODY_TYPE:
+ txtype = TextHeaderAtom.BODY_TYPE;
+ break;
+ case TextHeaderAtom.CENTER_TITLE_TYPE:
+ txtype = TextHeaderAtom.TITLE_TYPE;
+ break;
+ default:
+ return null;
}
- return prop;
+
+ return getStyleAttribute(txtype, level, name, isCharacter);
}
/**
diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java
index c0d381e91..ee99745b5 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java
@@ -34,7 +34,7 @@ import org.apache.poi.util.*;
* This class represents a run of text in a powerpoint document. That
* run could be text on a sheet, or text in a note.
* It is only a very basic class for now
- *
+ *
* @author Nick Burch
*/
@@ -44,50 +44,49 @@ public final class HSLFTextParagraph implements TextParagraph {
/**
* How to align the text
*/
- /* package */ static final int AlignLeft = 0;
- /* package */ static final int AlignCenter = 1;
- /* package */ static final int AlignRight = 2;
- /* package */ static final int AlignJustify = 3;
-
+ /* package */static final int AlignLeft = 0;
+ /* package */static final int AlignCenter = 1;
+ /* package */static final int AlignRight = 2;
+ /* package */static final int AlignJustify = 3;
// Note: These fields are protected to help with unit testing
- // Other classes shouldn't really go playing with them!
- private final TextHeaderAtom _headerAtom;
- private TextBytesAtom _byteAtom;
- private TextCharsAtom _charAtom;
- private final TextPropCollection _paragraphStyle = new TextPropCollection(1, TextPropType.paragraph);
+ // Other classes shouldn't really go playing with them!
+ private final TextHeaderAtom _headerAtom;
+ private TextBytesAtom _byteAtom;
+ private TextCharsAtom _charAtom;
+ private final TextPropCollection _paragraphStyle = new TextPropCollection(1, TextPropType.paragraph);
protected TextRulerAtom _ruler;
- protected List _runs = new ArrayList();
- protected HSLFTextShape _parentShape;
+ protected final List _runs = new ArrayList();
+ protected HSLFTextShape _parentShape;
private HSLFSheet _sheet;
private int shapeId;
- // private StyleTextPropAtom styleTextPropAtom;
- private StyleTextProp9Atom styleTextProp9Atom;
+ // private StyleTextPropAtom styleTextPropAtom;
+ private StyleTextProp9Atom styleTextProp9Atom;
- /**
+ /**
* Constructs a Text Run from a Unicode text block.
* Either a {@link TextCharsAtom} or a {@link TextBytesAtom} needs to be provided.
- *
+ *
* @param tha the TextHeaderAtom that defines what's what
* @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
- */
+ */
/* package */ HSLFTextParagraph(
TextHeaderAtom tha,
TextBytesAtom tba,
TextCharsAtom tca
) {
- if (tha == null) {
- throw new IllegalArgumentException("TextHeaderAtom must be set.");
- }
- _headerAtom = tha;
+ if (tha == null) {
+ throw new IllegalArgumentException("TextHeaderAtom must be set.");
+ }
+ _headerAtom = tha;
_byteAtom = tba;
_charAtom = tca;
- }
+ }
- /* package */ HSLFTextParagraph(HSLFTextParagraph other) {
+ /* package */HSLFTextParagraph(HSLFTextParagraph other) {
_headerAtom = other._headerAtom;
_byteAtom = other._byteAtom;
_charAtom = other._charAtom;
@@ -98,67 +97,67 @@ public final class HSLFTextParagraph implements TextParagraph {
_paragraphStyle.copy(other._paragraphStyle);
}
- public void addTextRun(HSLFTextRun run) {
- _runs.add(run);
- }
+ public void addTextRun(HSLFTextRun run) {
+ _runs.add(run);
+ }
- /**
+ /**
* Fetch the rich text runs (runs of text with the same styling) that
* are contained within this block of text
- */
- public List getTextRuns() {
- return _runs;
- }
+ */
+ public List getTextRuns() {
+ return _runs;
+ }
- public TextPropCollection getParagraphStyle() {
- return _paragraphStyle;
- }
+ public TextPropCollection getParagraphStyle() {
+ return _paragraphStyle;
+ }
- public void setParagraphStyle(TextPropCollection paragraphStyle) {
- _paragraphStyle.copy(paragraphStyle);
- }
+ public void setParagraphStyle(TextPropCollection paragraphStyle) {
+ _paragraphStyle.copy(paragraphStyle);
+ }
- /**
+ /**
* Supply the Sheet we belong to, which might have an assigned SlideShow
* Also passes it on to our child RichTextRuns
*/
- public void supplySheet(HSLFSheet sheet){
+ public void supplySheet(HSLFSheet sheet) {
this._sheet = sheet;
if (_runs == null) return;
- for(HSLFTextRun rt : _runs) {
+ for (HSLFTextRun rt : _runs) {
rt.updateSheet();
}
- }
+ }
- public HSLFSheet getSheet(){
+ public HSLFSheet getSheet() {
return this._sheet;
}
/**
- * @return Shape ID
+ * @return Shape ID
*/
- protected int getShapeId(){
+ protected int getShapeId() {
return shapeId;
}
/**
- * @param id Shape ID
+ * @param id Shape ID
*/
- protected void setShapeId(int id){
+ protected void setShapeId(int id) {
shapeId = id;
}
/**
- * @return 0-based index of the text run in the SLWT container
+ * @return 0-based index of the text run in the SLWT container
*/
- protected int getIndex(){
+ protected int getIndex() {
return (_headerAtom != null) ? _headerAtom.getIndex() : -1;
}
/**
* Sets the index of the paragraph in the SLWT container
- *
+ *
* @param index
*/
protected void setIndex(int index) {
@@ -166,10 +165,10 @@ public final class HSLFTextParagraph implements TextParagraph {
}
/**
- * Returns the type of the text, from the TextHeaderAtom.
- * Possible values can be seen from TextHeaderAtom
- * @see org.apache.poi.hslf.record.TextHeaderAtom
- */
+ * Returns the type of the text, from the TextHeaderAtom.
+ * Possible values can be seen from TextHeaderAtom
+ * @see org.apache.poi.hslf.record.TextHeaderAtom
+ */
public int getRunType() {
return (_headerAtom != null) ? _headerAtom.getTextType() : -1;
}
@@ -177,8 +176,7 @@ public final class HSLFTextParagraph implements TextParagraph {
public void setRunType(int runType) {
if (_headerAtom != null) _headerAtom.setTextType(runType);
}
-
-
+
/**
* Is this Text Run one from a {@link PPDrawing}, or is it
* one from the {@link SlideListWithText}?
@@ -187,12 +185,12 @@ public final class HSLFTextParagraph implements TextParagraph {
return (getIndex() == -1);
}
- public TextRulerAtom getTextRuler(){
+ public TextRulerAtom getTextRuler() {
return _ruler;
}
- public TextRulerAtom createTextRuler(){
+ public TextRulerAtom createTextRuler() {
_ruler = getTextRuler();
if (_ruler == null) {
_ruler = TextRulerAtom.getParagraphInstance();
@@ -207,19 +205,19 @@ public final class HSLFTextParagraph implements TextParagraph {
/**
* Returns records that make up the list of text paragraphs
* (there can be misc InteractiveInfo, TxInteractiveInfo and other records)
- *
+ *
* @return text run records
*/
- public Record[] getRecords(){
+ public Record[] getRecords() {
Record r[] = _headerAtom.getParentRecord().getChildRecords();
- return getRecords(r, new int[]{0}, _headerAtom);
+ return getRecords(r, new int[] { 0 }, _headerAtom);
}
private static Record[] getRecords(Record[] records, int[] startIdx, TextHeaderAtom headerAtom) {
if (records == null) {
throw new NullPointerException("records need to be set.");
}
-
+
for (; startIdx[0] < records.length; startIdx[0]++) {
Record r = records[startIdx[0]];
if (r instanceof TextHeaderAtom && (headerAtom == null || r == headerAtom)) break;
@@ -229,135 +227,113 @@ public final class HSLFTextParagraph implements TextParagraph {
logger.log(POILogger.INFO, "header atom wasn't found - container might contain only an OutlineTextRefAtom");
return new Record[0];
}
-
+
int length;
- for (length = 1; startIdx[0]+length < records.length; length++) {
+ for (length = 1; startIdx[0] + length < records.length; length++) {
if (records[startIdx[0]+length] instanceof TextHeaderAtom) break;
}
-
+
Record result[] = new Record[length];
System.arraycopy(records, startIdx[0], result, 0, length);
startIdx[0] += length;
-
+
return result;
}
-
+
/** Numbered List info */
public void setStyleTextProp9Atom(final StyleTextProp9Atom styleTextProp9Atom) {
- this.styleTextProp9Atom = styleTextProp9Atom;
- }
+ this.styleTextProp9Atom = styleTextProp9Atom;
+ }
/** Numbered List info */
- public StyleTextProp9Atom getStyleTextProp9Atom() {
- return this.styleTextProp9Atom;
- }
-
- /**
- * Fetch the value of the given Paragraph related TextProp.
- * Returns -1 if that TextProp isn't present.
- * If the TextProp isn't present, the value from the appropriate
- * Master Sheet will apply.
- */
- private int getParaTextPropVal(String propName) {
- TextProp prop = _paragraphStyle.findByName(propName);
- BitMaskTextProp maskProp = (BitMaskTextProp)_paragraphStyle.findByName(ParagraphFlagsTextProp.NAME);
- boolean hardAttribute = (maskProp != null && maskProp.getValue() == 0);
- if (prop == null && !hardAttribute){
- HSLFSheet sheet = getSheet();
- int txtype = getRunType();
- HSLFMasterSheet master = sheet.getMasterSheet();
- if (master != null)
- prop = master.getStyleAttribute(txtype, getIndentLevel(), propName, false);
- }
-
- return prop == null ? -1 : prop.getValue();
+ public StyleTextProp9Atom getStyleTextProp9Atom() {
+ return this.styleTextProp9Atom;
}
-
- /**
- * Sets the value of the given Character TextProp, add if required
- * @param propName The name of the Character TextProp
- * @param val The value to set for the TextProp
- */
- public void setParaTextPropVal(String propName, int val) {
- // Ensure we have the StyleTextProp atom we're going to need
- assert(_paragraphStyle!=null);
- TextProp tp = fetchOrAddTextProp(_paragraphStyle, propName);
- tp.setValue(val);
- }
-
+
@Override
public Iterator iterator() {
return _runs.iterator();
}
@Override
- public double getLeftMargin() {
- int val = getParaTextPropVal("text.offset");
- return val*HSLFShape.POINT_DPI/((double)HSLFShape.MASTER_DPI);
+ public Double getLeftMargin() {
+ TextProp val = getPropVal(_paragraphStyle, "text.offset", this);
+ return (val == null) ? null : Units.masterToPoints(val.getValue());
}
@Override
- public void setLeftMargin(double leftMargin) {
- int val = (int)(leftMargin*HSLFShape.MASTER_DPI/HSLFShape.POINT_DPI);
- setParaTextPropVal("text.offset", val);
+ public void setLeftMargin(Double leftMargin) {
+ Integer val = (leftMargin == null) ? null : Units.pointsToMaster(leftMargin);
+ setPropVal(_paragraphStyle, "text.offset", val);
}
@Override
- public double getRightMargin() {
+ public Double getRightMargin() {
// TODO: find out, how to determine this value
- return 0;
+ return null;
}
@Override
- public void setRightMargin(double rightMargin) {
+ public void setRightMargin(Double rightMargin) {
// TODO: find out, how to set this value
}
@Override
- public double getIndent() {
- int val = getParaTextPropVal("bullet.offset");
- return val*HSLFShape.POINT_DPI/((double)HSLFShape.MASTER_DPI);
+ public Double getIndent() {
+ TextProp val = getPropVal(_paragraphStyle, "bullet.offset", this);
+ return (val == null) ? null : Units.masterToPoints(val.getValue());
}
@Override
- public void setIndent(double intent) {
- int val = (int)(intent*HSLFShape.MASTER_DPI/HSLFShape.POINT_DPI);
- setParaTextPropVal("bullet.offset", val);
+ public void setIndent(Double indent) {
+ Integer val = (indent == null) ? null : Units.pointsToMaster(indent);
+ setPropVal(_paragraphStyle, "bullet.offset", val);
}
@Override
public String getDefaultFontFamily() {
- return (_runs.isEmpty() ? "Arial" : _runs.get(0).getFontFamily());
+ String typeface = null;
+ if (!_runs.isEmpty()) {
+ typeface = _runs.get(0).getFontFamily();
+ }
+ return (typeface != null) ? typeface : "Arial";
}
@Override
- public double getDefaultFontSize() {
- return (_runs.isEmpty() ? 12 : _runs.get(0).getFontSize());
+ public Double getDefaultFontSize() {
+ Double d = null;
+ if (!_runs.isEmpty()) {
+ d = _runs.get(0).getFontSize();
+ }
+
+ return (d != null) ? d : 12d;
}
/**
* Sets the type of horizontal alignment for the paragraph.
- *
+ *
* @param align - the type of alignment
*/
public void setAlignment(org.apache.poi.sl.usermodel.TextParagraph.TextAlign align) {
- int alignInt;
- switch (align) {
- default:
- case LEFT: alignInt = TextAlignmentProp.LEFT; break;
- case CENTER: alignInt = TextAlignmentProp.CENTER; break;
- case RIGHT: alignInt = TextAlignmentProp.RIGHT; break;
- case DIST: alignInt = TextAlignmentProp.DISTRIBUTED; break;
- case JUSTIFY: alignInt = TextAlignmentProp.JUSTIFY; break;
- case JUSTIFY_LOW: alignInt = TextAlignmentProp.JUSTIFYLOW; break;
- case THAI_DIST: alignInt = TextAlignmentProp.THAIDISTRIBUTED; break;
+ Integer alignInt = null;
+ if (align != null) switch (align) {
+ default:
+ case LEFT: alignInt = TextAlignmentProp.LEFT;break;
+ case CENTER: alignInt = TextAlignmentProp.CENTER; break;
+ case RIGHT: alignInt = TextAlignmentProp.RIGHT; break;
+ case DIST: alignInt = TextAlignmentProp.DISTRIBUTED; break;
+ case JUSTIFY: alignInt = TextAlignmentProp.JUSTIFY; break;
+ case JUSTIFY_LOW: alignInt = TextAlignmentProp.JUSTIFYLOW; break;
+ case THAI_DIST: alignInt = TextAlignmentProp.THAIDISTRIBUTED; break;
}
- setParaTextPropVal("alignment", alignInt);
+ setPropVal(_paragraphStyle, "alignment", alignInt);
}
@Override
public org.apache.poi.sl.usermodel.TextParagraph.TextAlign getTextAlign() {
- switch (getParaTextPropVal("alignment")) {
+ TextProp tp = getPropVal(_paragraphStyle, "alignment", this);
+ if (tp == null) return null;
+ switch (tp.getValue()) {
default:
case TextAlignmentProp.LEFT: return TextAlign.LEFT;
case TextAlignmentProp.CENTER: return TextAlign.CENTER;
@@ -371,34 +347,33 @@ public final class HSLFTextParagraph implements TextParagraph {
@Override
public FontAlign getFontAlign() {
- switch(getParaTextPropVal("fontAlign")) {
- default:
- case -1: return FontAlign.AUTO;
+ TextProp tp = getPropVal(_paragraphStyle, FontAlignmentProp.NAME, this);
+ if (tp == null) return null;
+
+ switch (tp.getValue()) {
case FontAlignmentProp.BASELINE: return FontAlign.BASELINE;
case FontAlignmentProp.TOP: return FontAlign.TOP;
case FontAlignmentProp.CENTER: return FontAlign.CENTER;
case FontAlignmentProp.BOTTOM: return FontAlign.BOTTOM;
+ default: return FontAlign.AUTO;
}
}
@Override
public BulletStyle getBulletStyle() {
- if (getBulletChar() == 0) return null;
+ if (!isBullet()) return null;
return new BulletStyle() {
public String getBulletCharacter() {
- char chr = HSLFTextParagraph.this.getBulletChar();
- return (chr == 0 ? "" : ""+chr);
+ Character chr = HSLFTextParagraph.this.getBulletChar();
+ return (chr == null || chr == 0) ? "" : "" + chr;
}
public String getBulletFont() {
- int fontIdx = HSLFTextParagraph.this.getBulletFont();
- if (fontIdx == -1) return getDefaultFontFamily();
- PPFont ppFont = getSheet().getSlideShow().getFont(fontIdx);
- return ppFont.getFontName();
+ return HSLFTextParagraph.this.getBulletFont();
}
- public double getBulletFontSize() {
+ public Double getBulletFontSize() {
return HSLFTextParagraph.this.getBulletSize();
}
@@ -417,666 +392,669 @@ public final class HSLFTextParagraph implements TextParagraph {
_parentShape = parentShape;
}
-
/**
- *
- * @return indentation level
- */
- public int getIndentLevel() {
- return _paragraphStyle == null ? 0 : _paragraphStyle.getIndentLevel();
- }
-
- /**
- * Sets indentation level
- *
- * @param level indentation level. Must be in the range [0, 4]
- */
- public void setIndentLevel(int level) {
- if( _paragraphStyle != null ) _paragraphStyle.setIndentLevel((short)level);
- }
-
- /**
- * Sets whether this rich text run has bullets
- */
- public void setBullet(boolean flag) {
- setFlag(ParagraphFlagsTextProp.BULLET_IDX, flag);
- }
-
- /**
- * Returns whether this rich text run has bullets
- */
- public boolean isBullet() {
- return getFlag(ParagraphFlagsTextProp.BULLET_IDX);
- }
-
- /**
- * Returns whether this rich text run has bullets
- */
- public boolean isBulletHard() {
- return getFlag(ParagraphFlagsTextProp.BULLET_IDX);
- }
-
- /**
- * Sets the bullet character
- */
- public void setBulletChar(char c) {
- setParaTextPropVal("bullet.char", c);
- }
-
- /**
- * Returns the bullet character
- */
- public char getBulletChar() {
- int val = getParaTextPropVal("bullet.char");
- return (char)(val == -1 ? 0 : val);
- }
-
- /**
- * Sets the bullet size
- */
- public void setBulletSize(int size) {
- setParaTextPropVal("bullet.size", size);
- }
-
- /**
- * Returns the bullet size
- */
- public int getBulletSize() {
- return getParaTextPropVal("bullet.size");
- }
-
- /**
- * Sets the bullet color
- */
- public void setBulletColor(Color color) {
- int rgb = new Color(color.getBlue(), color.getGreen(), color.getRed(), 254).getRGB();
- setParaTextPropVal("bullet.color", rgb);
- }
-
- /**
- * Returns the bullet color
- */
- public Color getBulletColor() {
- int rgb = getParaTextPropVal("bullet.color");
- if (rgb == -1) {
- // if bullet color is undefined, return color of first run
- if (_runs.isEmpty()) return null;
- return _runs.get(0).getFontColor();
- }
-
- int cidx = rgb >> 24;
- if (rgb % 0x1000000 == 0){
- if (_sheet == null) return null;
- ColorSchemeAtom ca = _sheet.getColorScheme();
- if(cidx >= 0 && cidx <= 7) rgb = ca.getColor(cidx);
- }
- Color tmp = new Color(rgb, true);
- return new Color(tmp.getBlue(), tmp.getGreen(), tmp.getRed());
- }
-
- /**
- * Sets the bullet font
- */
- public void setBulletFont(int idx) {
- setParaTextPropVal("bullet.font", idx);
- setFlag(ParagraphFlagsTextProp.BULLET_HARDFONT_IDX, true);
- }
-
- /**
- * Returns the bullet font
- */
- public int getBulletFont() {
- return getParaTextPropVal("bullet.font");
- }
-
- @Override
- public void setLineSpacing(double lineSpacing) {
- // if lineSpacing < 0, we need to convert points (common interface) to master units (hslf)
- if (lineSpacing < 0) {
- lineSpacing = (lineSpacing*HSLFShape.MASTER_DPI/HSLFShape.POINT_DPI);
- }
- setParaTextPropVal("linespacing", (int)lineSpacing);
- }
-
- @Override
- public double getLineSpacing() {
- double val = getParaTextPropVal("linespacing");
- // if lineSpacing < 0, we need to convert master units (hslf) to points (common interface)
- if (val == -1) return 0;
- if (val < -1) val *= HSLFShape.POINT_DPI/((double)HSLFShape.MASTER_DPI);
- return val;
- }
-
- /**
- * Sets spacing before a paragraph.
- *
- * If spacebefore >= 0, then spacebefore is a percentage of normal line height.
- * If spacebefore < 0, the absolute value of spacebefore is the spacing in master coordinates.
- *
- */
- public void setSpaceBefore(int val) {
- setParaTextPropVal("spacebefore", val);
- }
-
- /**
- * Returns spacing before a paragraph
- *
- * If spacebefore >= 0, then spacebefore is a percentage of normal line height.
- * If spacebefore < 0, the absolute value of spacebefore is the spacing in master coordinates.
- *
- *
- * @return the spacing before a paragraph
- */
- @Override
- public double getSpaceBefore() {
- int val = getParaTextPropVal("spacebefore");
- return val == -1 ? 0 : val;
- }
-
- /**
- * Sets spacing after a paragraph.
- *
- * If spaceafter >= 0, then spaceafter is a percentage of normal line height.
- * If spaceafter < 0, the absolute value of spaceafter is the spacing in master coordinates.
- *
- */
- public void setSpaceAfter(int val) {
- setParaTextPropVal("spaceafter", val);
- }
-
- /**
- * Returns spacing after a paragraph
- *
- * If spaceafter >= 0, then spaceafter is a percentage of normal line height.
- * If spaceafter < 0, the absolute value of spaceafter is the spacing in master coordinates.
- *
- *
- * @return the spacing before a paragraph
- */
- @Override
- public double getSpaceAfter() {
- int val = getParaTextPropVal("spaceafter");
- return val == -1 ? 0 : val;
- }
-
- /**
- * Returns the named TextProp, either by fetching it (if it exists) or adding it
- * (if it didn't)
- * @param textPropCol The TextPropCollection to fetch from / add into
- * @param textPropName The name of the TextProp to fetch/add
- */
- protected static TextProp fetchOrAddTextProp(TextPropCollection textPropCol, String textPropName) {
- // Fetch / Add the TextProp
- return textPropCol.addWithName(textPropName);
+ *
+ * @return indentation level
+ */
+ public int getIndentLevel() {
+ return _paragraphStyle == null ? 0 : _paragraphStyle.getIndentLevel();
}
- protected boolean getFlag(int index) {
- if (_paragraphStyle == null) return false;
+ /**
+ * Sets indentation level
+ *
+ * @param level indentation level. Must be in the range [0, 4]
+ */
+ public void setIndentLevel(int level) {
+ if( _paragraphStyle != null ) _paragraphStyle.setIndentLevel((short)level);
+ }
- BitMaskTextProp prop = (BitMaskTextProp) _paragraphStyle.findByName(ParagraphFlagsTextProp.NAME);
+ /**
+ * Sets whether this rich text run has bullets
+ */
+ public void setBullet(boolean flag) {
+ setFlag(ParagraphFlagsTextProp.BULLET_IDX, flag);
+ }
- if (prop == null) {
- if (_sheet != null) {
- int txtype = getRunType();
- HSLFMasterSheet master = _sheet.getMasterSheet();
- if (master != null) {
- prop = (BitMaskTextProp) master.getStyleAttribute(txtype, getIndentLevel(), ParagraphFlagsTextProp.NAME, false);
- }
- } else {
- logger.log(POILogger.WARN, "MasterSheet is not available");
+ /**
+ * Returns whether this rich text run has bullets
+ */
+ public boolean isBullet() {
+ return getFlag(ParagraphFlagsTextProp.BULLET_IDX);
+ }
+
+ /**
+ * Sets the bullet character
+ */
+ public void setBulletChar(Character c) {
+ Integer val = (c == null) ? null : (int)c.charValue();
+ setPropVal(_paragraphStyle, "bullet.char", val);
+ }
+
+ /**
+ * Returns the bullet character
+ */
+ public Character getBulletChar() {
+ TextProp tp = getPropVal(_paragraphStyle, "bullet.char", this);
+ return (tp == null) ? null : (char)tp.getValue();
+ }
+
+ /**
+ * Sets the bullet size
+ */
+ public void setBulletSize(Double size) {
+ setPctOrPoints("bullet.size", size);
+ }
+
+ /**
+ * Returns the bullet size, null if unset
+ */
+ public Double getBulletSize() {
+ return getPctOrPoints("bullet.size");
+ }
+
+ /**
+ * Sets the bullet color
+ */
+ public void setBulletColor(Color color) {
+ Integer val = (color == null) ? null : new Color(color.getBlue(), color.getGreen(), color.getRed(), 254).getRGB();
+ setPropVal(_paragraphStyle, "bullet.color", val);
+ }
+
+ /**
+ * Returns the bullet color
+ */
+ public Color getBulletColor() {
+ TextProp tp = getPropVal(_paragraphStyle, "bullet.color", this);
+ if (tp == null) {
+ // if bullet color is undefined, return color of first run
+ return (_runs.isEmpty()) ? null : _runs.get(0).getFontColor();
+ }
+
+ int rgb = tp.getValue();
+ int cidx = rgb >> 24;
+ if (rgb % 0x1000000 == 0) {
+ if (_sheet == null)
+ return null;
+ ColorSchemeAtom ca = _sheet.getColorScheme();
+ if (cidx >= 0 && cidx <= 7)
+ rgb = ca.getColor(cidx);
+ }
+ Color tmp = new Color(rgb, true);
+ return new Color(tmp.getBlue(), tmp.getGreen(), tmp.getRed());
+ }
+
+ /**
+ * Sets the bullet font
+ */
+ public void setBulletFont(String typeface) {
+ if (typeface == null) {
+ setPropVal(_paragraphStyle, "bullet.font", null);
+ setFlag(ParagraphFlagsTextProp.BULLET_HARDFONT_IDX, false);
+ }
+
+ FontCollection fc = getSheet().getSlideShow().getFontCollection();
+ int idx = fc.addFont(typeface);
+
+ setPropVal(_paragraphStyle, "bullet.font", idx);
+ setFlag(ParagraphFlagsTextProp.BULLET_HARDFONT_IDX, true);
+ }
+
+ /**
+ * Returns the bullet font
+ */
+ public String getBulletFont() {
+ TextProp tp = getPropVal(_paragraphStyle, "bullet.font", this);
+ if (tp == null) return getDefaultFontFamily();
+ PPFont ppFont = getSheet().getSlideShow().getFont(tp.getValue());
+ assert(ppFont != null);
+ return ppFont.getFontName();
+ }
+
+ @Override
+ public void setLineSpacing(Double lineSpacing) {
+ setPctOrPoints("linespacing", lineSpacing);
+ }
+
+ @Override
+ public Double getLineSpacing() {
+ return getPctOrPoints("linespacing");
+ }
+
+ @Override
+ public void setSpaceBefore(Double spaceBefore) {
+ setPctOrPoints("spacebefore", spaceBefore);
+ }
+
+ @Override
+ public Double getSpaceBefore() {
+ return getPctOrPoints("spacebefore");
+ }
+
+ @Override
+ public void setSpaceAfter(Double spaceAfter) {
+ setPctOrPoints("spaceafter", spaceAfter);
+ }
+
+ @Override
+ public Double getSpaceAfter() {
+ return getPctOrPoints("spaceafter");
+ }
+
+ @Override
+ public Double getDefaultTabSize() {
+ // TODO: implement
+ return null;
+ }
+
+ private Double getPctOrPoints(String propName) {
+ TextProp tp = getPropVal(_paragraphStyle, propName, this);
+ if (tp == null) return null;
+ int val = tp.getValue();
+ return (val < 0) ? Units.masterToPoints(val) : val;
+ }
+
+ private void setPctOrPoints(String propName, Double dval) {
+ Integer ival = null;
+ if (dval != null) {
+ ival = (dval < 0) ? Units.pointsToMaster(dval) : dval.intValue();
+ }
+ setPropVal(_paragraphStyle, propName, ival);
+ }
+
+ private boolean getFlag(int index) {
+ BitMaskTextProp tp = (BitMaskTextProp)getPropVal(_paragraphStyle, ParagraphFlagsTextProp.NAME, this);
+ return (tp == null) ? false : tp.getSubValue(index);
+ }
+
+ private void setFlag(int index, boolean value) {
+ BitMaskTextProp tp = (BitMaskTextProp)_paragraphStyle.addWithName(ParagraphFlagsTextProp.NAME);
+ tp.setSubValue(value, index);
+ }
+
+ /**
+ * Fetch the value of the given Paragraph related TextProp. Returns null if
+ * that TextProp isn't present. If the TextProp isn't present, the value
+ * from the appropriate Master Sheet will apply.
+ */
+ protected static TextProp getPropVal(TextPropCollection props, String propName, HSLFTextParagraph paragraph) {
+ TextProp prop = props.findByName(propName);
+ if (prop != null) return prop;
+
+ BitMaskTextProp maskProp = (BitMaskTextProp) props.findByName(ParagraphFlagsTextProp.NAME);
+ boolean hardAttribute = (maskProp != null && maskProp.getValue() == 0);
+ if (hardAttribute) return null;
+
+ HSLFSheet sheet = paragraph.getSheet();
+ int txtype = paragraph.getRunType();
+ HSLFMasterSheet master = sheet.getMasterSheet();
+ if (master == null) {
+ logger.log(POILogger.WARN, "MasterSheet is not available");
+ return null;
+ }
+
+ boolean isChar = props.getTextPropType() == TextPropType.character;
+ return master.getStyleAttribute(txtype, paragraph.getIndentLevel(), propName, isChar);
+ }
+
+ /**
+ * Returns the named TextProp, either by fetching it (if it exists) or
+ * adding it (if it didn't)
+ *
+ * @param props the TextPropCollection to fetch from / add into
+ * @param name the name of the TextProp to fetch/add
+ * @param val the value, null if unset
+ */
+ protected static void setPropVal(TextPropCollection props, String name, Integer val) {
+ if (val == null) {
+ props.removeByName(name);
+ return;
+ }
+
+ // Fetch / Add the TextProp
+ TextProp tp = props.addWithName(name);
+ tp.setValue(val);
+ }
+
+ /**
+ * Check and add linebreaks to text runs leading other paragraphs
+ *
+ * @param paragraphs
+ */
+ protected static void fixLineEndings(List paragraphs) {
+ HSLFTextRun lastRun = null;
+ for (HSLFTextParagraph p : paragraphs) {
+ if (lastRun != null && !lastRun.getRawText().endsWith("\r")) {
+ lastRun.setText(lastRun.getRawText() + "\r");
+ }
+ List ltr = p.getTextRuns();
+ if (ltr.isEmpty()) {
+ throw new RuntimeException("paragraph without textruns found");
+ }
+ lastRun = ltr.get(ltr.size() - 1);
+ assert (lastRun.getRawText() != null);
+ }
+ }
+
+ /**
+ * Search for a StyleTextPropAtom is for this text header (list of paragraphs)
+ *
+ * @param header the header
+ * @param textLen the length of the rawtext, or -1 if the length is not known
+ */
+ private static StyleTextPropAtom findStyleAtomPresent(TextHeaderAtom header, int textLen) {
+ boolean afterHeader = false;
+ StyleTextPropAtom style = null;
+ for (Record record : header.getParentRecord().getChildRecords()) {
+ long rt = record.getRecordType();
+ if (afterHeader && rt == RecordTypes.TextHeaderAtom.typeID) {
+ // already on the next header, quit searching
+ break;
+ }
+ afterHeader |= (header == record);
+ if (afterHeader && rt == RecordTypes.StyleTextPropAtom.typeID) {
+ // found it
+ style = (StyleTextPropAtom) record;
}
}
- return prop == null ? false : prop.getSubValue(index);
+ if (style == null) {
+ logger.log(POILogger.INFO, "styles atom doesn't exist. Creating dummy record for later saving.");
+ style = new StyleTextPropAtom((textLen < 0) ? 1 : textLen);
+ } else {
+ if (textLen >= 0) {
+ style.setParentTextSize(textLen);
+ }
+ }
+
+ return style;
}
- protected void setFlag(int index, boolean value) {
- // Ensure we have the StyleTextProp atom we're going to need
- assert(_paragraphStyle!=null);
- BitMaskTextProp prop = (BitMaskTextProp) fetchOrAddTextProp(_paragraphStyle, ParagraphFlagsTextProp.NAME);
- prop.setSubValue(value,index);
- }
+ /**
+ * Saves the modified paragraphs/textrun to the records.
+ * Also updates the styles to the correct text length.
+ */
+ protected static void storeText(List paragraphs) {
+ fixLineEndings(paragraphs);
- /**
- * Check and add linebreaks to text runs leading other paragraphs
- *
- * @param paragraphs
- */
- protected static void fixLineEndings(List paragraphs) {
- HSLFTextRun lastRun = null;
- for (HSLFTextParagraph p : paragraphs) {
- if (lastRun != null && !lastRun.getRawText().endsWith("\r")) {
- lastRun.setText(lastRun.getRawText()+"\r");
- }
- List ltr = p.getTextRuns();
- if (ltr.isEmpty()) {
- throw new RuntimeException("paragraph without textruns found");
- }
- lastRun = ltr.get(ltr.size()-1);
- assert(lastRun.getRawText() != null);
- }
- }
+ String rawText = toInternalString(getRawText(paragraphs));
- /**
- * Search for a StyleTextPropAtom is for this text header (list of paragraphs)
- *
- * @param header the header
- * @param textLen the length of the rawtext, or -1 if the length is not known
- */
- private static StyleTextPropAtom findStyleAtomPresent(TextHeaderAtom header, int textLen) {
- boolean afterHeader = false;
- StyleTextPropAtom style = null;
- for (Record record : header.getParentRecord().getChildRecords()) {
- long rt = record.getRecordType();
- if (afterHeader && rt == RecordTypes.TextHeaderAtom.typeID) {
- // already on the next header, quit searching
- break;
- }
- afterHeader |= (header == record);
- if (afterHeader && rt == RecordTypes.StyleTextPropAtom.typeID) {
- // found it
- style = (StyleTextPropAtom)record;
- }
- }
+ // Will it fit in a 8 bit atom?
+ boolean isUnicode = StringUtil.hasMultibyte(rawText);
+ // isUnicode = true;
- if (style == null) {
- logger.log(POILogger.INFO, "styles atom doesn't exist. Creating dummy record for later saving.");
- style = new StyleTextPropAtom((textLen < 0) ? 1 : textLen);
- } else {
- if (textLen >= 0) {
- style.setParentTextSize(textLen);
- }
- }
-
- return style;
- }
+ TextHeaderAtom headerAtom = paragraphs.get(0)._headerAtom;
+ TextBytesAtom byteAtom = paragraphs.get(0)._byteAtom;
+ TextCharsAtom charAtom = paragraphs.get(0)._charAtom;
+ StyleTextPropAtom styleAtom = findStyleAtomPresent(headerAtom, rawText.length());
+ // Store in the appropriate record
+ Record oldRecord = null, newRecord = null;
+ if (isUnicode) {
+ if (byteAtom != null || charAtom == null) {
+ oldRecord = byteAtom;
+ charAtom = new TextCharsAtom();
+ }
+ newRecord = charAtom;
+ charAtom.setText(rawText);
+ } else {
+ if (charAtom != null || byteAtom == null) {
+ oldRecord = charAtom;
+ byteAtom = new TextBytesAtom();
+ }
+ newRecord = byteAtom;
+ byte[] byteText = new byte[rawText.length()];
+ StringUtil.putCompressedUnicode(rawText, byteText, 0);
+ byteAtom.setText(byteText);
+ }
+ assert (newRecord != null);
- /**
- * Saves the modified paragraphs/textrun to the records.
- * Also updates the styles to the correct text length.
- */
- protected static void storeText(List paragraphs) {
- fixLineEndings(paragraphs);
+ RecordContainer _txtbox = headerAtom.getParentRecord();
+ Record[] cr = _txtbox.getChildRecords();
+ int headerIdx = -1, textIdx = -1, styleIdx = -1;
+ for (int i = 0; i < cr.length; i++) {
+ Record r = cr[i];
+ if (r == headerAtom) headerIdx = i;
+ else if (r == oldRecord || r == newRecord) textIdx = i;
+ else if (r == styleAtom) styleIdx = i;
+ }
- String rawText = toInternalString(getRawText(paragraphs));
+ if (textIdx == -1) {
+ // the old record was never registered, ignore it
+ _txtbox.addChildAfter(newRecord, headerAtom);
+ textIdx = headerIdx + 1;
+ } else {
+ // swap not appropriated records - noop if unchanged
+ cr[textIdx] = newRecord;
+ }
- // Will it fit in a 8 bit atom?
- boolean isUnicode = StringUtil.hasMultibyte(rawText);
- // isUnicode = true;
+ if (styleIdx == -1) {
+ // Add the new StyleTextPropAtom after the TextCharsAtom / TextBytesAtom
+ _txtbox.addChildAfter(styleAtom, newRecord);
+ }
- TextHeaderAtom headerAtom = paragraphs.get(0)._headerAtom;
- TextBytesAtom byteAtom = paragraphs.get(0)._byteAtom;
- TextCharsAtom charAtom = paragraphs.get(0)._charAtom;
- StyleTextPropAtom styleAtom = findStyleAtomPresent(headerAtom, rawText.length());
+ for (HSLFTextParagraph p : paragraphs) {
+ if (newRecord == byteAtom) {
+ p._byteAtom = byteAtom;
+ p._charAtom = null;
+ } else {
+ p._byteAtom = null;
+ p._charAtom = charAtom;
+ }
+ }
- // Store in the appropriate record
- Record oldRecord = null, newRecord = null;
- if (isUnicode) {
- if (byteAtom != null || charAtom == null) {
- oldRecord = byteAtom;
- charAtom = new TextCharsAtom();
- }
- newRecord = charAtom;
- charAtom.setText(rawText);
- } else {
- if (charAtom != null || byteAtom == null) {
- oldRecord = charAtom;
- byteAtom = new TextBytesAtom();
- }
- newRecord = byteAtom;
- byte[] byteText = new byte[rawText.length()];
- StringUtil.putCompressedUnicode(rawText,byteText,0);
- byteAtom.setText(byteText);
- }
- assert(newRecord != null);
-
- RecordContainer _txtbox = headerAtom.getParentRecord();
- Record[] cr = _txtbox.getChildRecords();
- int headerIdx = -1, textIdx = -1, styleIdx = -1;
- for (int i=0; i paragraphs, String text, boolean newParagraph) {
+ text = toInternalString(text);
- /**
- * Adds the supplied text onto the end of the TextParagraphs,
- * creating a new RichTextRun for it to sit in.
- *
- * @param text the text string used by this object.
- */
- protected static HSLFTextRun appendText(List paragraphs, String text, boolean newParagraph) {
- text = toInternalString(text);
+ // check paragraphs
+ assert(!paragraphs.isEmpty() && !paragraphs.get(0).getTextRuns().isEmpty());
- // check paragraphs
- assert(!paragraphs.isEmpty() && !paragraphs.get(0).getTextRuns().isEmpty());
+ HSLFTextParagraph htp = paragraphs.get(paragraphs.size() - 1);
+ HSLFTextRun htr = htp.getTextRuns().get(htp.getTextRuns().size() - 1);
- HSLFTextParagraph htp = paragraphs.get(paragraphs.size()-1);
- HSLFTextRun htr = htp.getTextRuns().get(htp.getTextRuns().size()-1);
+ boolean isFirst = !newParagraph;
+ for (String rawText : text.split("(?<=\r)")) {
+ if (!isFirst) {
+ TextPropCollection tpc = htp.getParagraphStyle();
+ HSLFTextParagraph prevHtp = htp;
+ htp = new HSLFTextParagraph(htp._headerAtom, htp._byteAtom, htp._charAtom);
+ htp.getParagraphStyle().copy(tpc);
+ htp.setParentShape(prevHtp.getParentShape());
+ htp.setShapeId(prevHtp.getShapeId());
+ htp.supplySheet(prevHtp.getSheet());
+ paragraphs.add(htp);
+ }
+ isFirst = false;
- boolean isFirst = !newParagraph;
- for (String rawText : text.split("(?<=\r)")) {
- if (!isFirst) {
- TextPropCollection tpc = htp.getParagraphStyle();
- HSLFTextParagraph prevHtp = htp;
- htp = new HSLFTextParagraph(htp._headerAtom, htp._byteAtom, htp._charAtom);
- htp.getParagraphStyle().copy(tpc);
- htp.setParentShape(prevHtp.getParentShape());
- htp.setShapeId(prevHtp.getShapeId());
- htp.supplySheet(prevHtp.getSheet());
- paragraphs.add(htp);
- }
- isFirst = false;
-
- TextPropCollection tpc = htr.getCharacterStyle();
- // special case, last text run is empty, we will reuse it
- if (htr.getLength() > 0) {
- htr = new HSLFTextRun(htp);
- htr.getCharacterStyle().copy(tpc);
- htp.addTextRun(htr);
- }
- htr.setText(rawText);
- }
-
- storeText(paragraphs);
+ TextPropCollection tpc = htr.getCharacterStyle();
+ // special case, last text run is empty, we will reuse it
+ if (htr.getLength() > 0) {
+ htr = new HSLFTextRun(htp);
+ htr.getCharacterStyle().copy(tpc);
+ htp.addTextRun(htr);
+ }
+ htr.setText(rawText);
+ }
- return htr;
- }
+ storeText(paragraphs);
- /**
- * Sets (overwrites) the current text.
- * Uses the properties of the first paragraph / textrun
- *
- * @param text the text string used by this object.
- */
- public static HSLFTextRun setText(List paragraphs, String text) {
- // check paragraphs
- assert(!paragraphs.isEmpty() && !paragraphs.get(0).getTextRuns().isEmpty());
+ return htr;
+ }
- Iterator paraIter = paragraphs.iterator();
- HSLFTextParagraph htp = paraIter.next(); // keep first
- assert(htp != null);
- while (paraIter.hasNext()) {
- paraIter.next();
- paraIter.remove();
- }
+ /**
+ * Sets (overwrites) the current text.
+ * Uses the properties of the first paragraph / textrun
+ *
+ * @param text the text string used by this object.
+ */
+ public static HSLFTextRun setText(List paragraphs, String text) {
+ // check paragraphs
+ assert(!paragraphs.isEmpty() && !paragraphs.get(0).getTextRuns().isEmpty());
- Iterator runIter = htp.getTextRuns().iterator();
- HSLFTextRun htr = runIter.next();
- htr.setText("");
- assert(htr != null);
- while (runIter.hasNext()) {
- runIter.next();
- runIter.remove();
- }
+ Iterator paraIter = paragraphs.iterator();
+ HSLFTextParagraph htp = paraIter.next(); // keep first
+ assert (htp != null);
+ while (paraIter.hasNext()) {
+ paraIter.next();
+ paraIter.remove();
+ }
- return appendText(paragraphs, text, false);
- }
+ Iterator runIter = htp.getTextRuns().iterator();
+ HSLFTextRun htr = runIter.next();
+ htr.setText("");
+ assert (htr != null);
+ while (runIter.hasNext()) {
+ runIter.next();
+ runIter.remove();
+ }
- public static String getText(List paragraphs) {
- assert(!paragraphs.isEmpty());
- String rawText = getRawText(paragraphs);
- return toExternalString(rawText, paragraphs.get(0).getRunType());
- }
-
- public static String getRawText(List paragraphs) {
- StringBuilder sb = new StringBuilder();
- for (HSLFTextParagraph p : paragraphs) {
- for (HSLFTextRun r : p.getTextRuns()) {
- sb.append(r.getRawText());
- }
- }
- return sb.toString();
- }
+ return appendText(paragraphs, text, false);
+ }
- /**
- * Returns a new string with line breaks converted into internal ppt
- * representation
- */
- protected static String toInternalString(String s) {
- String ns = s.replaceAll("\\r?\\n", "\r");
- return ns;
- }
+ public static String getText(List paragraphs) {
+ assert (!paragraphs.isEmpty());
+ String rawText = getRawText(paragraphs);
+ return toExternalString(rawText, paragraphs.get(0).getRunType());
+ }
- /**
- * Converts raw text from the text paragraphs to a formatted string,
- * i.e. it converts certain control characters used in the raw txt
- *
- * @param rawText the raw text
- * @param runType the run type of the shape, paragraph or headerAtom.
- * use -1 if unknown
- * @return the formatted string
- */
- public static String toExternalString(String rawText, int runType) {
- // PowerPoint seems to store files with \r as the line break
- // The messes things up on everything but a Mac, so translate
- // them to \n
- String text = rawText.replace('\r', '\n');
+ public static String getRawText(List paragraphs) {
+ StringBuilder sb = new StringBuilder();
+ for (HSLFTextParagraph p : paragraphs) {
+ for (HSLFTextRun r : p.getTextRuns()) {
+ sb.append(r.getRawText());
+ }
+ }
+ return sb.toString();
+ }
- switch (runType) {
- // 0xB acts like cariage return in page titles and like blank in the
- // others
- case -1:
- case org.apache.poi.hslf.record.TextHeaderAtom.TITLE_TYPE:
- case org.apache.poi.hslf.record.TextHeaderAtom.CENTER_TITLE_TYPE:
- text = text.replace((char) 0x0B, '\n');
- break;
- default:
- text = text.replace((char) 0x0B, ' ');
- break;
- }
+ /**
+ * Returns a new string with line breaks converted into internal ppt
+ * representation
+ */
+ protected static String toInternalString(String s) {
+ String ns = s.replaceAll("\\r?\\n", "\r");
+ return ns;
+ }
- return text;
- }
+ /**
+ * Converts raw text from the text paragraphs to a formatted string,
+ * i.e. it converts certain control characters used in the raw txt
+ *
+ * @param rawText the raw text
+ * @param runType the run type of the shape, paragraph or headerAtom.
+ * use -1 if unknown
+ * @return the formatted string
+ */
+ public static String toExternalString(String rawText, int runType) {
+ // PowerPoint seems to store files with \r as the line break
+ // The messes things up on everything but a Mac, so translate
+ // them to \n
+ String text = rawText.replace('\r', '\n');
- /**
- * For a given PPDrawing, grab all the TextRuns
- */
+ switch (runType) {
+ // 0xB acts like cariage return in page titles and like blank in the
+ // others
+ case -1:
+ case org.apache.poi.hslf.record.TextHeaderAtom.TITLE_TYPE:
+ case org.apache.poi.hslf.record.TextHeaderAtom.CENTER_TITLE_TYPE:
+ text = text.replace((char) 0x0B, '\n');
+ break;
+ default:
+ text = text.replace((char) 0x0B, ' ');
+ break;
+ }
+
+ return text;
+ }
+
+ /**
+ * For a given PPDrawing, grab all the TextRuns
+ */
public static List> findTextParagraphs(PPDrawing ppdrawing, HSLFSheet sheet) {
- List> runsV = new ArrayList>();
- for (EscherTextboxWrapper wrapper : ppdrawing.getTextboxWrappers()) {
- runsV.add(findTextParagraphs(wrapper, sheet));
- }
- return runsV;
- }
+ List> runsV = new ArrayList>();
+ for (EscherTextboxWrapper wrapper : ppdrawing.getTextboxWrappers()) {
+ runsV.add(findTextParagraphs(wrapper, sheet));
+ }
+ return runsV;
+ }
- /**
- * Scans through the supplied record array, looking for
- * a TextHeaderAtom followed by one of a TextBytesAtom or
- * a TextCharsAtom. Builds up TextRuns from these
- *
- * @param wrapper an EscherTextboxWrapper
- */
- protected static List findTextParagraphs(EscherTextboxWrapper wrapper, HSLFSheet sheet) {
- // propagate parents to parent-aware records
- RecordContainer.handleParentAwareRecords(wrapper);
- int shapeId = wrapper.getShapeId();
- List rv = null;
-
- OutlineTextRefAtom ota = (OutlineTextRefAtom)wrapper.findFirstOfType(OutlineTextRefAtom.typeID);
- if (ota != null) {
- // if we are based on an outline, there are no further records to be parsed from the wrapper
- if (sheet == null) {
- throw new RuntimeException("Outline atom reference can't be solved without a sheet record");
- }
-
- List> sheetRuns = sheet.getTextParagraphs();
- assert(sheetRuns != null);
-
- int idx = ota.getTextIndex();
- for (List r : sheetRuns) {
- if (r.isEmpty()) continue;
- int ridx = r.get(0).getIndex();
- if (ridx > idx) break;
- if (ridx == idx) {
- if (rv == null) {
- rv = r;
- } else {
- // create a new container
- // TODO: ... is this case really happening?
- rv = new ArrayList(rv);
- rv.addAll(r);
- }
- }
- }
- if(rv == null || rv.isEmpty()) {
- logger.log(POILogger.WARN, "text run not found for OutlineTextRefAtom.TextIndex=" + idx);
- }
- } else {
- if (sheet != null) {
- // check sheet runs first, so we get exactly the same paragraph list
- List> sheetRuns = sheet.getTextParagraphs();
- assert(sheetRuns != null);
+ /**
+ * Scans through the supplied record array, looking for
+ * a TextHeaderAtom followed by one of a TextBytesAtom or
+ * a TextCharsAtom. Builds up TextRuns from these
+ *
+ * @param wrapper an EscherTextboxWrapper
+ */
+ protected static List findTextParagraphs(EscherTextboxWrapper wrapper, HSLFSheet sheet) {
+ // propagate parents to parent-aware records
+ RecordContainer.handleParentAwareRecords(wrapper);
+ int shapeId = wrapper.getShapeId();
+ List rv = null;
- for (List paras : sheetRuns) {
+ OutlineTextRefAtom ota = (OutlineTextRefAtom)wrapper.findFirstOfType(OutlineTextRefAtom.typeID);
+ if (ota != null) {
+ // if we are based on an outline, there are no further records to be parsed from the wrapper
+ if (sheet == null) {
+ throw new RuntimeException("Outline atom reference can't be solved without a sheet record");
+ }
+
+ List> sheetRuns = sheet.getTextParagraphs();
+ assert (sheetRuns != null);
+
+ int idx = ota.getTextIndex();
+ for (List r : sheetRuns) {
+ if (r.isEmpty()) continue;
+ int ridx = r.get(0).getIndex();
+ if (ridx > idx) break;
+ if (ridx == idx) {
+ if (rv == null) {
+ rv = r;
+ } else {
+ // create a new container
+ // TODO: ... is this case really happening?
+ rv = new ArrayList(rv);
+ rv.addAll(r);
+ }
+ }
+ }
+ if (rv == null || rv.isEmpty()) {
+ logger.log(POILogger.WARN, "text run not found for OutlineTextRefAtom.TextIndex=" + idx);
+ }
+ } else {
+ if (sheet != null) {
+ // check sheet runs first, so we get exactly the same paragraph list
+ List> sheetRuns = sheet.getTextParagraphs();
+ assert (sheetRuns != null);
+
+ for (List paras : sheetRuns) {
if (!paras.isEmpty() && paras.get(0)._headerAtom.getParentRecord() == wrapper) {
- rv = paras;
- break;
- }
- }
- }
-
- if (rv == null) {
- // if we haven't found the wrapper in the sheet runs, create a new paragraph list from its record
- List> rvl = findTextParagraphs(wrapper.getChildRecords());
- switch (rvl.size()) {
- case 0: break; // nothing found
- case 1: rv = rvl.get(0); break; // normal case
- default:
- throw new RuntimeException("TextBox contains more than one list of paragraphs.");
- }
- }
- }
-
- if (rv != null) {
- StyleTextProp9Atom styleTextProp9Atom = wrapper.getStyleTextProp9Atom();
-
- for (HSLFTextParagraph htp : rv) {
- htp.setShapeId(shapeId);
- htp.setStyleTextProp9Atom(styleTextProp9Atom);
- }
- }
- return rv;
- }
+ rv = paras;
+ break;
+ }
+ }
+ }
- /**
- * Scans through the supplied record array, looking for
- * a TextHeaderAtom followed by one of a TextBytesAtom or
- * a TextCharsAtom. Builds up TextRuns from these
- *
- * @param records the records to build from
- */
+ if (rv == null) {
+ // if we haven't found the wrapper in the sheet runs, create a new paragraph list from its record
+ List> rvl = findTextParagraphs(wrapper.getChildRecords());
+ switch (rvl.size()) {
+ case 0: break; // nothing found
+ case 1: rv = rvl.get(0); break; // normal case
+ default:
+ throw new RuntimeException("TextBox contains more than one list of paragraphs.");
+ }
+ }
+ }
+
+ if (rv != null) {
+ StyleTextProp9Atom styleTextProp9Atom = wrapper.getStyleTextProp9Atom();
+
+ for (HSLFTextParagraph htp : rv) {
+ htp.setShapeId(shapeId);
+ htp.setStyleTextProp9Atom(styleTextProp9Atom);
+ }
+ }
+ return rv;
+ }
+
+ /**
+ * Scans through the supplied record array, looking for
+ * a TextHeaderAtom followed by one of a TextBytesAtom or
+ * a TextCharsAtom. Builds up TextRuns from these
+ *
+ * @param records the records to build from
+ */
protected static List> findTextParagraphs(Record[] records) {
List> paragraphCollection = new ArrayList>();
- int[] recordIdx = {0};
-
+ int[] recordIdx = { 0 };
+
for (int slwtIndex = 0; recordIdx[0] < records.length; slwtIndex++) {
- TextHeaderAtom header = null;
- TextBytesAtom tbytes = null;
- TextCharsAtom tchars = null;
- TextRulerAtom ruler = null;
+ TextHeaderAtom header = null;
+ TextBytesAtom tbytes = null;
+ TextCharsAtom tchars = null;
+ TextRulerAtom ruler = null;
MasterTextPropAtom indents = null;
for (Record r : getRecords(records, recordIdx, null)) {
long rt = r.getRecordType();
if (RecordTypes.TextHeaderAtom.typeID == rt) {
- header = (TextHeaderAtom)r;
+ header = (TextHeaderAtom) r;
} else if (RecordTypes.TextBytesAtom.typeID == rt) {
- tbytes = (TextBytesAtom)r;
+ tbytes = (TextBytesAtom) r;
} else if (RecordTypes.TextCharsAtom.typeID == rt) {
- tchars = (TextCharsAtom)r;
+ tchars = (TextCharsAtom) r;
} else if (RecordTypes.TextRulerAtom.typeID == rt) {
- ruler = (TextRulerAtom)r;
+ ruler = (TextRulerAtom) r;
} else if (RecordTypes.MasterTextPropAtom.typeID == rt) {
- indents = (MasterTextPropAtom)r;
+ indents = (MasterTextPropAtom) r;
}
// don't search for RecordTypes.StyleTextPropAtom.typeID here ... see findStyleAtomPresent below
}
if (header == null) break;
-
+
if (header.getParentRecord() instanceof SlideListWithText) {
// runs found in PPDrawing are not linked with SlideListWithTexts
header.setIndex(slwtIndex);
@@ -1093,7 +1071,7 @@ public final class HSLFTextParagraph implements TextParagraph {
List paragraphs = new ArrayList();
paragraphCollection.add(paragraphs);
-
+
// split, but keep delimiter
for (String para : rawText.split("(?<=\r)")) {
HSLFTextParagraph tpara = new HSLFTextParagraph(header, tbytes, tchars);
@@ -1116,7 +1094,7 @@ public final class HSLFTextParagraph implements TextParagraph {
if (paragraphCollection.isEmpty()) {
logger.log(POILogger.DEBUG, "No text records found.");
}
-
+
return paragraphCollection;
}
@@ -1124,25 +1102,25 @@ public final class HSLFTextParagraph implements TextParagraph {
int paraIdx = 0, runIdx = 0;
HSLFTextRun trun;
- for (int csIdx=0; csIdx runs = para.getTextRuns();
trun = runs.get(runIdx);
int len = trun.getLength();
- if (ccRun+len <= ccStyle) {
+ if (ccRun + len <= ccStyle) {
ccRun += len;
} else {
String text = trun.getRawText();
- trun.setText(text.substring(0,ccStyle-ccRun));
+ trun.setText(text.substring(0, ccStyle - ccRun));
HSLFTextRun nextRun = new HSLFTextRun(para);
- nextRun.setText(text.substring(ccStyle-ccRun));
- runs.add(runIdx+1, nextRun);
+ nextRun.setText(text.substring(ccStyle - ccRun));
+ runs.add(runIdx + 1, nextRun);
- ccRun += ccStyle-ccRun;
+ ccRun += ccStyle - ccRun;
}
TextPropCollection pCopy = new TextPropCollection(0, TextPropType.character);
@@ -1151,7 +1129,7 @@ public final class HSLFTextParagraph implements TextParagraph {
len = trun.getLength();
if (paraIdx == paragraphs.size()-1 && runIdx == runs.size()-1) {
- if (csIdx < charStyles.size()-1) {
+ if (csIdx < charStyles.size() - 1) {
// special case, empty trailing text run
HSLFTextRun nextRun = new HSLFTextRun(para);
nextRun.setText("");
@@ -1204,7 +1182,7 @@ public final class HSLFTextParagraph implements TextParagraph {
len += trun.getLength();
}
para.setIndentLevel(p.getIndentLevel());
- ccPara += len+1;
+ ccPara += len + 1;
}
}
}
@@ -1237,6 +1215,6 @@ public final class HSLFTextParagraph implements TextParagraph {
}
public EscherTextboxWrapper getTextboxWrapper() {
- return (EscherTextboxWrapper)_headerAtom.getParentRecord();
+ return (EscherTextboxWrapper) _headerAtom.getParentRecord();
}
}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java
index 1e1dca888..bb4f14041 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java
@@ -17,7 +17,8 @@
package org.apache.poi.hslf.usermodel;
-import static org.apache.poi.hslf.usermodel.HSLFTextParagraph.fetchOrAddTextProp;
+import static org.apache.poi.hslf.usermodel.HSLFTextParagraph.setPropVal;
+import static org.apache.poi.hslf.usermodel.HSLFTextParagraph.getPropVal;
import java.awt.Color;
@@ -132,39 +133,17 @@ public final class HSLFTextRun implements TextRun {
* it if required.
*/
private void setCharFlagsTextPropVal(int index, boolean value) {
- if(getFlag(index) != value) setFlag(index, value);
+ // TODO: check if paragraph/chars can be handled the same ...
+ if (getFlag(index) != value) setFlag(index, value);
}
- /**
- * Fetch the value of the given Character related TextProp.
- * Returns -1 if that TextProp isn't present.
- * If the TextProp isn't present, the value from the appropriate
- * Master Sheet will apply.
- */
- private int getCharTextPropVal(String propName) {
- TextProp prop = null;
- if (characterStyle != null){
- prop = characterStyle.findByName(propName);
- }
-
- if (prop == null){
- HSLFSheet sheet = parentParagraph.getSheet();
- int txtype = parentParagraph.getRunType();
- HSLFMasterSheet master = sheet.getMasterSheet();
- if (master != null)
- prop = master.getStyleAttribute(txtype, parentParagraph.getIndentLevel(), propName, true);
- }
- return prop == null ? -1 : prop.getValue();
- }
-
/**
* Sets the value of the given Paragraph TextProp, add if required
* @param propName The name of the Paragraph TextProp
* @param val The value to set for the TextProp
*/
public void setCharTextPropVal(String propName, int val) {
- TextProp tp = fetchOrAddTextProp(characterStyle, propName);
- tp.setValue(val);
+ setPropVal(characterStyle, propName, val);
}
@@ -260,8 +239,8 @@ public final class HSLFTextRun implements TextRun {
* @return the percentage of the font size. If the value is positive, it is superscript, otherwise it is subscript
*/
public int getSuperscript() {
- int val = getCharTextPropVal("superscript");
- return val == -1 ? 0 : val;
+ TextProp tp = getPropVal(characterStyle, "superscript", parentParagraph);
+ return tp == null ? 0 : tp.getValue();
}
/**
@@ -270,14 +249,15 @@ public final class HSLFTextRun implements TextRun {
* @param val the percentage of the font size. If the value is positive, it is superscript, otherwise it is subscript
*/
public void setSuperscript(int val) {
- setCharTextPropVal("superscript", val);
+ setPropVal(characterStyle, "superscript", val);
}
/**
* Gets the font size
*/
- public double getFontSize() {
- return getCharTextPropVal("font.size");
+ public Double getFontSize() {
+ TextProp tp = getPropVal(characterStyle, "font.size", parentParagraph);
+ return tp == null ? null : (double)tp.getValue();
}
@@ -292,7 +272,8 @@ public final class HSLFTextRun implements TextRun {
* Gets the font index
*/
public int getFontIndex() {
- return getCharTextPropVal("font.index");
+ TextProp tp = getPropVal(characterStyle, "font.index", parentParagraph);
+ return tp == null ? -1 : tp.getValue();
}
/**
@@ -329,9 +310,9 @@ public final class HSLFTextRun implements TextRun {
if (sheet == null || slideShow == null) {
return _fontFamily;
}
- int fontIdx = getCharTextPropVal("font.index");
- if(fontIdx == -1) { return null; }
- return slideShow.getFontCollection().getFontWithId(fontIdx);
+ TextProp tp = getPropVal(characterStyle, "font.index", parentParagraph);
+ if (tp == null) { return null; }
+ return slideShow.getFontCollection().getFontWithId(tp.getValue());
}
/**
@@ -339,7 +320,9 @@ public final class HSLFTextRun implements TextRun {
* @see java.awt.Color
*/
public Color getFontColor() {
- int rgb = getCharTextPropVal("font.color");
+ TextProp tp = getPropVal(characterStyle, "font.color", parentParagraph);
+ if (tp == null) return null;
+ int rgb = tp.getValue();
int cidx = rgb >> 24;
if (rgb % 0x1000000 == 0){
@@ -370,7 +353,7 @@ public final class HSLFTextRun implements TextRun {
}
protected void setFlag(int index, boolean value) {
- BitMaskTextProp prop = (BitMaskTextProp) fetchOrAddTextProp(characterStyle, CharFlagsTextProp.NAME);
+ BitMaskTextProp prop = (BitMaskTextProp)characterStyle.addWithName(CharFlagsTextProp.NAME);
prop.setSubValue(value, index);
}
diff --git a/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextParagraph.java b/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextParagraph.java
index 3db3c6f6d..d3c606a62 100644
--- a/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextParagraph.java
+++ b/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextParagraph.java
@@ -55,12 +55,21 @@ public class DrawTextParagraph implements Drawable {
double rightInset = insets.right;
double penY = y;
- double leftMargin = paragraph.getLeftMargin();
boolean firstLine = true;
- double indent = paragraph.getIndent();
+ Double leftMargin = paragraph.getLeftMargin();
+ Double indent = paragraph.getIndent();
+ if (leftMargin == null) {
+ leftMargin = (indent != null) ? -indent : 0;
+ }
+ if (indent == null) {
+ indent = (leftMargin != null) ? -leftMargin : 0;
+ }
+
//The vertical line spacing
- double spacing = paragraph.getLineSpacing();
+ Double spacing = paragraph.getLineSpacing();
+ if (spacing == null) spacing = 100d;
+
for(DrawTextFragment line : lines){
double penX = x + leftMargin;
@@ -77,7 +86,7 @@ public class DrawTextParagraph implements Drawable {
bullet.setPosition(penX + indent, penY);
} else if(indent > 0){
// a positive value means the "First Line" indentation:
- // the first line is indented and other lines start at the bullet ofset
+ // the first line is indented and other lines start at the bullet offset
bullet.setPosition(penX, penY);
penX += indent;
} else {
@@ -96,7 +105,9 @@ public class DrawTextParagraph implements Drawable {
Rectangle2D anchor = DrawShape.getAnchor(graphics, paragraph.getParentShape());
- switch (paragraph.getTextAlign()) {
+ TextAlign ta = paragraph.getTextAlign();
+ if (ta == null) ta = TextAlign.LEFT;
+ switch (ta) {
case CENTER:
penX += (anchor.getWidth() - leftMargin - line.getWidth() - leftInset - rightInset) / 2;
break;
@@ -219,9 +230,10 @@ public class DrawTextParagraph implements Drawable {
if (buColor == null) buColor = (Color)firstLineAttr.getAttribute(TextAttribute.FOREGROUND);
float fontSize = (Float)firstLineAttr.getAttribute(TextAttribute.SIZE);
- float buSz = (float)bulletStyle.getBulletFontSize();
- if(buSz > 0) fontSize *= buSz* 0.01;
- else fontSize = -buSz;
+ Double buSz = bulletStyle.getBulletFontSize();
+ if (buSz == null) buSz = 100d;
+ if (buSz > 0) fontSize *= buSz* 0.01;
+ else fontSize = (float)-buSz;
AttributedString str = new AttributedString(buCharacter);
@@ -237,10 +249,13 @@ public class DrawTextParagraph implements Drawable {
protected String getRenderableText(TextRun tr) {
StringBuilder buf = new StringBuilder();
TextCap cap = tr.getTextCap();
+ String tabs = null;
for (char c : tr.getRawText().toCharArray()) {
if(c == '\t') {
- // TODO: finish support for tabs
- buf.append(" ");
+ if (tabs == null) {
+ tabs = tab2space(tr);
+ }
+ buf.append(tabs);
continue;
}
@@ -255,6 +270,34 @@ public class DrawTextParagraph implements Drawable {
return buf.toString();
}
+
+ /**
+ * Replace a tab with the effective number of white spaces.
+ */
+ private String tab2space(TextRun tr) {
+ AttributedString string = new AttributedString(" ");
+ String typeFace = tr.getFontFamily();
+ if (typeFace == null) typeFace = "Lucida Sans";
+ string.addAttribute(TextAttribute.FAMILY, typeFace);
+
+ Double fs = tr.getFontSize();
+ if (fs == null) fs = 12d;
+ string.addAttribute(TextAttribute.SIZE, fs.floatValue());
+
+ TextLayout l = new TextLayout(string.getIterator(), new FontRenderContext(null, true, true));
+ double wspace = l.getAdvance();
+
+ Double tabSz = paragraph.getDefaultTabSize();
+ if (tabSz == null) tabSz = wspace*4;
+
+ int numSpaces = (int)Math.ceil(tabSz / wspace);
+ StringBuilder buf = new StringBuilder();
+ for(int i = 0; i < numSpaces; i++) {
+ buf.append(' ');
+ }
+ return buf.toString();
+ }
+
/**
* Returns wrapping width to break lines in this paragraph
@@ -271,8 +314,14 @@ public class DrawTextParagraph implements Drawable {
Rectangle2D anchor = DrawShape.getAnchor(graphics, paragraph.getParentShape());
- double leftMargin = paragraph.getLeftMargin();
- double indent = paragraph.getIndent();
+ Double leftMargin = paragraph.getLeftMargin();
+ Double indent = paragraph.getIndent();
+ if (leftMargin == null) {
+ leftMargin = (indent != null) ? -indent : 0;
+ }
+ if (indent == null) {
+ indent = (leftMargin != null) ? -leftMargin : 0;
+ }
double width;
TextShape extends TextParagraph> ts = paragraph.getParentShape();
@@ -323,7 +372,9 @@ public class DrawTextParagraph implements Drawable {
text.append(runText);
int endIndex = text.length();
- attList.add(new AttributedStringData(TextAttribute.FOREGROUND, run.getFontColor(), beginIndex, endIndex));
+ Color fgColor = run.getFontColor();
+ if (fgColor == null) fgColor = Color.BLACK;
+ attList.add(new AttributedStringData(TextAttribute.FOREGROUND, fgColor, beginIndex, endIndex));
// user can pass an custom object to convert fonts
String fontFamily = run.getFontFamily();
@@ -335,10 +386,14 @@ public class DrawTextParagraph implements Drawable {
if(fontHandler != null) {
fontFamily = fontHandler.getRendererableFont(fontFamily, run.getPitchAndFamily());
}
+ if (fontFamily == null) {
+ fontFamily = paragraph.getDefaultFontFamily();
+ }
attList.add(new AttributedStringData(TextAttribute.FAMILY, fontFamily, beginIndex, endIndex));
- float fontSz = (float)run.getFontSize();
- attList.add(new AttributedStringData(TextAttribute.SIZE, fontSz, beginIndex, endIndex));
+ Double fontSz = run.getFontSize();
+ if (fontSz == null) fontSz = paragraph.getDefaultFontSize();
+ attList.add(new AttributedStringData(TextAttribute.SIZE, fontSz.floatValue(), beginIndex, endIndex));
if(run.isBold()) {
attList.add(new AttributedStringData(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD, beginIndex, endIndex));
@@ -364,9 +419,9 @@ public class DrawTextParagraph implements Drawable {
// ensure that the paragraph contains at least one character
// We need this trick to correctly measure text
if (text.length() == 0) {
- float fontSz = (float)paragraph.getDefaultFontSize();
+ Double fontSz = paragraph.getDefaultFontSize();
text.append(" ");
- attList.add(new AttributedStringData(TextAttribute.SIZE, fontSz, 0, 1));
+ attList.add(new AttributedStringData(TextAttribute.SIZE, fontSz.floatValue(), 0, 1));
}
AttributedString string = new AttributedString(text.toString());
diff --git a/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextShape.java b/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextShape.java
index 247c7c184..bb6c6dd29 100644
--- a/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextShape.java
+++ b/src/scratchpad/src/org/apache/poi/sl/draw/DrawTextShape.java
@@ -95,7 +95,8 @@ public class DrawTextShape 0) {
// positive value means percentage spacing of the height of the first line, e.g.
// the higher the first line, the bigger the space before the paragraph
@@ -112,7 +113,8 @@ public class DrawTextShape 0) {
// positive value means percentage spacing of the height of the last line, e.g.
// the higher the last line, the bigger the space after the paragraph
diff --git a/src/scratchpad/src/org/apache/poi/sl/usermodel/TextParagraph.java b/src/scratchpad/src/org/apache/poi/sl/usermodel/TextParagraph.java
index 14eb03c84..e85eee140 100644
--- a/src/scratchpad/src/org/apache/poi/sl/usermodel/TextParagraph.java
+++ b/src/scratchpad/src/org/apache/poi/sl/usermodel/TextParagraph.java
@@ -104,10 +104,18 @@ public interface TextParagraph extends Iterable {
public interface BulletStyle {
String getBulletCharacter();
String getBulletFont();
- double getBulletFontSize();
+
+ /**
+ * The bullet point font size
+ * If bulletFontSize >= 0, then space is a percentage of normal line height.
+ * If bulletFontSize < 0, the absolute value in points
+ *
+ * @return the bullet point font size
+ */
+ Double getBulletFontSize();
Color getBulletFontColor();
}
-
+
/**
* The amount of vertical white space before the paragraph
* This may be specified in two different ways, percentage spacing and font point spacing:
@@ -116,9 +124,30 @@ public interface TextParagraph extends Iterable {
* If spaceBefore < 0, the absolute value in points
*
*
- * @return the vertical white space before the paragraph
+ * @return the vertical white space before the paragraph, or null if unset
*/
- double getSpaceBefore();
+ Double getSpaceBefore();
+
+ /**
+ * Set the amount of vertical white space that will be present before the paragraph.
+ * This space is specified in either percentage or points:
+ *
+ * If spaceBefore >= 0, then space is a percentage of normal line height.
+ * If spaceBefore < 0, the absolute value of linespacing is the spacing in points
+ *
+ * Examples:
+ *
+ * // The paragraph will be formatted to have a spacing before the paragraph text.
+ * // The spacing will be 200% of the size of the largest text on each line
+ * paragraph.setSpaceBefore(200);
+ *
+ * // The spacing will be a size of 48 points
+ * paragraph.setSpaceBefore(-48.0);
+ *
+ *
+ * @param spaceBefore the vertical white space before the paragraph, null to unset
+ */
+ void setSpaceBefore(Double spaceBefore);
/**
* The amount of vertical white space after the paragraph
@@ -128,40 +157,74 @@ public interface TextParagraph extends Iterable {
* If spaceBefore < 0, the absolute value of linespacing is the spacing in points
*
*
- * @return the vertical white space after the paragraph
+ * @return the vertical white space after the paragraph or null, if unset
*/
- double getSpaceAfter();
+ Double getSpaceAfter();
/**
- * @return the left margin (in points) of the paragraph
+ * Set the amount of vertical white space that will be present after the paragraph.
+ * This space is specified in either percentage or points:
+ *
+ * If spaceAfter >= 0, then space is a percentage of normal line height.
+ * If spaceAfter < 0, the absolute value of linespacing is the spacing in points
+ *
+ * Examples:
+ *
+ * // The paragraph will be formatted to have a spacing after the paragraph text.
+ * // The spacing will be 200% of the size of the largest text on each line
+ * paragraph.setSpaceAfter(200);
+ *
+ * // The spacing will be a size of 48 points
+ * paragraph.setSpaceAfter(-48.0);
+ *
+ *
+ * @param spaceAfter the vertical white space after the paragraph, null to unset
*/
- double getLeftMargin();
+ public void setSpaceAfter(Double spaceAfter);
+
+ /**
+ * @return the left margin (in points) of the paragraph or null, if unset
+ */
+ Double getLeftMargin();
/**
- * @param leftMargin the left margin (in points)
+ * Specifies the left margin of the paragraph. This is specified in addition to the text body
+ * inset and applies only to this text paragraph. That is the text body Inset and the LeftMargin
+ * attributes are additive with respect to the text position.
+ *
+ * @param leftMargin the left margin (in points) or null to unset
*/
- void setLeftMargin(double leftMargin);
+ void setLeftMargin(Double leftMargin);
/**
- * @return the right margin (in points) of the paragraph
+ * Specifies the right margin of the paragraph. This is specified in addition to the text body
+ * inset and applies only to this text paragraph. That is the text body Inset and the RightMargin
+ * attributes are additive with respect to the text position.
+ *
+ * The right margin is not support and therefore ignored by the HSLF implementation.
+ *
+ * @return the right margin (in points) of the paragraph or null, if unset
*/
- double getRightMargin();
+ Double getRightMargin();
/**
* @param rightMargin the right margin (in points) of the paragraph
*/
- void setRightMargin(double rightMargin);
+ void setRightMargin(Double rightMargin);
/**
* @return the indent (in points) applied to the first line of text in the paragraph.
+ * or null, if unset
*/
- double getIndent();
+ Double getIndent();
/**
+ * Specifies the indent size that will be applied to the first line of text in the paragraph.
+ *
* @param indent the indent (in points) applied to the first line of text in the paragraph
*/
- void setIndent(double indent);
+ void setIndent(Double indent);
/**
* Returns the vertical line spacing that is to be used within a paragraph.
@@ -171,9 +234,9 @@ public interface TextParagraph extends Iterable {
* If linespacing < 0, the absolute value of linespacing is the spacing in points
*
*
- * @return the vertical line spacing.
+ * @return the vertical line spacing or null, if unset
*/
- double getLineSpacing();
+ Double getLineSpacing();
/**
* This element specifies the vertical line spacing that is to be used within a paragraph.
@@ -196,14 +259,14 @@ public interface TextParagraph extends Iterable {
*
* @param linespacing the vertical line spacing
*/
- void setLineSpacing(double lineSpacing);
+ void setLineSpacing(Double lineSpacing);
String getDefaultFontFamily();
/**
- * @return the default font size, in case its not set in the textrun
+ * @return the default font size, in case its not set in the textrun or null, if unset
*/
- double getDefaultFontSize();
+ Double getDefaultFontSize();
/**
* Returns the alignment that is applied to the paragraph.
@@ -217,8 +280,10 @@ public interface TextParagraph extends Iterable {
/**
* Returns the font alignment that is applied to the paragraph.
*
- * If this attribute is omitted, then a value of auto (~ left) is implied.
- * @return ??? alignment that is applied to the paragraph
+ * If this attribute is omitted, then null is return,
+ * user code can imply the a value of {@link FontAlign#AUTO}
+ *
+ * @return alignment that is applied to the paragraph
*/
FontAlign getFontAlign();
@@ -227,5 +292,11 @@ public interface TextParagraph extends Iterable {
*/
BulletStyle getBulletStyle();
+ /**
+ * @return the default size for a tab character within this paragraph in points, null if unset
+ */
+ Double getDefaultTabSize();
+
+
TextShape extends TextParagraph> getParentShape();
}
diff --git a/src/scratchpad/src/org/apache/poi/sl/usermodel/TextRun.java b/src/scratchpad/src/org/apache/poi/sl/usermodel/TextRun.java
index 8ac40c189..bc1652afe 100644
--- a/src/scratchpad/src/org/apache/poi/sl/usermodel/TextRun.java
+++ b/src/scratchpad/src/org/apache/poi/sl/usermodel/TextRun.java
@@ -21,8 +21,6 @@ import java.awt.Color;
/**
* Some text.
- *
- * TODO - decide on how we do rich text stuff
*/
public interface TextRun {
enum TextCap {
@@ -31,13 +29,13 @@ public interface TextRun {
ALL
}
- public String getRawText();
- public void setText(String text);
+ String getRawText();
+ void setText(String text);
TextCap getTextCap();
Color getFontColor();
- double getFontSize();
+ Double getFontSize();
String getFontFamily();
boolean isBold();
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestPicture.java b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestPicture.java
index 8e5652fb8..4733999d4 100644
--- a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestPicture.java
+++ b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestPicture.java
@@ -22,8 +22,7 @@ import static org.junit.Assert.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.*;
import javax.imageio.ImageIO;
@@ -35,7 +34,6 @@ import org.apache.poi.sl.usermodel.Slide;
import org.apache.poi.sl.usermodel.SlideShow;
import org.apache.poi.util.JvmBugs;
import org.apache.poi.xslf.usermodel.XMLSlideShow;
-import org.junit.Ignore;
import org.junit.Test;
/**
@@ -142,29 +140,43 @@ public final class TestPicture {
@Test
// @Ignore("Just for visual validation - antialiasing is different on various systems")
public void bug54541() throws Exception {
- String file = new String[]{
- "54542_cropped_bitmap.pptx",
- "54541_cropped_bitmap.ppt",
- "54541_cropped_bitmap2.ppt",
- "sample_pptx_grouping_issues.pptx"
- }[3];
- InputStream is = _slTests.openResourceAsStream(file);
- SlideShow ss = file.endsWith("pptx") ? new XMLSlideShow(is) : new HSLFSlideShow(is);
- is.close();
+ String files[] = {
+// "sample_pptx_grouping_issues.pptx",
+// "54542_cropped_bitmap.pptx",
+// "54541_cropped_bitmap.ppt",
+// "54541_cropped_bitmap2.ppt",
+// "alterman_security.ppt",
+ "alterman_security2.pptx",
+ };
- boolean debugOut = false;
- Dimension pg = ss.getPageSize();
- int i=1;
- for(Slide,?,?> slide : ss.getSlides()) {
- if (debugOut) {
- DummyGraphics2d graphics = new DummyGraphics2d();
- slide.draw(graphics);
- } else {
- BufferedImage img = new BufferedImage(pg.width, pg.height, BufferedImage.TYPE_INT_RGB);
- Graphics2D graphics = img.createGraphics();
- fixFonts(graphics);
- slide.draw(graphics);
- ImageIO.write(img, "PNG", new File("test"+(i++)+"hslf.png"));
+ BitSet pages = new BitSet();
+ pages.set(2);
+
+ for (String file : files) {
+ InputStream is = _slTests.openResourceAsStream(file);
+ SlideShow ss = file.endsWith("pptx") ? new XMLSlideShow(is) : new HSLFSlideShow(is);
+ is.close();
+
+ boolean debugOut = false;
+ Dimension pg = ss.getPageSize();
+ for (Slide,?,?> slide : ss.getSlides()) {
+ int slideNo = slide.getSlideNumber();
+ if (!pages.get(slideNo-1)) {
+ if (pages.nextSetBit(slideNo-1) == -1) break; else continue;
+ }
+ if (debugOut) {
+ DummyGraphics2d graphics = new DummyGraphics2d();
+ slide.draw(graphics);
+ } else {
+ BufferedImage img = new BufferedImage(pg.width, pg.height, BufferedImage.TYPE_INT_ARGB);
+ Graphics2D graphics = img.createGraphics();
+ fixFonts(graphics);
+ slide.draw(graphics);
+ graphics.setColor(Color.BLACK);
+ graphics.setStroke(new BasicStroke(1));
+ graphics.drawRect(0, 0, (int)pg.getWidth()-1, (int)pg.getHeight()-1);
+ ImageIO.write(img, "PNG", new File(file.replaceFirst(".pptx?", "-")+slideNo+".png"));
+ }
}
}
}
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestRichTextRun.java b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestRichTextRun.java
index 0468955ee..b57e9f503 100644
--- a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestRichTextRun.java
+++ b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestRichTextRun.java
@@ -470,7 +470,7 @@ public final class TestRichTextRun {
assertEquals(expected, HSLFTextParagraph.getRawText(txt.get(1)));
assertEquals(4, txt.get(1).size());
rt = txt.get(1).get(0);
- assertEquals('\u2022', rt.getBulletChar());
+ assertEquals('\u2022', (char)rt.getBulletChar());
assertTrue(rt.isBullet());
@@ -486,7 +486,7 @@ public final class TestRichTextRun {
assertEquals(4, txt.get(0).size());
rt = txt.get(0).get(0);
assertTrue(rt.isBullet());
- assertEquals('\u2022', rt.getBulletChar());
+ assertEquals('\u2022', (char)rt.getBulletChar());
expected =
"I\u2019m a text box with user-defined\r" +
@@ -495,7 +495,7 @@ public final class TestRichTextRun {
assertEquals(2, txt.get(1).size());
rt = txt.get(1).get(0);
assertTrue(rt.isBullet());
- assertEquals('\u263A', rt.getBulletChar());
+ assertEquals('\u263A', (char)rt.getBulletChar());
}
@Test
@@ -513,8 +513,8 @@ public final class TestRichTextRun {
HSLFTextRun tr = rt.getTextRuns().get(0);
tr.setFontSize(42);
rt.setBullet(true);
- rt.setLeftMargin(50);
- rt.setIndent(0);
+ rt.setLeftMargin(50d);
+ rt.setIndent(0d);
rt.setBulletChar('\u263A');
slide.addShape(shape);
@@ -522,7 +522,7 @@ public final class TestRichTextRun {
assertEquals(true, rt.isBullet());
assertEquals(50.0, rt.getLeftMargin(), 0);
assertEquals(0, rt.getIndent(), 0);
- assertEquals('\u263A', rt.getBulletChar());
+ assertEquals('\u263A', (char)rt.getBulletChar());
shape.setAnchor(new java.awt.Rectangle(50, 50, 500, 300));
slide.addShape(shape);
@@ -541,7 +541,7 @@ public final class TestRichTextRun {
assertEquals(true, rt.isBullet());
assertEquals(50.0, rt.getLeftMargin(), 0);
assertEquals(0, rt.getIndent(), 0);
- assertEquals('\u263A', rt.getBulletChar());
+ assertEquals('\u263A', (char)rt.getBulletChar());
}
@Test
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestTextRun.java b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestTextRun.java
index 9dda9af24..c2f7ac9d0 100644
--- a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestTextRun.java
+++ b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestTextRun.java
@@ -556,7 +556,7 @@ public final class TestTextRun {
int i=0;
for (List textParas : slide.getTextParagraphs()) {
assertEquals("Arial", textParas.get(0).getTextRuns().get(0).getFontFamily());
- assertEquals(sizes[i++], (int)textParas.get(0).getTextRuns().get(0).getFontSize());
+ assertEquals(sizes[i++], textParas.get(0).getTextRuns().get(0).getFontSize().intValue());
}
}
diff --git a/test-data/slideshow/alterman_security2.pptx b/test-data/slideshow/alterman_security2.pptx
new file mode 100644
index 0000000000000000000000000000000000000000..13292286460cab6462696da2849e58edd6997f67
GIT binary patch
literal 425540
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 BkW;cJK^PSTR3?
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