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);
rt.getTextRuns().get(0).setFontSize(42);
rt.setBullet(true);
rt.setIndent(0); //bullet offset
rt.setLeftMargin(50); //text offset (should be greater than bullet offset)
rt.setIndent(0d); //bullet offset
rt.setLeftMargin(50d); //text offset (should be greater than bullet offset)
rt.setBulletChar('\u263A'); //bullet character
shape.setText(
"January\r" +

View File

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

View File

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

View File

@ -16,6 +16,8 @@
==================================================================== */
package org.apache.poi.util;
import org.apache.poi.hslf.usermodel.HSLFShape;
/**
* @author Yegor Kozlov
*/
@ -23,6 +25,22 @@ public class Units {
public static final int EMU_PER_PIXEL = 9525;
public static final int EMU_PER_POINT = 12700;
/**
* Master DPI (576 pixels per inch).
* Used by the reference coordinate system in PowerPoint (HSLF)
*/
public static final int MASTER_DPI = 576;
/**
* Pixels DPI (96 pixels per inch)
*/
public static final int PIXEL_DPI = 96;
/**
* Points DPI (72 pixels per inch)
*/
public static final int POINT_DPI = 72;
/**
* Converts points to EMUs
* @param points points
@ -70,4 +88,17 @@ public class Units {
int fixedPoint = (i << 16) | (f & 0xFFFF);
return fixedPoint;
}
public static double masterToPoints(int masterDPI) {
double points = masterDPI;
points *= HSLFShape.POINT_DPI;
points /= HSLFShape.MASTER_DPI;
return points;
}
public static int pointsToMaster(double points) {
points *= HSLFShape.MASTER_DPI;
points /= HSLFShape.POINT_DPI;
return (int)points;
}
}

View File

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

View File

@ -17,26 +17,11 @@
package org.apache.poi.xslf.usermodel;
import java.awt.Color;
import java.awt.font.FontRenderContext;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
import java.text.AttributedString;
import org.apache.poi.sl.usermodel.TextRun;
import org.apache.poi.util.Beta;
import org.apache.poi.xslf.model.CharacterPropertyFetcher;
import org.openxmlformats.schemas.drawingml.x2006.main.CTRegularTextRun;
import org.openxmlformats.schemas.drawingml.x2006.main.CTSRgbColor;
import org.openxmlformats.schemas.drawingml.x2006.main.CTSchemeColor;
import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeStyle;
import org.openxmlformats.schemas.drawingml.x2006.main.CTSolidColorFillProperties;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextCharacterProperties;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextFont;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextNormalAutofit;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraphProperties;
import org.openxmlformats.schemas.drawingml.x2006.main.STSchemeColorVal;
import org.openxmlformats.schemas.drawingml.x2006.main.STTextStrikeType;
import org.openxmlformats.schemas.drawingml.x2006.main.STTextUnderlineType;
import org.openxmlformats.schemas.drawingml.x2006.main.*;
import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder;
/**
@ -89,28 +74,6 @@ public class XSLFTextRun implements TextRun {
return buf.toString();
}
/**
* Replace a tab with the effective number of white spaces.
*/
private String tab2space(){
AttributedString string = new AttributedString(" ");
// user can pass an object to convert fonts via a rendering hint
string.addAttribute(TextAttribute.FAMILY, getFontFamily());
string.addAttribute(TextAttribute.SIZE, (float)getFontSize());
TextLayout l = new TextLayout(string.getIterator(), new FontRenderContext(null, true, true));
double wspace = l.getAdvance();
double tabSz = _p.getDefaultTabSize();
int numSpaces = (int)Math.ceil(tabSz / wspace);
StringBuffer buf = new StringBuffer();
for(int i = 0; i < numSpaces; i++) {
buf.append(' ');
}
return buf.toString();
}
public void setText(String text){
_r.setT(text);
}
@ -175,9 +138,10 @@ public class XSLFTextRun implements TextRun {
}
/**
* @return font size in points or -1 if font size is not set.
* @return font size in points or null if font size is not set.
*/
public double getFontSize(){
@Override
public Double getFontSize(){
double scale = 1;
CTTextNormalAutofit afit = getParentParagraph().getParentShape().getTextBodyPr().getNormAutofit();
if(afit != null) scale = (double)afit.getFontScale() / 100000;
@ -192,7 +156,7 @@ public class XSLFTextRun implements TextRun {
}
};
fetchCharacterProperty(fetcher);
return fetcher.getValue() == null ? -1 : fetcher.getValue()*scale;
return fetcher.getValue() == null ? null : fetcher.getValue()*scale;
}
/**
@ -514,7 +478,7 @@ public class XSLFTextRun implements TextRun {
return new XSLFHyperlink(_r.getRPr().getHlinkClick(), this);
}
private boolean fetchCharacterProperty(CharacterPropertyFetcher fetcher){
private boolean fetchCharacterProperty(CharacterPropertyFetcher<?> fetcher){
boolean ok = false;
if(_r.isSetRPr()) ok = fetcher.fetch(getRPr());

View File

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

View File

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

View File

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

View File

@ -31,47 +31,10 @@ import org.apache.poi.util.LittleEndian;
* properties, and the indent level if required.
*/
public class TextPropCollection {
/*
private static TextProp paragraphSpecialPropTypes[] = {
new ParagraphFlagsTextProp(),
new TextProp(2, 0x80, "bullet.char"),
new TextProp(2, 0x10, "bullet.font"),
new TextProp(2, 0x40, "bullet.size"),
new TextProp(4, 0x20, "bullet.color"),
new TextProp(2, 0xD00, "alignment"),
new TextProp(2, 0x1000, "linespacing"),
new TextProp(2, 0x2000, "spacebefore"),
new TextProp(2, 0x4000, "spaceafter"),
new TextProp(2, 0x8000, "text.offset"),
new TextProp(2, 0x10000, "bullet.offset"),
new TextProp(2, 0x20000, "defaulttab"),
new TextProp(2, 0x40000, "para_unknown_2"),
new TextProp(2, 0x80000, "para_unknown_3"),
new TextProp(2, 0x100000, "para_unknown_4"),
new TextProp(2, 0x200000, "para_unknown_5")
};
private static TextProp characterSpecialPropTypes[] = {
new CharFlagsTextProp(),
new TextProp(2, 0x10000, "font.index"),
new TextProp(2, 0x20000, "char_unknown_1"),
new TextProp(4, 0x40000, "char_unknown_2"),
new TextProp(2, 0x80000, "font.size"),
new TextProp(2, 0x100000, "char_unknown_3"),
new TextProp(4, 0x200000, "font.color"),
new TextProp(2, 0x800000, "char_unknown_4")
};
*/
/** All the different kinds of paragraph properties we might handle */
public static final TextProp[] paragraphTextPropTypes = {
// TextProp order is according to 2.9.20 TextPFException,
// bitmask order can be different
// new TextProp(0, 0x1, "hasBullet"),
// new TextProp(0, 0x2, "hasBulletFont"),
// new TextProp(0, 0x4, "hasBulletColor"),
// new TextProp(0, 0x8, "hasBulletSize"),
new ParagraphFlagsTextProp(),
new TextProp(2, 0x80, "bullet.char"),
new TextProp(2, 0x10, "bullet.font"),
@ -95,24 +58,9 @@ public class TextPropCollection {
new TextProp(0, 0x2000000, "hasBulletScheme"), // TODO: check size
// 0xFC000000 MUST be zero and MUST be ignored
};
/** All the different kinds of character properties we might handle */
public static final TextProp[] characterTextPropTypes = new TextProp[] {
// new TextProp(0, 0x1, "bold"),
// new TextProp(0, 0x2, "italic"),
// new TextProp(0, 0x4, "underline"),
// new TextProp(0, 0x8, "unused1"),
// new TextProp(0, 0x10, "shadow"),
// new TextProp(0, 0x20, "fehint"),
// new TextProp(0, 0x40, "unused2"),
// new TextProp(0, 0x80, "kumi"),
// new TextProp(0, 0x100, "strikethrough"),
// new TextProp(0, 0x200, "emboss"),
// new TextProp(0, 0x400, "nibble1"),
// new TextProp(0, 0x800, "nibble2"),
// new TextProp(0, 0x1000, "nibble3"),
// new TextProp(0, 0x2000, "nibble4"),
// new TextProp(0, 0x4000, "unused4"),
// new TextProp(0, 0x8000, "unused5"),
new TextProp(0, 0x100000, "pp10ext"),
new TextProp(0, 0x1000000, "newAsian.font.index"), // A bit that specifies whether the newEAFontRef field of the TextCFException10 structure that contains this CFMasks exists.
new TextProp(0, 0x2000000, "cs.font.index"), // A bit that specifies whether the csFontRef field of the TextCFException10 structure that contains this CFMasks exists.
@ -166,6 +114,19 @@ public class TextPropCollection {
}
return null;
}
public TextProp removeByName(String name) {
Iterator<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 */
public TextProp addWithName(String name) {
@ -192,6 +153,10 @@ public class TextPropCollection {
return textProp;
}
public TextPropType getTextPropType() {
return textPropType;
}
private TextProp[] getPotentialProperties() {
return (textPropType == TextPropType.paragraph) ? paragraphTextPropTypes : characterTextPropTypes;
}

View File

@ -17,6 +17,7 @@
package org.apache.poi.hslf.record;
import org.apache.poi.hslf.model.PPFont;
import org.apache.poi.util.POILogger;
import java.io.*;
@ -75,9 +76,9 @@ public final class FontCollection extends RecordContainer {
*/
public int addFont(String name) {
int idx = getFontIndex(name);
if(idx != -1) return idx;
if (idx != -1) return idx;
return addFont(name, 0, 0, 4, 34);
return addFont(name, 0, 0, 4, PPFont.FF_SWISS | PPFont.VARIABLE_PITCH);
}
public int addFont(String name, int charset, int flags, int type, int pitch) {

View File

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

View File

@ -17,7 +17,8 @@
package org.apache.poi.hslf.usermodel;
import static org.apache.poi.hslf.usermodel.HSLFTextParagraph.fetchOrAddTextProp;
import static org.apache.poi.hslf.usermodel.HSLFTextParagraph.setPropVal;
import static org.apache.poi.hslf.usermodel.HSLFTextParagraph.getPropVal;
import java.awt.Color;
@ -132,39 +133,17 @@ public final class HSLFTextRun implements TextRun {
* it if required.
*/
private void setCharFlagsTextPropVal(int index, boolean value) {
if(getFlag(index) != value) setFlag(index, value);
// TODO: check if paragraph/chars can be handled the same ...
if (getFlag(index) != value) setFlag(index, value);
}
/**
* Fetch the value of the given Character related TextProp.
* Returns -1 if that TextProp isn't present.
* If the TextProp isn't present, the value from the appropriate
* Master Sheet will apply.
*/
private int getCharTextPropVal(String propName) {
TextProp prop = null;
if (characterStyle != null){
prop = characterStyle.findByName(propName);
}
if (prop == null){
HSLFSheet sheet = parentParagraph.getSheet();
int txtype = parentParagraph.getRunType();
HSLFMasterSheet master = sheet.getMasterSheet();
if (master != null)
prop = master.getStyleAttribute(txtype, parentParagraph.getIndentLevel(), propName, true);
}
return prop == null ? -1 : prop.getValue();
}
/**
* Sets the value of the given Paragraph TextProp, add if required
* @param propName The name of the Paragraph TextProp
* @param val The value to set for the TextProp
*/
public void setCharTextPropVal(String propName, int val) {
TextProp tp = fetchOrAddTextProp(characterStyle, propName);
tp.setValue(val);
setPropVal(characterStyle, propName, val);
}
@ -260,8 +239,8 @@ public final class HSLFTextRun implements TextRun {
* @return the percentage of the font size. If the value is positive, it is superscript, otherwise it is subscript
*/
public int getSuperscript() {
int val = getCharTextPropVal("superscript");
return val == -1 ? 0 : val;
TextProp tp = getPropVal(characterStyle, "superscript", parentParagraph);
return tp == null ? 0 : tp.getValue();
}
/**
@ -270,14 +249,15 @@ public final class HSLFTextRun implements TextRun {
* @param val the percentage of the font size. If the value is positive, it is superscript, otherwise it is subscript
*/
public void setSuperscript(int val) {
setCharTextPropVal("superscript", val);
setPropVal(characterStyle, "superscript", val);
}
/**
* Gets the font size
*/
public double getFontSize() {
return getCharTextPropVal("font.size");
public Double getFontSize() {
TextProp tp = getPropVal(characterStyle, "font.size", parentParagraph);
return tp == null ? null : (double)tp.getValue();
}
@ -292,7 +272,8 @@ public final class HSLFTextRun implements TextRun {
* Gets the font index
*/
public int getFontIndex() {
return getCharTextPropVal("font.index");
TextProp tp = getPropVal(characterStyle, "font.index", parentParagraph);
return tp == null ? -1 : tp.getValue();
}
/**
@ -329,9 +310,9 @@ public final class HSLFTextRun implements TextRun {
if (sheet == null || slideShow == null) {
return _fontFamily;
}
int fontIdx = getCharTextPropVal("font.index");
if(fontIdx == -1) { return null; }
return slideShow.getFontCollection().getFontWithId(fontIdx);
TextProp tp = getPropVal(characterStyle, "font.index", parentParagraph);
if (tp == null) { return null; }
return slideShow.getFontCollection().getFontWithId(tp.getValue());
}
/**
@ -339,7 +320,9 @@ public final class HSLFTextRun implements TextRun {
* @see java.awt.Color
*/
public Color getFontColor() {
int rgb = getCharTextPropVal("font.color");
TextProp tp = getPropVal(characterStyle, "font.color", parentParagraph);
if (tp == null) return null;
int rgb = tp.getValue();
int cidx = rgb >> 24;
if (rgb % 0x1000000 == 0){
@ -370,7 +353,7 @@ public final class HSLFTextRun implements TextRun {
}
protected void setFlag(int index, boolean value) {
BitMaskTextProp prop = (BitMaskTextProp) fetchOrAddTextProp(characterStyle, CharFlagsTextProp.NAME);
BitMaskTextProp prop = (BitMaskTextProp)characterStyle.addWithName(CharFlagsTextProp.NAME);
prop.setSubValue(value, index);
}

View File

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

View File

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

View File

@ -104,10 +104,18 @@ public interface TextParagraph<T extends TextRun> extends Iterable<T> {
public interface BulletStyle {
String getBulletCharacter();
String getBulletFont();
double getBulletFontSize();
/**
* The bullet point font size
* If bulletFontSize >= 0, then space is a percentage of normal line height.
* If bulletFontSize < 0, the absolute value in points
*
* @return the bullet point font size
*/
Double getBulletFontSize();
Color getBulletFontColor();
}
/**
* The amount of vertical white space before the paragraph
* This may be specified in two different ways, percentage spacing and font point spacing:
@ -116,9 +124,30 @@ public interface TextParagraph<T extends TextRun> extends Iterable<T> {
* If spaceBefore < 0, the absolute value in points
* </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
@ -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
* </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);
/**
* @return the left margin (in points) of the paragraph or null, if unset
*/
Double getLeftMargin();
/**
* @param leftMargin the left margin (in points)
* Specifies the left margin of the paragraph. This is specified in addition to the text body
* inset and applies only to this text paragraph. That is the text body Inset and the LeftMargin
* attributes are additive with respect to the text position.
*
* @param leftMargin the left margin (in points) or null to unset
*/
void setLeftMargin(double leftMargin);
void setLeftMargin(Double leftMargin);
/**
* @return the right margin (in points) of the paragraph
* Specifies the right margin of the paragraph. This is specified in addition to the text body
* inset and applies only to this text paragraph. That is the text body Inset and the RightMargin
* attributes are additive with respect to the text position.
*
* The right margin is not support and therefore ignored by the HSLF implementation.
*
* @return the right margin (in points) of the paragraph or null, if unset
*/
double getRightMargin();
Double getRightMargin();
/**
* @param rightMargin the right margin (in points) of the paragraph
*/
void setRightMargin(double rightMargin);
void setRightMargin(Double rightMargin);
/**
* @return the indent (in points) applied to the first line of text in the paragraph.
* or null, if unset
*/
double getIndent();
Double getIndent();
/**
* Specifies the indent size that will be applied to the first line of text in the paragraph.
*
* @param indent the indent (in points) applied to the first line of text in the paragraph
*/
void setIndent(double indent);
void setIndent(Double indent);
/**
* Returns the vertical line spacing that is to be used within a paragraph.
@ -171,9 +234,9 @@ public interface TextParagraph<T extends TextRun> extends Iterable<T> {
* If linespacing < 0, the absolute value of linespacing is the spacing in points
* </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.
@ -196,14 +259,14 @@ public interface TextParagraph<T extends TextRun> extends Iterable<T> {
*
* @param linespacing the vertical line spacing
*/
void setLineSpacing(double lineSpacing);
void setLineSpacing(Double lineSpacing);
String getDefaultFontFamily();
/**
* @return the default font size, in case its not set in the textrun
* @return the default font size, in case its not set in the textrun or null, if unset
*/
double getDefaultFontSize();
Double getDefaultFontSize();
/**
* Returns the alignment that is applied to the paragraph.
@ -217,8 +280,10 @@ public interface TextParagraph<T extends TextRun> extends Iterable<T> {
/**
* Returns the font alignment that is applied to the paragraph.
*
* If this attribute is omitted, then a value of auto (~ left) is implied.
* @return ??? alignment that is applied to the paragraph
* If this attribute is omitted, then null is return,
* user code can imply the a value of {@link FontAlign#AUTO}
*
* @return alignment that is applied to the paragraph
*/
FontAlign getFontAlign();
@ -227,5 +292,11 @@ public interface TextParagraph<T extends TextRun> extends Iterable<T> {
*/
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();
}

View File

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

View File

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

View File

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

View File

@ -556,7 +556,7 @@ public final class TestTextRun {
int i=0;
for (List<HSLFTextParagraph> textParas : slide.getTextParagraphs()) {
assertEquals("Arial", textParas.get(0).getTextRuns().get(0).getFontFamily());
assertEquals(sizes[i++], (int)textParas.get(0).getTextRuns().get(0).getFontSize());
assertEquals(sizes[i++], textParas.get(0).getTextRuns().get(0).getFontSize().intValue());
}
}

Binary file not shown.