Rendering fixes

git-svn-id: https://svn.apache.org/repos/asf/poi/branches/common_sl@1686117 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Andreas Beeker 2015-06-17 22:21:13 +00:00
parent 2170a52383
commit f97d6f6c34
22 changed files with 1192 additions and 1197 deletions

View File

@ -39,8 +39,8 @@ public final class BulletsDemo {
HSLFTextParagraph rt = shape.getTextParagraphs().get(0); HSLFTextParagraph rt = shape.getTextParagraphs().get(0);
rt.getTextRuns().get(0).setFontSize(42); rt.getTextRuns().get(0).setFontSize(42);
rt.setBullet(true); rt.setBullet(true);
rt.setIndent(0); //bullet offset rt.setIndent(0d); //bullet offset
rt.setLeftMargin(50); //text offset (should be greater than bullet offset) rt.setLeftMargin(50d); //text offset (should be greater than bullet offset)
rt.setBulletChar('\u263A'); //bullet character rt.setBulletChar('\u263A'); //bullet character
shape.setText( shape.setText(
"January\r" + "January\r" +

View File

@ -48,8 +48,8 @@ public class Tutorial2 {
XSLFTextParagraph p2 = shape1.addNewTextParagraph(); XSLFTextParagraph p2 = shape1.addNewTextParagraph();
// If spaceBefore >= 0, then space is a percentage of normal line height. // 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 // If spaceBefore < 0, the absolute value of linespacing is the spacing in points
p2.setSpaceBefore(-20); // 20 pt from the previous paragraph p2.setSpaceBefore(-20d); // 20 pt from the previous paragraph
p2.setSpaceAfter(300); // 3 lines after the paragraph p2.setSpaceAfter(300d); // 3 lines after the paragraph
XSLFTextRun r2 = p2.addNewTextRun(); XSLFTextRun r2 = p2.addNewTextRun();
r2.setText("Paragraph properties apply to all text residing within the corresponding paragraph."); r2.setText("Paragraph properties apply to all text residing within the corresponding paragraph.");
r2.setFontSize(16); r2.setFontSize(16);
@ -62,8 +62,8 @@ public class Tutorial2 {
r3.setFontColor(new Color(85, 142, 213)); r3.setFontColor(new Color(85, 142, 213));
XSLFTextParagraph p4 = shape1.addNewTextParagraph(); XSLFTextParagraph p4 = shape1.addNewTextParagraph();
p4.setSpaceBefore(-20); // 20 pt from the previous paragraph p4.setSpaceBefore(-20d); // 20 pt from the previous paragraph
p4.setSpaceAfter(300); // 3 lines after the paragraph p4.setSpaceAfter(300d); // 3 lines after the paragraph
XSLFTextRun r4 = p4.addNewTextRun(); XSLFTextRun r4 = p4.addNewTextRun();
r4.setFontSize(16); r4.setFontSize(16);
r4.setText( r4.setText(

View File

@ -45,9 +45,9 @@ public class Tutorial7 {
XSLFTextParagraph p2 = shape.addNewTextParagraph(); XSLFTextParagraph p2 = shape.addNewTextParagraph();
// indentation before text // indentation before text
p2.setLeftMargin(60); p2.setLeftMargin(60d);
// the bullet is set 40 pt before the text // the bullet is set 40 pt before the text
p2.setIndent(-40); p2.setIndent(-40d);
p2.setBullet(true); p2.setBullet(true);
// customize bullets // customize bullets
p2.setBulletFontColor(Color.red); p2.setBulletFontColor(Color.red);

View File

@ -16,6 +16,8 @@
==================================================================== */ ==================================================================== */
package org.apache.poi.util; package org.apache.poi.util;
import org.apache.poi.hslf.usermodel.HSLFShape;
/** /**
* @author Yegor Kozlov * @author Yegor Kozlov
*/ */
@ -23,6 +25,22 @@ public class Units {
public static final int EMU_PER_PIXEL = 9525; public static final int EMU_PER_PIXEL = 9525;
public static final int EMU_PER_POINT = 12700; 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 * Converts points to EMUs
* @param points points * @param points points
@ -70,4 +88,17 @@ public class Units {
int fixedPoint = (i << 16) | (f & 0xFFFF); int fixedPoint = (i << 16) | (f & 0xFFFF);
return fixedPoint; 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;
}
} }

View File

@ -137,8 +137,10 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
/** /**
* Returns the alignment that is applied to the paragraph. * Returns the alignment that is applied to the paragraph.
* *
* If this attribute is omitted, then a value of left is implied. * If this attribute is omitted, then null is returned.
* @return ??? alignment that is applied to the paragraph * User code can imply the value {@link TextAlign#LEFT} then.
*
* @return alignment that is applied to the paragraph
*/ */
public TextAlign getTextAlign(){ public TextAlign getTextAlign(){
ParagraphPropertyFetcher<TextAlign> fetcher = new ParagraphPropertyFetcher<TextAlign>(getLevel()){ ParagraphPropertyFetcher<TextAlign> fetcher = new ParagraphPropertyFetcher<TextAlign>(getLevel()){
@ -152,7 +154,7 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
} }
}; };
fetchParagraphProperty(fetcher); fetchParagraphProperty(fetcher);
return fetcher.getValue() == null ? TextAlign.LEFT : fetcher.getValue(); return fetcher.getValue();
} }
/** /**
@ -184,7 +186,7 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
} }
}; };
fetchParagraphProperty(fetcher); fetchParagraphProperty(fetcher);
return fetcher.getValue() == null ? FontAlign.AUTO : fetcher.getValue(); return fetcher.getValue();
} }
/** /**
@ -294,7 +296,7 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
* *
* @return the bullet size * @return the bullet size
*/ */
public double getBulletFontSize(){ public Double getBulletFontSize(){
ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getLevel()){ ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getLevel()){
public boolean fetch(CTTextParagraphProperties props){ public boolean fetch(CTTextParagraphProperties props){
if(props.isSetBuSzPct()){ if(props.isSetBuSzPct()){
@ -309,7 +311,7 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
} }
}; };
fetchParagraphProperty(fetcher); fetchParagraphProperty(fetcher);
return fetcher.getValue() == null ? 100 : fetcher.getValue(); return fetcher.getValue();
} }
/** /**
@ -334,27 +336,19 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
} }
} }
/**
* Specifies the indent size that will be applied to the first line of text in the paragraph.
*
* @param value the indent in points.
*/
@Override @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(); CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
if(value == -1) { if(indent == -1) {
if(pr.isSetIndent()) pr.unsetIndent(); if(pr.isSetIndent()) pr.unsetIndent();
} else { } 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 @Override
public double getIndent(){ public Double getIndent() {
ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getLevel()){ ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getLevel()){
public boolean fetch(CTTextParagraphProperties props){ public boolean fetch(CTTextParagraphProperties props){
@ -367,32 +361,26 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
}; };
fetchParagraphProperty(fetcher); 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 @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(); CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
if(value == -1) { if (leftMargin == null) {
if(pr.isSetMarL()) pr.unsetMarL(); if(pr.isSetMarL()) pr.unsetMarL();
} else { } 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 @Override
public double getLeftMargin(){ public Double getLeftMargin(){
ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getLevel()){ ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getLevel()){
public boolean fetch(CTTextParagraphProperties props){ public boolean fetch(CTTextParagraphProperties props){
if(props.isSetMarL()){ if(props.isSetMarL()){
@ -405,32 +393,26 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
}; };
fetchParagraphProperty(fetcher); fetchParagraphProperty(fetcher);
// if the marL attribute is omitted, then a value of 347663 is implied // 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 @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(); CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
if(value == -1) { if(rightMargin == -1) {
if(pr.isSetMarR()) pr.unsetMarR(); if(pr.isSetMarR()) pr.unsetMarR();
} else { } 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 @Override
public double getRightMargin(){ public Double getRightMargin(){
ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getLevel()){ ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getLevel()){
public boolean fetch(CTTextParagraphProperties props){ public boolean fetch(CTTextParagraphProperties props){
if(props.isSetMarR()){ if(props.isSetMarR()){
@ -443,14 +425,11 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
}; };
fetchParagraphProperty(fetcher); fetchParagraphProperty(fetcher);
// if the marL attribute is omitted, then a value of 347663 is implied // if the marL attribute is omitted, then a value of 347663 is implied
return fetcher.getValue() == null ? 0 : fetcher.getValue(); return fetcher.getValue();
} }
/** @Override
* public Double getDefaultTabSize(){
* @return the default size for a tab character within this paragraph in points
*/
public double getDefaultTabSize(){
ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getLevel()){ ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getLevel()){
public boolean fetch(CTTextParagraphProperties props){ public boolean fetch(CTTextParagraphProperties props){
if(props.isSetDefTabSz()){ if(props.isSetDefTabSz()){
@ -462,7 +441,7 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
} }
}; };
fetchParagraphProperty(fetcher); fetchParagraphProperty(fetcher);
return fetcher.getValue() == null ? 0 : fetcher.getValue(); return fetcher.getValue();
} }
public double getTabStop(final int idx){ public double getTabStop(final int idx){
@ -491,16 +470,25 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
} }
@Override @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(); CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
CTTextSpacing spc = CTTextSpacing.Factory.newInstance(); if(lineSpacing == null) {
if(linespacing >= 0) spc.addNewSpcPct().setVal((int)(linespacing*1000)); if (pr.isSetLnSpc()) pr.unsetLnSpc();
else spc.addNewSpcPts().setVal((int)(-linespacing*100)); } else {
pr.setLnSpc(spc); 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 @Override
public double getLineSpacing(){ public Double getLineSpacing(){
ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getLevel()){ ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getLevel()){
public boolean fetch(CTTextParagraphProperties props){ public boolean fetch(CTTextParagraphProperties props){
if(props.isSetLnSpc()){ if(props.isSetLnSpc()){
@ -515,8 +503,8 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
}; };
fetchParagraphProperty(fetcher); fetchParagraphProperty(fetcher);
double lnSpc = fetcher.getValue() == null ? 100 : fetcher.getValue(); Double lnSpc = fetcher.getValue();
if(lnSpc > 0) { if (lnSpc != null && lnSpc > 0) {
// check if the percentage value is scaled // check if the percentage value is scaled
CTTextNormalAutofit normAutofit = getParentShape().getTextBodyPr().getNormAutofit(); CTTextNormalAutofit normAutofit = getParentShape().getTextBodyPr().getNormAutofit();
if(normAutofit != null) { if(normAutofit != null) {
@ -528,26 +516,9 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
return lnSpc; return lnSpc;
} }
/** @Override
* Set the amount of vertical white space that will be present before the paragraph. public void setSpaceBefore(Double spaceBefore){
* This space is specified in either percentage or points: if (spaceBefore == null && !_p.isSetPPr()) return;
* <p>
* 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
* </p>
* Examples:
* <pre><code>
* // 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);
* </code></pre>
*
* @param spaceBefore the vertical white space before the paragraph.
*/
public void setSpaceBefore(double spaceBefore){
CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
CTTextSpacing spc = CTTextSpacing.Factory.newInstance(); CTTextSpacing spc = CTTextSpacing.Factory.newInstance();
if(spaceBefore >= 0) spc.addNewSpcPct().setVal((int)(spaceBefore*1000)); if(spaceBefore >= 0) spc.addNewSpcPct().setVal((int)(spaceBefore*1000));
@ -555,17 +526,8 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
pr.setSpcBef(spc); pr.setSpcBef(spc);
} }
/** @Override
* The amount of vertical white space before the paragraph public Double getSpaceBefore(){
* This may be specified in two different ways, percentage spacing and font point spacing:
* <p>
* 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
* </p>
*
* @return the vertical white space before the paragraph
*/
public double getSpaceBefore(){
ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getLevel()){ ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getLevel()){
public boolean fetch(CTTextParagraphProperties props){ public boolean fetch(CTTextParagraphProperties props){
if(props.isSetSpcBef()){ if(props.isSetSpcBef()){
@ -580,30 +542,11 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
}; };
fetchParagraphProperty(fetcher); fetchParagraphProperty(fetcher);
double spcBef = fetcher.getValue() == null ? 0 : fetcher.getValue(); return fetcher.getValue();
return spcBef;
} }
/** @Override
* Set the amount of vertical white space that will be present after the paragraph. public void setSpaceAfter(Double spaceAfter){
* This space is specified in either percentage or points:
* <p>
* 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
* </p>
* Examples:
* <pre><code>
* // 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);
* </code></pre>
*
* @param spaceAfter the vertical white space after the paragraph.
*/
public void setSpaceAfter(double spaceAfter){
CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
CTTextSpacing spc = CTTextSpacing.Factory.newInstance(); CTTextSpacing spc = CTTextSpacing.Factory.newInstance();
if(spaceAfter >= 0) spc.addNewSpcPct().setVal((int)(spaceAfter*1000)); if(spaceAfter >= 0) spc.addNewSpcPct().setVal((int)(spaceAfter*1000));
@ -611,17 +554,8 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
pr.setSpcAft(spc); pr.setSpcAft(spc);
} }
/** @Override
* The amount of vertical white space after the paragraph public Double getSpaceAfter(){
* This may be specified in two different ways, percentage spacing and font point spacing:
* <p>
* 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
* </p>
*
* @return the vertical white space after the paragraph
*/
public double getSpaceAfter(){
ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getLevel()){ ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getLevel()){
public boolean fetch(CTTextParagraphProperties props){ public boolean fetch(CTTextParagraphProperties props){
if(props.isSetSpcAft()){ if(props.isSetSpcAft()){
@ -635,7 +569,7 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
} }
}; };
fetchParagraphProperty(fetcher); fetchParagraphProperty(fetcher);
return fetcher.getValue() == null ? 0 : fetcher.getValue(); return fetcher.getValue();
} }
/** /**
@ -647,7 +581,6 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
*/ */
public void setLevel(int level){ public void setLevel(int level){
CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr(); CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
pr.setLvl(level); pr.setLvl(level);
} }
@ -657,10 +590,7 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
*/ */
public int getLevel(){ public int getLevel(){
CTTextParagraphProperties pr = _p.getPPr(); CTTextParagraphProperties pr = _p.getPPr();
if(pr == null) return 0; return (pr == null || !pr.isSetLvl()) ? 0 : pr.getLvl();
return pr.getLvl();
} }
/** /**
@ -721,16 +651,15 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
} }
CTTextParagraphProperties getDefaultMasterStyle(){ /* package */ CTTextParagraphProperties getDefaultMasterStyle(){
CTPlaceholder ph = _shape.getCTPlaceholder(); CTPlaceholder ph = _shape.getCTPlaceholder();
String defaultStyleSelector; String defaultStyleSelector;
if(ph == null) defaultStyleSelector = "otherStyle"; // no placeholder means plain text box switch(ph == null ? -1 : ph.getType().intValue()) {
else {
switch(ph.getType().intValue()){
case STPlaceholderType.INT_TITLE: case STPlaceholderType.INT_TITLE:
case STPlaceholderType.INT_CTR_TITLE: case STPlaceholderType.INT_CTR_TITLE:
defaultStyleSelector = "titleStyle"; defaultStyleSelector = "titleStyle";
break; break;
case -1: // no placeholder means plain text box
case STPlaceholderType.INT_FTR: case STPlaceholderType.INT_FTR:
case STPlaceholderType.INT_SLD_NUM: case STPlaceholderType.INT_SLD_NUM:
case STPlaceholderType.INT_DT: case STPlaceholderType.INT_DT:
@ -740,34 +669,52 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
defaultStyleSelector = "bodyStyle"; defaultStyleSelector = "bodyStyle";
break; break;
} }
}
int level = getLevel(); int level = getLevel();
// wind up and find the root master sheet which must be slide master // wind up and find the root master sheet which must be slide master
XSLFSheet masterSheet = _shape.getSheet(); XSLFSheet masterSheet = _shape.getSheet();
while (masterSheet.getMasterSheet() != null){ for (XSLFSheet m = masterSheet; m != null; m = (XSLFSheet)m.getMasterSheet()) {
masterSheet = (XSLFSheet)masterSheet.getMasterSheet(); masterSheet = m;
} }
XmlObject[] o = masterSheet.getXmlObject().selectPath( String nsDecl =
"declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main' " + "declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main' " +
"declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' " + "declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' ";
".//p:txStyles/p:" + defaultStyleSelector +"/a:lvl" +(level+1)+ "pPr"); String xpaths[] = {
if (o.length == 1){ nsDecl+".//p:txStyles/p:" + defaultStyleSelector +"/a:lvl" +(level+1)+ "pPr",
return (CTTextParagraphProperties)o[0]; nsDecl+".//p:notesStyle/a:lvl" +(level+1)+ "pPr"
} else { };
o = masterSheet.getXmlObject().selectPath( XmlObject xo = masterSheet.getXmlObject();
"declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main' " + for (String xpath : xpaths) {
"declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' " + XmlObject[] o = xo.selectPath(xpath);
".//p:notesStyle/a:lvl" +(level+1)+ "pPr"); if (o.length == 1) {
if (o.length == 1){
return (CTTextParagraphProperties)o[0]; 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 <T> boolean fetchParagraphProperty(ParagraphPropertyFetcher<T> visitor){ private <T> boolean fetchParagraphProperty(ParagraphPropertyFetcher<T> visitor){
@ -860,9 +807,9 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
} }
@Override @Override
public double getDefaultFontSize() { public Double getDefaultFontSize() {
CTTextCharacterProperties endPr = _p.getEndParaRPr(); CTTextCharacterProperties endPr = _p.getEndParaRPr();
return (endPr == null || !endPr.isSetSz()) ? 12 : (endPr.getSz() / 100); return (endPr == null || !endPr.isSetSz()) ? 12 : (endPr.getSz() / 100.);
} }
@Override @Override
@ -871,6 +818,7 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
} }
public BulletStyle getBulletStyle() { public BulletStyle getBulletStyle() {
if (!isBullet()) return null;
return new BulletStyle(){ return new BulletStyle(){
public String getBulletCharacter() { public String getBulletCharacter() {
return XSLFTextParagraph.this.getBulletCharacter(); return XSLFTextParagraph.this.getBulletCharacter();
@ -880,7 +828,7 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
return XSLFTextParagraph.this.getBulletFont(); return XSLFTextParagraph.this.getBulletFont();
} }
public double getBulletFontSize() { public Double getBulletFontSize() {
return XSLFTextParagraph.this.getBulletFontSize(); return XSLFTextParagraph.this.getBulletFontSize();
} }

View File

@ -17,26 +17,11 @@
package org.apache.poi.xslf.usermodel; package org.apache.poi.xslf.usermodel;
import java.awt.Color; 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.sl.usermodel.TextRun;
import org.apache.poi.util.Beta; import org.apache.poi.util.Beta;
import org.apache.poi.xslf.model.CharacterPropertyFetcher; import org.apache.poi.xslf.model.CharacterPropertyFetcher;
import org.openxmlformats.schemas.drawingml.x2006.main.CTRegularTextRun; import org.openxmlformats.schemas.drawingml.x2006.main.*;
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.presentationml.x2006.main.CTPlaceholder; import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder;
/** /**
@ -89,28 +74,6 @@ public class XSLFTextRun implements TextRun {
return buf.toString(); 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){ public void setText(String text){
_r.setT(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; double scale = 1;
CTTextNormalAutofit afit = getParentParagraph().getParentShape().getTextBodyPr().getNormAutofit(); CTTextNormalAutofit afit = getParentParagraph().getParentShape().getTextBodyPr().getNormAutofit();
if(afit != null) scale = (double)afit.getFontScale() / 100000; if(afit != null) scale = (double)afit.getFontScale() / 100000;
@ -192,7 +156,7 @@ public class XSLFTextRun implements TextRun {
} }
}; };
fetchCharacterProperty(fetcher); 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); return new XSLFHyperlink(_r.getRPr().getHlinkClick(), this);
} }
private boolean fetchCharacterProperty(CharacterPropertyFetcher fetcher){ private boolean fetchCharacterProperty(CharacterPropertyFetcher<?> fetcher){
boolean ok = false; boolean ok = false;
if(_r.isSetRPr()) ok = fetcher.fetch(getRPr()); if(_r.isSetRPr()) ok = fetcher.fetch(getRPr());

View File

@ -125,7 +125,7 @@ public class TestXSLFAutoShape {
p.setIndent(2.0); p.setIndent(2.0);
assertEquals(2.0, p.getIndent(), 0); assertEquals(2.0, p.getIndent(), 0);
assertTrue(p.getXmlObject().getPPr().isSetIndent()); assertTrue(p.getXmlObject().getPPr().isSetIndent());
p.setIndent(-1); p.setIndent(-1d);
assertEquals(0.0, p.getIndent(), 0); assertEquals(0.0, p.getIndent(), 0);
assertFalse(p.getXmlObject().getPPr().isSetIndent()); assertFalse(p.getXmlObject().getPPr().isSetIndent());
p.setIndent(10.0); p.setIndent(10.0);
@ -149,44 +149,44 @@ public class TestXSLFAutoShape {
assertFalse(p.getXmlObject().getPPr().isSetSpcAft()); assertFalse(p.getXmlObject().getPPr().isSetSpcAft());
p.setSpaceAfter(200); p.setSpaceAfter(200d);
assertEquals(200000, p.getXmlObject().getPPr().getSpcAft().getSpcPct().getVal()); assertEquals(200000, p.getXmlObject().getPPr().getSpcAft().getSpcPct().getVal());
assertFalse(p.getXmlObject().getPPr().getSpcAft().isSetSpcPts()); assertFalse(p.getXmlObject().getPPr().getSpcAft().isSetSpcPts());
p.setSpaceAfter(100); p.setSpaceAfter(100d);
assertEquals(100000, p.getXmlObject().getPPr().getSpcAft().getSpcPct().getVal()); assertEquals(100000, p.getXmlObject().getPPr().getSpcAft().getSpcPct().getVal());
assertFalse(p.getXmlObject().getPPr().getSpcAft().isSetSpcPts()); assertFalse(p.getXmlObject().getPPr().getSpcAft().isSetSpcPts());
p.setSpaceAfter(-20); p.setSpaceAfter(-20d);
assertEquals(2000, p.getXmlObject().getPPr().getSpcAft().getSpcPts().getVal()); assertEquals(2000, p.getXmlObject().getPPr().getSpcAft().getSpcPts().getVal());
assertFalse(p.getXmlObject().getPPr().getSpcAft().isSetSpcPct()); assertFalse(p.getXmlObject().getPPr().getSpcAft().isSetSpcPct());
p.setSpaceAfter(-10); p.setSpaceAfter(-10d);
assertEquals(1000, p.getXmlObject().getPPr().getSpcAft().getSpcPts().getVal()); assertEquals(1000, p.getXmlObject().getPPr().getSpcAft().getSpcPts().getVal());
assertFalse(p.getXmlObject().getPPr().getSpcAft().isSetSpcPct()); assertFalse(p.getXmlObject().getPPr().getSpcAft().isSetSpcPct());
assertFalse(p.getXmlObject().getPPr().isSetSpcBef()); assertFalse(p.getXmlObject().getPPr().isSetSpcBef());
p.setSpaceBefore(200); p.setSpaceBefore(200d);
assertEquals(200000, p.getXmlObject().getPPr().getSpcBef().getSpcPct().getVal()); assertEquals(200000, p.getXmlObject().getPPr().getSpcBef().getSpcPct().getVal());
assertFalse(p.getXmlObject().getPPr().getSpcBef().isSetSpcPts()); assertFalse(p.getXmlObject().getPPr().getSpcBef().isSetSpcPts());
p.setSpaceBefore(100); p.setSpaceBefore(100d);
assertEquals(100000, p.getXmlObject().getPPr().getSpcBef().getSpcPct().getVal()); assertEquals(100000, p.getXmlObject().getPPr().getSpcBef().getSpcPct().getVal());
assertFalse(p.getXmlObject().getPPr().getSpcBef().isSetSpcPts()); assertFalse(p.getXmlObject().getPPr().getSpcBef().isSetSpcPts());
p.setSpaceBefore(-20); p.setSpaceBefore(-20d);
assertEquals(2000, p.getXmlObject().getPPr().getSpcBef().getSpcPts().getVal()); assertEquals(2000, p.getXmlObject().getPPr().getSpcBef().getSpcPts().getVal());
assertFalse(p.getXmlObject().getPPr().getSpcBef().isSetSpcPct()); assertFalse(p.getXmlObject().getPPr().getSpcBef().isSetSpcPct());
p.setSpaceBefore(-10); p.setSpaceBefore(-10d);
assertEquals(1000, p.getXmlObject().getPPr().getSpcBef().getSpcPts().getVal()); assertEquals(1000, p.getXmlObject().getPPr().getSpcBef().getSpcPts().getVal());
assertFalse(p.getXmlObject().getPPr().getSpcBef().isSetSpcPct()); assertFalse(p.getXmlObject().getPPr().getSpcBef().isSetSpcPct());
assertFalse(p.getXmlObject().getPPr().isSetLnSpc()); assertFalse(p.getXmlObject().getPPr().isSetLnSpc());
p.setLineSpacing(200); p.setLineSpacing(200d);
assertEquals(200000, p.getXmlObject().getPPr().getLnSpc().getSpcPct().getVal()); assertEquals(200000, p.getXmlObject().getPPr().getLnSpc().getSpcPct().getVal());
assertFalse(p.getXmlObject().getPPr().getLnSpc().isSetSpcPts()); assertFalse(p.getXmlObject().getPPr().getLnSpc().isSetSpcPts());
p.setLineSpacing(100); p.setLineSpacing(100d);
assertEquals(100000, p.getXmlObject().getPPr().getLnSpc().getSpcPct().getVal()); assertEquals(100000, p.getXmlObject().getPPr().getLnSpc().getSpcPct().getVal());
assertFalse(p.getXmlObject().getPPr().getLnSpc().isSetSpcPts()); assertFalse(p.getXmlObject().getPPr().getLnSpc().isSetSpcPts());
p.setLineSpacing(-20); p.setLineSpacing(-20d);
assertEquals(2000, p.getXmlObject().getPPr().getLnSpc().getSpcPts().getVal()); assertEquals(2000, p.getXmlObject().getPPr().getLnSpc().getSpcPts().getVal());
assertFalse(p.getXmlObject().getPPr().getLnSpc().isSetSpcPct()); assertFalse(p.getXmlObject().getPPr().getLnSpc().isSetSpcPct());
p.setLineSpacing(-10); p.setLineSpacing(-10d);
assertEquals(1000, p.getXmlObject().getPPr().getLnSpc().getSpcPts().getVal()); assertEquals(1000, p.getXmlObject().getPPr().getLnSpc().getSpcPts().getVal());
assertFalse(p.getXmlObject().getPPr().getLnSpc().isSetSpcPct()); assertFalse(p.getXmlObject().getPPr().getLnSpc().isSetSpcPct());

View File

@ -92,7 +92,7 @@ public class TestXSLFTextParagraph {
assertEquals(expectedWidth, dtp.getWrappingWidth(true, null), 0); assertEquals(expectedWidth, dtp.getWrappingWidth(true, null), 0);
assertEquals(expectedWidth, dtp.getWrappingWidth(false, null), 0); assertEquals(expectedWidth, dtp.getWrappingWidth(false, null), 0);
p.setLeftMargin(36); // 0.5" p.setLeftMargin(36d); // 0.5"
leftMargin = p.getLeftMargin(); leftMargin = p.getLeftMargin();
assertEquals(36.0, leftMargin, 0); assertEquals(36.0, leftMargin, 0);
expectedWidth = anchor.getWidth() - leftInset - rightInset - leftMargin; expectedWidth = anchor.getWidth() - leftInset - rightInset - leftMargin;

View File

@ -21,12 +21,13 @@ package org.apache.poi.hslf.model.textproperties;
* Definition for the font alignment property. * Definition for the font alignment property.
*/ */
public class FontAlignmentProp extends TextProp { public class FontAlignmentProp extends TextProp {
public static final String NAME = "fontAlign";
public static final int BASELINE = 0; public static final int BASELINE = 0;
public static final int TOP = 1; public static final int TOP = 1;
public static final int CENTER = 2; public static final int CENTER = 2;
public static final int BOTTOM = 3; public static final int BOTTOM = 3;
public FontAlignmentProp() { public FontAlignmentProp() {
super(2, 0x10000, "fontAlign"); super(2, 0x10000, NAME);
} }
} }

View File

@ -31,47 +31,10 @@ import org.apache.poi.util.LittleEndian;
* properties, and the indent level if required. * properties, and the indent level if required.
*/ */
public class TextPropCollection { 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 */ /** All the different kinds of paragraph properties we might handle */
public static final TextProp[] paragraphTextPropTypes = { public static final TextProp[] paragraphTextPropTypes = {
// TextProp order is according to 2.9.20 TextPFException, // TextProp order is according to 2.9.20 TextPFException,
// bitmask order can be different // 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 ParagraphFlagsTextProp(),
new TextProp(2, 0x80, "bullet.char"), new TextProp(2, 0x80, "bullet.char"),
new TextProp(2, 0x10, "bullet.font"), new TextProp(2, 0x10, "bullet.font"),
@ -95,24 +58,9 @@ public class TextPropCollection {
new TextProp(0, 0x2000000, "hasBulletScheme"), // TODO: check size new TextProp(0, 0x2000000, "hasBulletScheme"), // TODO: check size
// 0xFC000000 MUST be zero and MUST be ignored // 0xFC000000 MUST be zero and MUST be ignored
}; };
/** All the different kinds of character properties we might handle */ /** All the different kinds of character properties we might handle */
public static final TextProp[] characterTextPropTypes = new TextProp[] { 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, 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, 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. new TextProp(0, 0x2000000, "cs.font.index"), // A bit that specifies whether the csFontRef field of the TextCFException10 structure that contains this CFMasks exists.
@ -167,6 +115,19 @@ public class TextPropCollection {
return null; return null;
} }
public TextProp removeByName(String name) {
Iterator<TextProp> 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 */ /** Add the TextProp with this name to the list */
public TextProp addWithName(String name) { public TextProp addWithName(String name) {
// Find the base TextProp to base on // Find the base TextProp to base on
@ -192,6 +153,10 @@ public class TextPropCollection {
return textProp; return textProp;
} }
public TextPropType getTextPropType() {
return textPropType;
}
private TextProp[] getPotentialProperties() { private TextProp[] getPotentialProperties() {
return (textPropType == TextPropType.paragraph) ? paragraphTextPropTypes : characterTextPropTypes; return (textPropType == TextPropType.paragraph) ? paragraphTextPropTypes : characterTextPropTypes;
} }

View File

@ -17,6 +17,7 @@
package org.apache.poi.hslf.record; package org.apache.poi.hslf.record;
import org.apache.poi.hslf.model.PPFont;
import org.apache.poi.util.POILogger; import org.apache.poi.util.POILogger;
import java.io.*; import java.io.*;
@ -75,9 +76,9 @@ public final class FontCollection extends RecordContainer {
*/ */
public int addFont(String name) { public int addFont(String name) {
int idx = getFontIndex(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) { public int addFont(String name, int charset, int flags, int type, int pitch) {

View File

@ -76,16 +76,17 @@ public final class HSLFSlideMaster extends HSLFMasterSheet {
* This is the "workhorse" which returns the default style attrubutes. * This is the "workhorse" which returns the default style attrubutes.
*/ */
public TextProp getStyleAttribute(int txtype, int level, String name, boolean isCharacter) { public TextProp getStyleAttribute(int txtype, int level, String name, boolean isCharacter) {
if (_txmaster.length <= txtype) return null;
TxMasterStyleAtom t = _txmaster[txtype];
List<TextPropCollection> styles = isCharacter ? t.getCharacterStyles() : t.getParagraphStyles();
TextProp prop = null; TextProp prop = null;
for (int i = level; i >= 0; i--) { for (int i = Math.min(level, styles.size()-1); prop == null && i >= 0; i--) {
List<TextPropCollection> styles = prop = styles.get(i).findByName(name);
isCharacter ? _txmaster[txtype].getCharacterStyles() : _txmaster[txtype].getParagraphStyles();
if (i < styles.size()) prop = styles.get(i).findByName(name);
if (prop != null) break;
} }
if (prop == null) {
if(isCharacter) { if (prop != null) return prop;
switch (txtype) { switch (txtype) {
case TextHeaderAtom.CENTRE_BODY_TYPE: case TextHeaderAtom.CENTRE_BODY_TYPE:
case TextHeaderAtom.HALF_BODY_TYPE: case TextHeaderAtom.HALF_BODY_TYPE:
@ -98,23 +99,8 @@ public final class HSLFSlideMaster extends HSLFMasterSheet {
default: default:
return null; return null;
} }
} else {
switch (txtype) { return getStyleAttribute(txtype, level, name, isCharacter);
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);
}
return prop;
} }
/** /**

View File

@ -44,11 +44,10 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
/** /**
* How to align the text * How to align the text
*/ */
/* package */ static final int AlignLeft = 0; /* package */static final int AlignLeft = 0;
/* package */ static final int AlignCenter = 1; /* package */static final int AlignCenter = 1;
/* package */ static final int AlignRight = 2; /* package */static final int AlignRight = 2;
/* package */ static final int AlignJustify = 3; /* package */static final int AlignJustify = 3;
// Note: These fields are protected to help with unit testing // Note: These fields are protected to help with unit testing
// Other classes shouldn't really go playing with them! // Other classes shouldn't really go playing with them!
@ -58,7 +57,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
private final TextPropCollection _paragraphStyle = new TextPropCollection(1, TextPropType.paragraph); private final TextPropCollection _paragraphStyle = new TextPropCollection(1, TextPropType.paragraph);
protected TextRulerAtom _ruler; protected TextRulerAtom _ruler;
protected List<HSLFTextRun> _runs = new ArrayList<HSLFTextRun>(); protected final List<HSLFTextRun> _runs = new ArrayList<HSLFTextRun>();
protected HSLFTextShape _parentShape; protected HSLFTextShape _parentShape;
private HSLFSheet _sheet; private HSLFSheet _sheet;
private int shapeId; private int shapeId;
@ -87,7 +86,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
_charAtom = tca; _charAtom = tca;
} }
/* package */ HSLFTextParagraph(HSLFTextParagraph other) { /* package */HSLFTextParagraph(HSLFTextParagraph other) {
_headerAtom = other._headerAtom; _headerAtom = other._headerAtom;
_byteAtom = other._byteAtom; _byteAtom = other._byteAtom;
_charAtom = other._charAtom; _charAtom = other._charAtom;
@ -122,37 +121,37 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
* Supply the Sheet we belong to, which might have an assigned SlideShow * Supply the Sheet we belong to, which might have an assigned SlideShow
* Also passes it on to our child RichTextRuns * Also passes it on to our child RichTextRuns
*/ */
public void supplySheet(HSLFSheet sheet){ public void supplySheet(HSLFSheet sheet) {
this._sheet = sheet; this._sheet = sheet;
if (_runs == null) return; if (_runs == null) return;
for(HSLFTextRun rt : _runs) { for (HSLFTextRun rt : _runs) {
rt.updateSheet(); rt.updateSheet();
} }
} }
public HSLFSheet getSheet(){ public HSLFSheet getSheet() {
return this._sheet; return this._sheet;
} }
/** /**
* @return Shape ID * @return Shape ID
*/ */
protected int getShapeId(){ protected int getShapeId() {
return shapeId; return shapeId;
} }
/** /**
* @param id Shape ID * @param id Shape ID
*/ */
protected void setShapeId(int id){ protected void setShapeId(int id) {
shapeId = 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; return (_headerAtom != null) ? _headerAtom.getIndex() : -1;
} }
@ -178,7 +177,6 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
if (_headerAtom != null) _headerAtom.setTextType(runType); if (_headerAtom != null) _headerAtom.setTextType(runType);
} }
/** /**
* Is this Text Run one from a {@link PPDrawing}, or is it * Is this Text Run one from a {@link PPDrawing}, or is it
* one from the {@link SlideListWithText}? * one from the {@link SlideListWithText}?
@ -187,12 +185,12 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
return (getIndex() == -1); return (getIndex() == -1);
} }
public TextRulerAtom getTextRuler(){ public TextRulerAtom getTextRuler() {
return _ruler; return _ruler;
} }
public TextRulerAtom createTextRuler(){ public TextRulerAtom createTextRuler() {
_ruler = getTextRuler(); _ruler = getTextRuler();
if (_ruler == null) { if (_ruler == null) {
_ruler = TextRulerAtom.getParagraphInstance(); _ruler = TextRulerAtom.getParagraphInstance();
@ -210,9 +208,9 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
* *
* @return text run records * @return text run records
*/ */
public Record[] getRecords(){ public Record[] getRecords() {
Record r[] = _headerAtom.getParentRecord().getChildRecords(); 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) { private static Record[] getRecords(Record[] records, int[] startIdx, TextHeaderAtom headerAtom) {
@ -231,7 +229,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
} }
int length; 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; if (records[startIdx[0]+length] instanceof TextHeaderAtom) break;
} }
@ -252,87 +250,63 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
return this.styleTextProp9Atom; 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();
}
/**
* 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 @Override
public Iterator<HSLFTextRun> iterator() { public Iterator<HSLFTextRun> iterator() {
return _runs.iterator(); return _runs.iterator();
} }
@Override @Override
public double getLeftMargin() { public Double getLeftMargin() {
int val = getParaTextPropVal("text.offset"); TextProp val = getPropVal(_paragraphStyle, "text.offset", this);
return val*HSLFShape.POINT_DPI/((double)HSLFShape.MASTER_DPI); return (val == null) ? null : Units.masterToPoints(val.getValue());
} }
@Override @Override
public void setLeftMargin(double leftMargin) { public void setLeftMargin(Double leftMargin) {
int val = (int)(leftMargin*HSLFShape.MASTER_DPI/HSLFShape.POINT_DPI); Integer val = (leftMargin == null) ? null : Units.pointsToMaster(leftMargin);
setParaTextPropVal("text.offset", val); setPropVal(_paragraphStyle, "text.offset", val);
} }
@Override @Override
public double getRightMargin() { public Double getRightMargin() {
// TODO: find out, how to determine this value // TODO: find out, how to determine this value
return 0; return null;
} }
@Override @Override
public void setRightMargin(double rightMargin) { public void setRightMargin(Double rightMargin) {
// TODO: find out, how to set this value // TODO: find out, how to set this value
} }
@Override @Override
public double getIndent() { public Double getIndent() {
int val = getParaTextPropVal("bullet.offset"); TextProp val = getPropVal(_paragraphStyle, "bullet.offset", this);
return val*HSLFShape.POINT_DPI/((double)HSLFShape.MASTER_DPI); return (val == null) ? null : Units.masterToPoints(val.getValue());
} }
@Override @Override
public void setIndent(double intent) { public void setIndent(Double indent) {
int val = (int)(intent*HSLFShape.MASTER_DPI/HSLFShape.POINT_DPI); Integer val = (indent == null) ? null : Units.pointsToMaster(indent);
setParaTextPropVal("bullet.offset", val); setPropVal(_paragraphStyle, "bullet.offset", val);
} }
@Override @Override
public String getDefaultFontFamily() { 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 @Override
public double getDefaultFontSize() { public Double getDefaultFontSize() {
return (_runs.isEmpty() ? 12 : _runs.get(0).getFontSize()); Double d = null;
if (!_runs.isEmpty()) {
d = _runs.get(0).getFontSize();
}
return (d != null) ? d : 12d;
} }
/** /**
@ -341,10 +315,10 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
* @param align - the type of alignment * @param align - the type of alignment
*/ */
public void setAlignment(org.apache.poi.sl.usermodel.TextParagraph.TextAlign align) { public void setAlignment(org.apache.poi.sl.usermodel.TextParagraph.TextAlign align) {
int alignInt; Integer alignInt = null;
switch (align) { if (align != null) switch (align) {
default: default:
case LEFT: alignInt = TextAlignmentProp.LEFT; break; case LEFT: alignInt = TextAlignmentProp.LEFT;break;
case CENTER: alignInt = TextAlignmentProp.CENTER; break; case CENTER: alignInt = TextAlignmentProp.CENTER; break;
case RIGHT: alignInt = TextAlignmentProp.RIGHT; break; case RIGHT: alignInt = TextAlignmentProp.RIGHT; break;
case DIST: alignInt = TextAlignmentProp.DISTRIBUTED; break; case DIST: alignInt = TextAlignmentProp.DISTRIBUTED; break;
@ -352,12 +326,14 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
case JUSTIFY_LOW: alignInt = TextAlignmentProp.JUSTIFYLOW; break; case JUSTIFY_LOW: alignInt = TextAlignmentProp.JUSTIFYLOW; break;
case THAI_DIST: alignInt = TextAlignmentProp.THAIDISTRIBUTED; break; case THAI_DIST: alignInt = TextAlignmentProp.THAIDISTRIBUTED; break;
} }
setParaTextPropVal("alignment", alignInt); setPropVal(_paragraphStyle, "alignment", alignInt);
} }
@Override @Override
public org.apache.poi.sl.usermodel.TextParagraph.TextAlign getTextAlign() { 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: default:
case TextAlignmentProp.LEFT: return TextAlign.LEFT; case TextAlignmentProp.LEFT: return TextAlign.LEFT;
case TextAlignmentProp.CENTER: return TextAlign.CENTER; case TextAlignmentProp.CENTER: return TextAlign.CENTER;
@ -371,34 +347,33 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
@Override @Override
public FontAlign getFontAlign() { public FontAlign getFontAlign() {
switch(getParaTextPropVal("fontAlign")) { TextProp tp = getPropVal(_paragraphStyle, FontAlignmentProp.NAME, this);
default: if (tp == null) return null;
case -1: return FontAlign.AUTO;
switch (tp.getValue()) {
case FontAlignmentProp.BASELINE: return FontAlign.BASELINE; case FontAlignmentProp.BASELINE: return FontAlign.BASELINE;
case FontAlignmentProp.TOP: return FontAlign.TOP; case FontAlignmentProp.TOP: return FontAlign.TOP;
case FontAlignmentProp.CENTER: return FontAlign.CENTER; case FontAlignmentProp.CENTER: return FontAlign.CENTER;
case FontAlignmentProp.BOTTOM: return FontAlign.BOTTOM; case FontAlignmentProp.BOTTOM: return FontAlign.BOTTOM;
default: return FontAlign.AUTO;
} }
} }
@Override @Override
public BulletStyle getBulletStyle() { public BulletStyle getBulletStyle() {
if (getBulletChar() == 0) return null; if (!isBullet()) return null;
return new BulletStyle() { return new BulletStyle() {
public String getBulletCharacter() { public String getBulletCharacter() {
char chr = HSLFTextParagraph.this.getBulletChar(); Character chr = HSLFTextParagraph.this.getBulletChar();
return (chr == 0 ? "" : ""+chr); return (chr == null || chr == 0) ? "" : "" + chr;
} }
public String getBulletFont() { public String getBulletFont() {
int fontIdx = HSLFTextParagraph.this.getBulletFont(); return HSLFTextParagraph.this.getBulletFont();
if (fontIdx == -1) return getDefaultFontFamily();
PPFont ppFont = getSheet().getSlideShow().getFont(fontIdx);
return ppFont.getFontName();
} }
public double getBulletFontSize() { public Double getBulletFontSize() {
return HSLFTextParagraph.this.getBulletSize(); return HSLFTextParagraph.this.getBulletSize();
} }
@ -417,7 +392,6 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
_parentShape = parentShape; _parentShape = parentShape;
} }
/** /**
* *
* @return indentation level * @return indentation level
@ -449,66 +423,62 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
return getFlag(ParagraphFlagsTextProp.BULLET_IDX); 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 * Sets the bullet character
*/ */
public void setBulletChar(char c) { public void setBulletChar(Character c) {
setParaTextPropVal("bullet.char", c); Integer val = (c == null) ? null : (int)c.charValue();
setPropVal(_paragraphStyle, "bullet.char", val);
} }
/** /**
* Returns the bullet character * Returns the bullet character
*/ */
public char getBulletChar() { public Character getBulletChar() {
int val = getParaTextPropVal("bullet.char"); TextProp tp = getPropVal(_paragraphStyle, "bullet.char", this);
return (char)(val == -1 ? 0 : val); return (tp == null) ? null : (char)tp.getValue();
} }
/** /**
* Sets the bullet size * Sets the bullet size
*/ */
public void setBulletSize(int size) { public void setBulletSize(Double size) {
setParaTextPropVal("bullet.size", size); setPctOrPoints("bullet.size", size);
} }
/** /**
* Returns the bullet size * Returns the bullet size, null if unset
*/ */
public int getBulletSize() { public Double getBulletSize() {
return getParaTextPropVal("bullet.size"); return getPctOrPoints("bullet.size");
} }
/** /**
* Sets the bullet color * Sets the bullet color
*/ */
public void setBulletColor(Color color) { public void setBulletColor(Color color) {
int rgb = new Color(color.getBlue(), color.getGreen(), color.getRed(), 254).getRGB(); Integer val = (color == null) ? null : new Color(color.getBlue(), color.getGreen(), color.getRed(), 254).getRGB();
setParaTextPropVal("bullet.color", rgb); setPropVal(_paragraphStyle, "bullet.color", val);
} }
/** /**
* Returns the bullet color * Returns the bullet color
*/ */
public Color getBulletColor() { public Color getBulletColor() {
int rgb = getParaTextPropVal("bullet.color"); TextProp tp = getPropVal(_paragraphStyle, "bullet.color", this);
if (rgb == -1) { if (tp == null) {
// if bullet color is undefined, return color of first run // if bullet color is undefined, return color of first run
if (_runs.isEmpty()) return null; return (_runs.isEmpty()) ? null : _runs.get(0).getFontColor();
return _runs.get(0).getFontColor();
} }
int rgb = tp.getValue();
int cidx = rgb >> 24; int cidx = rgb >> 24;
if (rgb % 0x1000000 == 0){ if (rgb % 0x1000000 == 0) {
if (_sheet == null) return null; if (_sheet == null)
return null;
ColorSchemeAtom ca = _sheet.getColorScheme(); ColorSchemeAtom ca = _sheet.getColorScheme();
if(cidx >= 0 && cidx <= 7) rgb = ca.getColor(cidx); if (cidx >= 0 && cidx <= 7)
rgb = ca.getColor(cidx);
} }
Color tmp = new Color(rgb, true); Color tmp = new Color(rgb, true);
return new Color(tmp.getBlue(), tmp.getGreen(), tmp.getRed()); return new Color(tmp.getBlue(), tmp.getGreen(), tmp.getRed());
@ -517,124 +487,133 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
/** /**
* Sets the bullet font * Sets the bullet font
*/ */
public void setBulletFont(int idx) { public void setBulletFont(String typeface) {
setParaTextPropVal("bullet.font", idx); 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); setFlag(ParagraphFlagsTextProp.BULLET_HARDFONT_IDX, true);
} }
/** /**
* Returns the bullet font * Returns the bullet font
*/ */
public int getBulletFont() { public String getBulletFont() {
return getParaTextPropVal("bullet.font"); 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 @Override
public void setLineSpacing(double lineSpacing) { public void setLineSpacing(Double lineSpacing) {
// if lineSpacing < 0, we need to convert points (common interface) to master units (hslf) setPctOrPoints("linespacing", lineSpacing);
if (lineSpacing < 0) {
lineSpacing = (lineSpacing*HSLFShape.MASTER_DPI/HSLFShape.POINT_DPI);
}
setParaTextPropVal("linespacing", (int)lineSpacing);
} }
@Override @Override
public double getLineSpacing() { public Double getLineSpacing() {
double val = getParaTextPropVal("linespacing"); return getPctOrPoints("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.
* <p>
* 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.
* </p>
*/
public void setSpaceBefore(int val) {
setParaTextPropVal("spacebefore", val);
}
/**
* Returns spacing before a paragraph
* <p>
* 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.
* </p>
*
* @return the spacing before a paragraph
*/
@Override @Override
public double getSpaceBefore() { public void setSpaceBefore(Double spaceBefore) {
int val = getParaTextPropVal("spacebefore"); setPctOrPoints("spacebefore", spaceBefore);
return val == -1 ? 0 : val;
} }
/**
* Sets spacing after a paragraph.
* <p>
* 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.
* </p>
*/
public void setSpaceAfter(int val) {
setParaTextPropVal("spaceafter", val);
}
/**
* Returns spacing after a paragraph
* <p>
* 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.
* </p>
*
* @return the spacing before a paragraph
*/
@Override @Override
public double getSpaceAfter() { public Double getSpaceBefore() {
int val = getParaTextPropVal("spaceafter"); return getPctOrPoints("spacebefore");
return val == -1 ? 0 : val; }
@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);
} }
/** /**
* Returns the named TextProp, either by fetching it (if it exists) or adding it * Fetch the value of the given Paragraph related TextProp. Returns null if
* (if it didn't) * that TextProp isn't present. If the TextProp isn't present, the value
* @param textPropCol The TextPropCollection to fetch from / add into * from the appropriate Master Sheet will apply.
* @param textPropName The name of the TextProp to fetch/add
*/ */
protected static TextProp fetchOrAddTextProp(TextPropCollection textPropCol, String textPropName) { protected static TextProp getPropVal(TextPropCollection props, String propName, HSLFTextParagraph paragraph) {
// Fetch / Add the TextProp TextProp prop = props.findByName(propName);
return textPropCol.addWithName(textPropName); if (prop != null) return prop;
}
protected boolean getFlag(int index) { BitMaskTextProp maskProp = (BitMaskTextProp) props.findByName(ParagraphFlagsTextProp.NAME);
if (_paragraphStyle == null) return false; boolean hardAttribute = (maskProp != null && maskProp.getValue() == 0);
if (hardAttribute) return null;
BitMaskTextProp prop = (BitMaskTextProp) _paragraphStyle.findByName(ParagraphFlagsTextProp.NAME); HSLFSheet sheet = paragraph.getSheet();
int txtype = paragraph.getRunType();
if (prop == null) { HSLFMasterSheet master = sheet.getMasterSheet();
if (_sheet != null) { if (master == 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"); logger.log(POILogger.WARN, "MasterSheet is not available");
} return null;
} }
return prop == null ? false : prop.getSubValue(index); boolean isChar = props.getTextPropType() == TextPropType.character;
return master.getStyleAttribute(txtype, paragraph.getIndentLevel(), propName, isChar);
} }
protected void setFlag(int index, boolean value) { /**
// Ensure we have the StyleTextProp atom we're going to need * Returns the named TextProp, either by fetching it (if it exists) or
assert(_paragraphStyle!=null); * adding it (if it didn't)
BitMaskTextProp prop = (BitMaskTextProp) fetchOrAddTextProp(_paragraphStyle, ParagraphFlagsTextProp.NAME); *
prop.setSubValue(value,index); * @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);
} }
/** /**
@ -646,14 +625,14 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
HSLFTextRun lastRun = null; HSLFTextRun lastRun = null;
for (HSLFTextParagraph p : paragraphs) { for (HSLFTextParagraph p : paragraphs) {
if (lastRun != null && !lastRun.getRawText().endsWith("\r")) { if (lastRun != null && !lastRun.getRawText().endsWith("\r")) {
lastRun.setText(lastRun.getRawText()+"\r"); lastRun.setText(lastRun.getRawText() + "\r");
} }
List<HSLFTextRun> ltr = p.getTextRuns(); List<HSLFTextRun> ltr = p.getTextRuns();
if (ltr.isEmpty()) { if (ltr.isEmpty()) {
throw new RuntimeException("paragraph without textruns found"); throw new RuntimeException("paragraph without textruns found");
} }
lastRun = ltr.get(ltr.size()-1); lastRun = ltr.get(ltr.size() - 1);
assert(lastRun.getRawText() != null); assert (lastRun.getRawText() != null);
} }
} }
@ -675,7 +654,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
afterHeader |= (header == record); afterHeader |= (header == record);
if (afterHeader && rt == RecordTypes.StyleTextPropAtom.typeID) { if (afterHeader && rt == RecordTypes.StyleTextPropAtom.typeID) {
// found it // found it
style = (StyleTextPropAtom)record; style = (StyleTextPropAtom) record;
} }
} }
@ -691,7 +670,6 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
return style; return style;
} }
/** /**
* Saves the modified paragraphs/textrun to the records. * Saves the modified paragraphs/textrun to the records.
* Also updates the styles to the correct text length. * Also updates the styles to the correct text length.
@ -726,15 +704,15 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
} }
newRecord = byteAtom; newRecord = byteAtom;
byte[] byteText = new byte[rawText.length()]; byte[] byteText = new byte[rawText.length()];
StringUtil.putCompressedUnicode(rawText,byteText,0); StringUtil.putCompressedUnicode(rawText, byteText, 0);
byteAtom.setText(byteText); byteAtom.setText(byteText);
} }
assert(newRecord != null); assert (newRecord != null);
RecordContainer _txtbox = headerAtom.getParentRecord(); RecordContainer _txtbox = headerAtom.getParentRecord();
Record[] cr = _txtbox.getChildRecords(); Record[] cr = _txtbox.getChildRecords();
int headerIdx = -1, textIdx = -1, styleIdx = -1; int headerIdx = -1, textIdx = -1, styleIdx = -1;
for (int i=0; i<cr.length; i++) { for (int i = 0; i < cr.length; i++) {
Record r = cr[i]; Record r = cr[i];
if (r == headerAtom) headerIdx = i; if (r == headerAtom) headerIdx = i;
else if (r == oldRecord || r == newRecord) textIdx = i; else if (r == oldRecord || r == newRecord) textIdx = i;
@ -744,7 +722,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
if (textIdx == -1) { if (textIdx == -1) {
// the old record was never registered, ignore it // the old record was never registered, ignore it
_txtbox.addChildAfter(newRecord, headerAtom); _txtbox.addChildAfter(newRecord, headerAtom);
textIdx = headerIdx+1; textIdx = headerIdx + 1;
} else { } else {
// swap not appropriated records - noop if unchanged // swap not appropriated records - noop if unchanged
cr[textIdx] = newRecord; cr[textIdx] = newRecord;
@ -787,18 +765,18 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
lastRTPC.copy(rtpc); lastRTPC.copy(rtpc);
} }
int len = tr.getLength(); int len = tr.getLength();
ptpc.updateTextSize(ptpc.getCharactersCovered()+len); ptpc.updateTextSize(ptpc.getCharactersCovered() + len);
rtpc.updateTextSize(len); rtpc.updateTextSize(len);
lastPTPC.updateTextSize(lastPTPC.getCharactersCovered()+len); lastPTPC.updateTextSize(lastPTPC.getCharactersCovered() + len);
lastRTPC.updateTextSize(lastRTPC.getCharactersCovered()+len); lastRTPC.updateTextSize(lastRTPC.getCharactersCovered() + len);
} }
} }
assert(lastPTPC != null && lastRTPC != null && ptpc != null && rtpc != null); assert (lastPTPC != null && lastRTPC != null && ptpc != null && rtpc != null);
ptpc.updateTextSize(ptpc.getCharactersCovered()+1); ptpc.updateTextSize(ptpc.getCharactersCovered() + 1);
rtpc.updateTextSize(rtpc.getCharactersCovered()+1); rtpc.updateTextSize(rtpc.getCharactersCovered() + 1);
lastPTPC.updateTextSize(lastPTPC.getCharactersCovered()+1); lastPTPC.updateTextSize(lastPTPC.getCharactersCovered() + 1);
lastRTPC.updateTextSize(lastRTPC.getCharactersCovered()+1); lastRTPC.updateTextSize(lastRTPC.getCharactersCovered() + 1);
/** /**
* If TextSpecInfoAtom is present, we must update the text size in it, * If TextSpecInfoAtom is present, we must update the text size in it,
@ -806,14 +784,14 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
*/ */
for (Record r : paragraphs.get(0).getRecords()) { for (Record r : paragraphs.get(0).getRecords()) {
if (r instanceof TextSpecInfoAtom) { if (r instanceof TextSpecInfoAtom) {
((TextSpecInfoAtom)r).setParentSize(rawText.length()+1); ((TextSpecInfoAtom) r).setParentSize(rawText.length() + 1);
break; break;
} }
} }
if (_txtbox instanceof EscherTextboxWrapper) { if (_txtbox instanceof EscherTextboxWrapper) {
try { try {
((EscherTextboxWrapper)_txtbox).writeOut(null); ((EscherTextboxWrapper) _txtbox).writeOut(null);
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException("failed dummy write", e); throw new RuntimeException("failed dummy write", e);
} }
@ -832,8 +810,8 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
// check paragraphs // check paragraphs
assert(!paragraphs.isEmpty() && !paragraphs.get(0).getTextRuns().isEmpty()); assert(!paragraphs.isEmpty() && !paragraphs.get(0).getTextRuns().isEmpty());
HSLFTextParagraph htp = paragraphs.get(paragraphs.size()-1); HSLFTextParagraph htp = paragraphs.get(paragraphs.size() - 1);
HSLFTextRun htr = htp.getTextRuns().get(htp.getTextRuns().size()-1); HSLFTextRun htr = htp.getTextRuns().get(htp.getTextRuns().size() - 1);
boolean isFirst = !newParagraph; boolean isFirst = !newParagraph;
for (String rawText : text.split("(?<=\r)")) { for (String rawText : text.split("(?<=\r)")) {
@ -876,7 +854,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
Iterator<HSLFTextParagraph> paraIter = paragraphs.iterator(); Iterator<HSLFTextParagraph> paraIter = paragraphs.iterator();
HSLFTextParagraph htp = paraIter.next(); // keep first HSLFTextParagraph htp = paraIter.next(); // keep first
assert(htp != null); assert (htp != null);
while (paraIter.hasNext()) { while (paraIter.hasNext()) {
paraIter.next(); paraIter.next();
paraIter.remove(); paraIter.remove();
@ -885,7 +863,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
Iterator<HSLFTextRun> runIter = htp.getTextRuns().iterator(); Iterator<HSLFTextRun> runIter = htp.getTextRuns().iterator();
HSLFTextRun htr = runIter.next(); HSLFTextRun htr = runIter.next();
htr.setText(""); htr.setText("");
assert(htr != null); assert (htr != null);
while (runIter.hasNext()) { while (runIter.hasNext()) {
runIter.next(); runIter.next();
runIter.remove(); runIter.remove();
@ -895,7 +873,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
} }
public static String getText(List<HSLFTextParagraph> paragraphs) { public static String getText(List<HSLFTextParagraph> paragraphs) {
assert(!paragraphs.isEmpty()); assert (!paragraphs.isEmpty());
String rawText = getRawText(paragraphs); String rawText = getRawText(paragraphs);
return toExternalString(rawText, paragraphs.get(0).getRunType()); return toExternalString(rawText, paragraphs.get(0).getRunType());
} }
@ -982,7 +960,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
} }
List<List<HSLFTextParagraph>> sheetRuns = sheet.getTextParagraphs(); List<List<HSLFTextParagraph>> sheetRuns = sheet.getTextParagraphs();
assert(sheetRuns != null); assert (sheetRuns != null);
int idx = ota.getTextIndex(); int idx = ota.getTextIndex();
for (List<HSLFTextParagraph> r : sheetRuns) { for (List<HSLFTextParagraph> r : sheetRuns) {
@ -1000,14 +978,14 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
} }
} }
} }
if(rv == null || rv.isEmpty()) { if (rv == null || rv.isEmpty()) {
logger.log(POILogger.WARN, "text run not found for OutlineTextRefAtom.TextIndex=" + idx); logger.log(POILogger.WARN, "text run not found for OutlineTextRefAtom.TextIndex=" + idx);
} }
} else { } else {
if (sheet != null) { if (sheet != null) {
// check sheet runs first, so we get exactly the same paragraph list // check sheet runs first, so we get exactly the same paragraph list
List<List<HSLFTextParagraph>> sheetRuns = sheet.getTextParagraphs(); List<List<HSLFTextParagraph>> sheetRuns = sheet.getTextParagraphs();
assert(sheetRuns != null); assert (sheetRuns != null);
for (List<HSLFTextParagraph> paras : sheetRuns) { for (List<HSLFTextParagraph> paras : sheetRuns) {
if (!paras.isEmpty() && paras.get(0)._headerAtom.getParentRecord() == wrapper) { if (!paras.isEmpty() && paras.get(0)._headerAtom.getParentRecord() == wrapper) {
@ -1050,7 +1028,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
protected static List<List<HSLFTextParagraph>> findTextParagraphs(Record[] records) { protected static List<List<HSLFTextParagraph>> findTextParagraphs(Record[] records) {
List<List<HSLFTextParagraph>> paragraphCollection = new ArrayList<List<HSLFTextParagraph>>(); List<List<HSLFTextParagraph>> paragraphCollection = new ArrayList<List<HSLFTextParagraph>>();
int[] recordIdx = {0}; int[] recordIdx = { 0 };
for (int slwtIndex = 0; recordIdx[0] < records.length; slwtIndex++) { for (int slwtIndex = 0; recordIdx[0] < records.length; slwtIndex++) {
TextHeaderAtom header = null; TextHeaderAtom header = null;
@ -1062,15 +1040,15 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
for (Record r : getRecords(records, recordIdx, null)) { for (Record r : getRecords(records, recordIdx, null)) {
long rt = r.getRecordType(); long rt = r.getRecordType();
if (RecordTypes.TextHeaderAtom.typeID == rt) { if (RecordTypes.TextHeaderAtom.typeID == rt) {
header = (TextHeaderAtom)r; header = (TextHeaderAtom) r;
} else if (RecordTypes.TextBytesAtom.typeID == rt) { } else if (RecordTypes.TextBytesAtom.typeID == rt) {
tbytes = (TextBytesAtom)r; tbytes = (TextBytesAtom) r;
} else if (RecordTypes.TextCharsAtom.typeID == rt) { } else if (RecordTypes.TextCharsAtom.typeID == rt) {
tchars = (TextCharsAtom)r; tchars = (TextCharsAtom) r;
} else if (RecordTypes.TextRulerAtom.typeID == rt) { } else if (RecordTypes.TextRulerAtom.typeID == rt) {
ruler = (TextRulerAtom)r; ruler = (TextRulerAtom) r;
} else if (RecordTypes.MasterTextPropAtom.typeID == rt) { } else if (RecordTypes.MasterTextPropAtom.typeID == rt) {
indents = (MasterTextPropAtom)r; indents = (MasterTextPropAtom) r;
} }
// don't search for RecordTypes.StyleTextPropAtom.typeID here ... see findStyleAtomPresent below // don't search for RecordTypes.StyleTextPropAtom.typeID here ... see findStyleAtomPresent below
} }
@ -1124,25 +1102,25 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
int paraIdx = 0, runIdx = 0; int paraIdx = 0, runIdx = 0;
HSLFTextRun trun; HSLFTextRun trun;
for (int csIdx=0; csIdx<charStyles.size(); csIdx++) { for (int csIdx = 0; csIdx < charStyles.size(); csIdx++) {
TextPropCollection p = charStyles.get(csIdx); TextPropCollection p = charStyles.get(csIdx);
for (int ccRun = 0, ccStyle = p.getCharactersCovered(); ccRun < ccStyle; ) { for (int ccRun = 0, ccStyle = p.getCharactersCovered(); ccRun < ccStyle;) {
HSLFTextParagraph para = paragraphs.get(paraIdx); HSLFTextParagraph para = paragraphs.get(paraIdx);
List<HSLFTextRun> runs = para.getTextRuns(); List<HSLFTextRun> runs = para.getTextRuns();
trun = runs.get(runIdx); trun = runs.get(runIdx);
int len = trun.getLength(); int len = trun.getLength();
if (ccRun+len <= ccStyle) { if (ccRun + len <= ccStyle) {
ccRun += len; ccRun += len;
} else { } else {
String text = trun.getRawText(); String text = trun.getRawText();
trun.setText(text.substring(0,ccStyle-ccRun)); trun.setText(text.substring(0, ccStyle - ccRun));
HSLFTextRun nextRun = new HSLFTextRun(para); HSLFTextRun nextRun = new HSLFTextRun(para);
nextRun.setText(text.substring(ccStyle-ccRun)); nextRun.setText(text.substring(ccStyle - ccRun));
runs.add(runIdx+1, nextRun); runs.add(runIdx + 1, nextRun);
ccRun += ccStyle-ccRun; ccRun += ccStyle - ccRun;
} }
TextPropCollection pCopy = new TextPropCollection(0, TextPropType.character); TextPropCollection pCopy = new TextPropCollection(0, TextPropType.character);
@ -1151,7 +1129,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
len = trun.getLength(); len = trun.getLength();
if (paraIdx == paragraphs.size()-1 && runIdx == runs.size()-1) { 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 // special case, empty trailing text run
HSLFTextRun nextRun = new HSLFTextRun(para); HSLFTextRun nextRun = new HSLFTextRun(para);
nextRun.setText(""); nextRun.setText("");
@ -1204,7 +1182,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
len += trun.getLength(); len += trun.getLength();
} }
para.setIndentLevel(p.getIndentLevel()); para.setIndentLevel(p.getIndentLevel());
ccPara += len+1; ccPara += len + 1;
} }
} }
} }
@ -1237,6 +1215,6 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
} }
public EscherTextboxWrapper getTextboxWrapper() { public EscherTextboxWrapper getTextboxWrapper() {
return (EscherTextboxWrapper)_headerAtom.getParentRecord(); return (EscherTextboxWrapper) _headerAtom.getParentRecord();
} }
} }

View File

@ -17,7 +17,8 @@
package org.apache.poi.hslf.usermodel; 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; import java.awt.Color;
@ -132,29 +133,8 @@ public final class HSLFTextRun implements TextRun {
* it if required. * it if required.
*/ */
private void setCharFlagsTextPropVal(int index, boolean value) { 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();
} }
/** /**
@ -163,8 +143,7 @@ public final class HSLFTextRun implements TextRun {
* @param val The value to set for the TextProp * @param val The value to set for the TextProp
*/ */
public void setCharTextPropVal(String propName, int val) { public void setCharTextPropVal(String propName, int val) {
TextProp tp = fetchOrAddTextProp(characterStyle, propName); setPropVal(characterStyle, propName, val);
tp.setValue(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 * @return the percentage of the font size. If the value is positive, it is superscript, otherwise it is subscript
*/ */
public int getSuperscript() { public int getSuperscript() {
int val = getCharTextPropVal("superscript"); TextProp tp = getPropVal(characterStyle, "superscript", parentParagraph);
return val == -1 ? 0 : val; 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 * @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) { public void setSuperscript(int val) {
setCharTextPropVal("superscript", val); setPropVal(characterStyle, "superscript", val);
} }
/** /**
* Gets the font size * Gets the font size
*/ */
public double getFontSize() { public Double getFontSize() {
return getCharTextPropVal("font.size"); 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 * Gets the font index
*/ */
public int getFontIndex() { 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) { if (sheet == null || slideShow == null) {
return _fontFamily; return _fontFamily;
} }
int fontIdx = getCharTextPropVal("font.index"); TextProp tp = getPropVal(characterStyle, "font.index", parentParagraph);
if(fontIdx == -1) { return null; } if (tp == null) { return null; }
return slideShow.getFontCollection().getFontWithId(fontIdx); return slideShow.getFontCollection().getFontWithId(tp.getValue());
} }
/** /**
@ -339,7 +320,9 @@ public final class HSLFTextRun implements TextRun {
* @see java.awt.Color * @see java.awt.Color
*/ */
public Color getFontColor() { 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; int cidx = rgb >> 24;
if (rgb % 0x1000000 == 0){ if (rgb % 0x1000000 == 0){
@ -370,7 +353,7 @@ public final class HSLFTextRun implements TextRun {
} }
protected void setFlag(int index, boolean value) { 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); prop.setSubValue(value, index);
} }

View File

@ -55,12 +55,21 @@ public class DrawTextParagraph<T extends TextRun> implements Drawable {
double rightInset = insets.right; double rightInset = insets.right;
double penY = y; double penY = y;
double leftMargin = paragraph.getLeftMargin();
boolean firstLine = true; 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 //The vertical line spacing
double spacing = paragraph.getLineSpacing(); Double spacing = paragraph.getLineSpacing();
if (spacing == null) spacing = 100d;
for(DrawTextFragment line : lines){ for(DrawTextFragment line : lines){
double penX = x + leftMargin; double penX = x + leftMargin;
@ -77,7 +86,7 @@ public class DrawTextParagraph<T extends TextRun> implements Drawable {
bullet.setPosition(penX + indent, penY); bullet.setPosition(penX + indent, penY);
} else if(indent > 0){ } else if(indent > 0){
// a positive value means the "First Line" indentation: // 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); bullet.setPosition(penX, penY);
penX += indent; penX += indent;
} else { } else {
@ -96,7 +105,9 @@ public class DrawTextParagraph<T extends TextRun> implements Drawable {
Rectangle2D anchor = DrawShape.getAnchor(graphics, paragraph.getParentShape()); Rectangle2D anchor = DrawShape.getAnchor(graphics, paragraph.getParentShape());
switch (paragraph.getTextAlign()) { TextAlign ta = paragraph.getTextAlign();
if (ta == null) ta = TextAlign.LEFT;
switch (ta) {
case CENTER: case CENTER:
penX += (anchor.getWidth() - leftMargin - line.getWidth() - leftInset - rightInset) / 2; penX += (anchor.getWidth() - leftMargin - line.getWidth() - leftInset - rightInset) / 2;
break; break;
@ -219,9 +230,10 @@ public class DrawTextParagraph<T extends TextRun> implements Drawable {
if (buColor == null) buColor = (Color)firstLineAttr.getAttribute(TextAttribute.FOREGROUND); if (buColor == null) buColor = (Color)firstLineAttr.getAttribute(TextAttribute.FOREGROUND);
float fontSize = (Float)firstLineAttr.getAttribute(TextAttribute.SIZE); float fontSize = (Float)firstLineAttr.getAttribute(TextAttribute.SIZE);
float buSz = (float)bulletStyle.getBulletFontSize(); Double buSz = bulletStyle.getBulletFontSize();
if(buSz > 0) fontSize *= buSz* 0.01; if (buSz == null) buSz = 100d;
else fontSize = -buSz; if (buSz > 0) fontSize *= buSz* 0.01;
else fontSize = (float)-buSz;
AttributedString str = new AttributedString(buCharacter); AttributedString str = new AttributedString(buCharacter);
@ -237,10 +249,13 @@ public class DrawTextParagraph<T extends TextRun> implements Drawable {
protected String getRenderableText(TextRun tr) { protected String getRenderableText(TextRun tr) {
StringBuilder buf = new StringBuilder(); StringBuilder buf = new StringBuilder();
TextCap cap = tr.getTextCap(); TextCap cap = tr.getTextCap();
String tabs = null;
for (char c : tr.getRawText().toCharArray()) { for (char c : tr.getRawText().toCharArray()) {
if(c == '\t') { if(c == '\t') {
// TODO: finish support for tabs if (tabs == null) {
buf.append(" "); tabs = tab2space(tr);
}
buf.append(tabs);
continue; continue;
} }
@ -256,6 +271,34 @@ public class DrawTextParagraph<T extends TextRun> implements Drawable {
return buf.toString(); 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 * Returns wrapping width to break lines in this paragraph
* *
@ -271,8 +314,14 @@ public class DrawTextParagraph<T extends TextRun> implements Drawable {
Rectangle2D anchor = DrawShape.getAnchor(graphics, paragraph.getParentShape()); Rectangle2D anchor = DrawShape.getAnchor(graphics, paragraph.getParentShape());
double leftMargin = paragraph.getLeftMargin(); Double leftMargin = paragraph.getLeftMargin();
double indent = paragraph.getIndent(); Double indent = paragraph.getIndent();
if (leftMargin == null) {
leftMargin = (indent != null) ? -indent : 0;
}
if (indent == null) {
indent = (leftMargin != null) ? -leftMargin : 0;
}
double width; double width;
TextShape<? extends TextParagraph<T>> ts = paragraph.getParentShape(); TextShape<? extends TextParagraph<T>> ts = paragraph.getParentShape();
@ -323,7 +372,9 @@ public class DrawTextParagraph<T extends TextRun> implements Drawable {
text.append(runText); text.append(runText);
int endIndex = text.length(); 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 // user can pass an custom object to convert fonts
String fontFamily = run.getFontFamily(); String fontFamily = run.getFontFamily();
@ -335,10 +386,14 @@ public class DrawTextParagraph<T extends TextRun> implements Drawable {
if(fontHandler != null) { if(fontHandler != null) {
fontFamily = fontHandler.getRendererableFont(fontFamily, run.getPitchAndFamily()); fontFamily = fontHandler.getRendererableFont(fontFamily, run.getPitchAndFamily());
} }
if (fontFamily == null) {
fontFamily = paragraph.getDefaultFontFamily();
}
attList.add(new AttributedStringData(TextAttribute.FAMILY, fontFamily, beginIndex, endIndex)); attList.add(new AttributedStringData(TextAttribute.FAMILY, fontFamily, beginIndex, endIndex));
float fontSz = (float)run.getFontSize(); Double fontSz = run.getFontSize();
attList.add(new AttributedStringData(TextAttribute.SIZE, fontSz, beginIndex, endIndex)); if (fontSz == null) fontSz = paragraph.getDefaultFontSize();
attList.add(new AttributedStringData(TextAttribute.SIZE, fontSz.floatValue(), beginIndex, endIndex));
if(run.isBold()) { if(run.isBold()) {
attList.add(new AttributedStringData(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD, beginIndex, endIndex)); attList.add(new AttributedStringData(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD, beginIndex, endIndex));
@ -364,9 +419,9 @@ public class DrawTextParagraph<T extends TextRun> implements Drawable {
// ensure that the paragraph contains at least one character // ensure that the paragraph contains at least one character
// We need this trick to correctly measure text // We need this trick to correctly measure text
if (text.length() == 0) { if (text.length() == 0) {
float fontSz = (float)paragraph.getDefaultFontSize(); Double fontSz = paragraph.getDefaultFontSize();
text.append(" "); 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()); AttributedString string = new AttributedString(text.toString());

View File

@ -95,7 +95,8 @@ public class DrawTextShape<T extends TextShape<? extends TextParagraph<? extends
if (!isFirstLine) { if (!isFirstLine) {
// the amount of vertical white space before the paragraph // the amount of vertical white space before the paragraph
double spaceBefore = p.getSpaceBefore(); Double spaceBefore = p.getSpaceBefore();
if (spaceBefore == null) spaceBefore = 0d;
if(spaceBefore > 0) { if(spaceBefore > 0) {
// positive value means percentage spacing of the height of the first line, e.g. // 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 // the higher the first line, the bigger the space before the paragraph
@ -112,7 +113,8 @@ public class DrawTextShape<T extends TextShape<? extends TextParagraph<? extends
y += dp.getY(); y += dp.getY();
if (paragraphs.hasNext()) { if (paragraphs.hasNext()) {
double spaceAfter = p.getSpaceAfter(); Double spaceAfter = p.getSpaceAfter();
if (spaceAfter == null) spaceAfter = 0d;
if(spaceAfter > 0) { if(spaceAfter > 0) {
// positive value means percentage spacing of the height of the last line, e.g. // 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 // the higher the last line, the bigger the space after the paragraph

View File

@ -104,7 +104,15 @@ public interface TextParagraph<T extends TextRun> extends Iterable<T> {
public interface BulletStyle { public interface BulletStyle {
String getBulletCharacter(); String getBulletCharacter();
String getBulletFont(); 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(); Color getBulletFontColor();
} }
@ -116,9 +124,30 @@ public interface TextParagraph<T extends TextRun> extends Iterable<T> {
* If spaceBefore < 0, the absolute value in points * If spaceBefore < 0, the absolute value in points
* </p> * </p>
* *
* @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:
* <p>
* 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
* </p>
* Examples:
* <pre><code>
* // 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);
* </code></pre>
*
* @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 * The amount of vertical white space after the paragraph
@ -128,40 +157,74 @@ public interface TextParagraph<T extends TextRun> extends Iterable<T> {
* If spaceBefore < 0, the absolute value of linespacing is the spacing in points * If spaceBefore < 0, the absolute value of linespacing is the spacing in points
* </p> * </p>
* *
* @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:
* <p>
* 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
* </p>
* Examples:
* <pre><code>
* // 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);
* </code></pre>
*
* @param spaceAfter the vertical white space after the paragraph, null to unset
*/ */
double getLeftMargin(); public void setSpaceAfter(Double spaceAfter);
/** /**
* @param leftMargin the left margin (in points) * @return the left margin (in points) of the paragraph or null, if unset
*/ */
void setLeftMargin(double leftMargin); Double getLeftMargin();
/**
* 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);
/** /**
* @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 * @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. * @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 * @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. * Returns the vertical line spacing that is to be used within a paragraph.
@ -171,9 +234,9 @@ public interface TextParagraph<T extends TextRun> extends Iterable<T> {
* If linespacing < 0, the absolute value of linespacing is the spacing in points * If linespacing < 0, the absolute value of linespacing is the spacing in points
* </p> * </p>
* *
* @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. * This element specifies the vertical line spacing that is to be used within a paragraph.
@ -196,14 +259,14 @@ public interface TextParagraph<T extends TextRun> extends Iterable<T> {
* *
* @param linespacing the vertical line spacing * @param linespacing the vertical line spacing
*/ */
void setLineSpacing(double lineSpacing); void setLineSpacing(Double lineSpacing);
String getDefaultFontFamily(); 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. * Returns the alignment that is applied to the paragraph.
@ -217,8 +280,10 @@ public interface TextParagraph<T extends TextRun> extends Iterable<T> {
/** /**
* Returns the font alignment that is applied to the paragraph. * Returns the font alignment that is applied to the paragraph.
* *
* If this attribute is omitted, then a value of auto (~ left) is implied. * If this attribute is omitted, then null is return,
* @return ??? alignment that is applied to the paragraph * user code can imply the a value of {@link FontAlign#AUTO}
*
* @return alignment that is applied to the paragraph
*/ */
FontAlign getFontAlign(); FontAlign getFontAlign();
@ -227,5 +292,11 @@ public interface TextParagraph<T extends TextRun> extends Iterable<T> {
*/ */
BulletStyle getBulletStyle(); BulletStyle getBulletStyle();
/**
* @return the default size for a tab character within this paragraph in points, null if unset
*/
Double getDefaultTabSize();
TextShape<? extends TextParagraph<T>> getParentShape(); TextShape<? extends TextParagraph<T>> getParentShape();
} }

View File

@ -21,8 +21,6 @@ import java.awt.Color;
/** /**
* Some text. * Some text.
*
* TODO - decide on how we do rich text stuff
*/ */
public interface TextRun { public interface TextRun {
enum TextCap { enum TextCap {
@ -31,13 +29,13 @@ public interface TextRun {
ALL ALL
} }
public String getRawText(); String getRawText();
public void setText(String text); void setText(String text);
TextCap getTextCap(); TextCap getTextCap();
Color getFontColor(); Color getFontColor();
double getFontSize(); Double getFontSize();
String getFontFamily(); String getFontFamily();
boolean isBold(); boolean isBold();

View File

@ -22,8 +22,7 @@ import static org.junit.Assert.*;
import java.awt.*; import java.awt.*;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.*; import java.io.*;
import java.util.HashMap; import java.util.*;
import java.util.Map;
import javax.imageio.ImageIO; 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.sl.usermodel.SlideShow;
import org.apache.poi.util.JvmBugs; import org.apache.poi.util.JvmBugs;
import org.apache.poi.xslf.usermodel.XMLSlideShow; import org.apache.poi.xslf.usermodel.XMLSlideShow;
import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
/** /**
@ -142,29 +140,43 @@ public final class TestPicture {
@Test @Test
// @Ignore("Just for visual validation - antialiasing is different on various systems") // @Ignore("Just for visual validation - antialiasing is different on various systems")
public void bug54541() throws Exception { public void bug54541() throws Exception {
String file = new String[]{ String files[] = {
"54542_cropped_bitmap.pptx", // "sample_pptx_grouping_issues.pptx",
"54541_cropped_bitmap.ppt", // "54542_cropped_bitmap.pptx",
"54541_cropped_bitmap2.ppt", // "54541_cropped_bitmap.ppt",
"sample_pptx_grouping_issues.pptx" // "54541_cropped_bitmap2.ppt",
}[3]; // "alterman_security.ppt",
"alterman_security2.pptx",
};
BitSet pages = new BitSet();
pages.set(2);
for (String file : files) {
InputStream is = _slTests.openResourceAsStream(file); InputStream is = _slTests.openResourceAsStream(file);
SlideShow ss = file.endsWith("pptx") ? new XMLSlideShow(is) : new HSLFSlideShow(is); SlideShow ss = file.endsWith("pptx") ? new XMLSlideShow(is) : new HSLFSlideShow(is);
is.close(); is.close();
boolean debugOut = false; boolean debugOut = false;
Dimension pg = ss.getPageSize(); Dimension pg = ss.getPageSize();
int i=1; for (Slide<?,?,?> slide : ss.getSlides()) {
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) { if (debugOut) {
DummyGraphics2d graphics = new DummyGraphics2d(); DummyGraphics2d graphics = new DummyGraphics2d();
slide.draw(graphics); slide.draw(graphics);
} else { } else {
BufferedImage img = new BufferedImage(pg.width, pg.height, BufferedImage.TYPE_INT_RGB); BufferedImage img = new BufferedImage(pg.width, pg.height, BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics = img.createGraphics(); Graphics2D graphics = img.createGraphics();
fixFonts(graphics); fixFonts(graphics);
slide.draw(graphics); slide.draw(graphics);
ImageIO.write(img, "PNG", new File("test"+(i++)+"hslf.png")); 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"));
}
} }
} }
} }

View File

@ -470,7 +470,7 @@ public final class TestRichTextRun {
assertEquals(expected, HSLFTextParagraph.getRawText(txt.get(1))); assertEquals(expected, HSLFTextParagraph.getRawText(txt.get(1)));
assertEquals(4, txt.get(1).size()); assertEquals(4, txt.get(1).size());
rt = txt.get(1).get(0); rt = txt.get(1).get(0);
assertEquals('\u2022', rt.getBulletChar()); assertEquals('\u2022', (char)rt.getBulletChar());
assertTrue(rt.isBullet()); assertTrue(rt.isBullet());
@ -486,7 +486,7 @@ public final class TestRichTextRun {
assertEquals(4, txt.get(0).size()); assertEquals(4, txt.get(0).size());
rt = txt.get(0).get(0); rt = txt.get(0).get(0);
assertTrue(rt.isBullet()); assertTrue(rt.isBullet());
assertEquals('\u2022', rt.getBulletChar()); assertEquals('\u2022', (char)rt.getBulletChar());
expected = expected =
"I\u2019m a text box with user-defined\r" + "I\u2019m a text box with user-defined\r" +
@ -495,7 +495,7 @@ public final class TestRichTextRun {
assertEquals(2, txt.get(1).size()); assertEquals(2, txt.get(1).size());
rt = txt.get(1).get(0); rt = txt.get(1).get(0);
assertTrue(rt.isBullet()); assertTrue(rt.isBullet());
assertEquals('\u263A', rt.getBulletChar()); assertEquals('\u263A', (char)rt.getBulletChar());
} }
@Test @Test
@ -513,8 +513,8 @@ public final class TestRichTextRun {
HSLFTextRun tr = rt.getTextRuns().get(0); HSLFTextRun tr = rt.getTextRuns().get(0);
tr.setFontSize(42); tr.setFontSize(42);
rt.setBullet(true); rt.setBullet(true);
rt.setLeftMargin(50); rt.setLeftMargin(50d);
rt.setIndent(0); rt.setIndent(0d);
rt.setBulletChar('\u263A'); rt.setBulletChar('\u263A');
slide.addShape(shape); slide.addShape(shape);
@ -522,7 +522,7 @@ public final class TestRichTextRun {
assertEquals(true, rt.isBullet()); assertEquals(true, rt.isBullet());
assertEquals(50.0, rt.getLeftMargin(), 0); assertEquals(50.0, rt.getLeftMargin(), 0);
assertEquals(0, rt.getIndent(), 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)); shape.setAnchor(new java.awt.Rectangle(50, 50, 500, 300));
slide.addShape(shape); slide.addShape(shape);
@ -541,7 +541,7 @@ public final class TestRichTextRun {
assertEquals(true, rt.isBullet()); assertEquals(true, rt.isBullet());
assertEquals(50.0, rt.getLeftMargin(), 0); assertEquals(50.0, rt.getLeftMargin(), 0);
assertEquals(0, rt.getIndent(), 0); assertEquals(0, rt.getIndent(), 0);
assertEquals('\u263A', rt.getBulletChar()); assertEquals('\u263A', (char)rt.getBulletChar());
} }
@Test @Test

View File

@ -556,7 +556,7 @@ public final class TestTextRun {
int i=0; int i=0;
for (List<HSLFTextParagraph> textParas : slide.getTextParagraphs()) { for (List<HSLFTextParagraph> textParas : slide.getTextParagraphs()) {
assertEquals("Arial", textParas.get(0).getTextRuns().get(0).getFontFamily()); 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());
} }
} }

Binary file not shown.