关于POI 操作word的基础知识在这个博客(http://elim.iteye.com/blog/2049110)中有非常清晰的解释,在这里我就不多解释了
本文研究的内容就是
XWPFParagraph:代表一个段落
XWPFRun:代表具有相同属性的一段文本
大家都知道在设计模式中有个构造器模式,用于那些拥有很多属性但是有些属性可选设置的对象的生成。笔者觉得段落和文本的构建能很好运用此种模式。
首先是段落构建器
代码语言:javascript复制//段落构建器
public class XWPFParagraphBuilder {
//常量,在文档中定义长度和高度的单位
private static final int PER_LINE = 100;
//每个字符的单位长度
private static final int PER_CHART = 100;
//1厘米≈567
private static final int PER_CM = 567;
//每一磅的单位长度
private static final int PER_POUND = 20;
//行距单位长度
private static final int ONE_LINE = 240;
private XWPFParagraph paragraph = null;
private CTPPr pPr = null;
//保存通用段落属性引用,方便复用
private Map<String, CTPPr> savedPPr = null;
//设定间距的对象
private CTSpacing pSpacing = null;
//设定缩进的对象
private CTInd pInd = null;
public XWPFParagraphBuilder init(XWPFDocument document) {
return init(document.createParagraph());
}
public XWPFParagraphBuilder init(XWPFParagraph paragraph) {
if (paragraph == null) {
throw new IllegalArgumentException("the paragraph should not be null");
}
this.paragraph = paragraph;
pPr = getPrOfParagraph(paragraph);
return this;
}
//设置段落对齐方式
public XWPFParagraphBuilder align(ParagraphAlignment pAlign, TextAlignment vAlign) {
ensureInit();
if (pAlign != null) {
paragraph.setAlignment(pAlign);
}
if (vAlign != null) {
paragraph.setVerticalAlignment(vAlign);
}
return this;
}
//初始化段落间距属性,在设置各段落间距前调用
public XWPFParagraphBuilder initSpacing() {
ensureInit();
pSpacing = pPr.getSpacing() != null ? pPr.getSpacing() : pPr.addNewSpacing();
return this;
}
//设置段前和段后间距,以磅为单位
public XWPFParagraphBuilder spaceInPound(double before, double after) {
ensureInit();
if (pSpacing == null) {
initSpacing();
}
pSpacing.setBefore(BigInteger.valueOf((long) (before * PER_POUND)));
pSpacing.setAfter(BigInteger.valueOf((long) (after * PER_POUND)));
return this;
}
//设置段前和段后间距,以行为单位
public XWPFParagraphBuilder spaceInLine(double beforeLines, double afterLines) {
ensureInit();
if (pSpacing == null) {
initSpacing();
}
pSpacing.setBeforeLines(BigInteger.valueOf((long) (beforeLines * PER_LINE)));
pSpacing.setAfterLines(BigInteger.valueOf((long) (afterLines * PER_LINE)));
return this;
}
//设置段落行距
public XWPFParagraphBuilder lineSpace(double value, STLineSpacingRule.Enum spaceRule) {
ensureInit();
if (pSpacing == null) {
initSpacing();
}
int unit;
if (spaceRule == null) {
spaceRule = STLineSpacingRule.AUTO;
}
if (spaceRule.intValue() == STLineSpacingRule.INT_AUTO) {
//当行距规则为多倍行距时,单位为行,且最小为0.06行
unit = ONE_LINE;
if (value < 0.06) {
value = 0.06;
}
} else {
//当行距规则为固定值或最小值时,单位为磅,且最小为0.7磅
unit = PER_POUND;
if (value < 0.7) {
value = 0.7;
}
}
pSpacing.setLine(BigInteger.valueOf((long) (value * unit)));
pSpacing.setLineRule(spaceRule);
return this;
}
public XWPFParagraphBuilder initInd() {
ensureInit();
pInd = pPr.getInd() != null ? pPr.getInd() : pPr.addNewInd();
return this;
}
//设置段落缩进,以厘米为单位; 悬挂缩进高于首行缩进;右侧缩进高于左侧缩进
public XWPFParagraphBuilder indentInCM(double firstLine, double hanging, double right, double left) {
ensureInit();
if (pInd == null) {
initInd();
}
if (firstLine != 0) {
pInd.setFirstLine(BigInteger.valueOf((long) (firstLine * PER_CM)));
}
if (hanging != 0) {
pInd.setHanging(BigInteger.valueOf((long) (hanging * PER_CM)));
}
if (right != 0) {
pInd.setRight(BigInteger.valueOf((long) (right * PER_CM)));
}
if (left != 0) {
pInd.setLeft(BigInteger.valueOf((long) (left * PER_CM)));
}
return this;
}
//设置段落缩进,以字符为单位; 悬挂缩进高于首行缩进;右侧缩进高于左侧缩进
public XWPFParagraphBuilder indentInChart(int firstLine, int hanging, int left, int right) {
ensureInit();
if (pInd == null) {
initInd();
}
if (firstLine != 0) {
pInd.setFirstLineChars(BigInteger.valueOf((long) (firstLine * PER_CHART)));
}
if (hanging != 0) {
pInd.setHangingChars(BigInteger.valueOf((long) (hanging * PER_CHART)));
}
if (right != 0) {
pInd.setRightChars(BigInteger.valueOf((long) (right * PER_CHART)));
}
if (left != 0) {
pInd.setLeftChars(BigInteger.valueOf((long) (left * PER_CHART)));
}
return this;
}
public XWPFParagraphBuilder savePr(String pPrName) {
ensureInit();
if (savedPPr == null) {
savedPPr = new HashedMap<String, CTPPr>();
}
savedPPr.put(pPrName, pPr);
return this;
}
public XWPFParagraphBuilder samePrOf(String pPrName) {
ensureInit();
if (savedPPr != null && savedPPr.containsKey(pPrName)) {
return samePrOf(savedPPr.get(pPrName));
}
return this;
}
public XWPFParagraphBuilder samePrOf(CTPPr pPr) {
ensureInit();
if (pPr != null) {
paragraph.getCTP().setPPr(pPr);
}
return this;
}
public XWPFParagraphBuilder samePrOf(XWPFParagraph otherPra) {
ensureInit();
paragraph.getCTP().setPPr(getPrOfParagraph(otherPra));
return this;
}
public XWPFParagraph build() {
return paragraph;
}
//确保init方法是第一个调用的,避免出现空指针异常
private void ensureInit() {
if (this.paragraph == null) {
throw new IllegalStateException("the init method must be invoked firstly");
}
}
}
构建器的优点在于能够链式调用
示例:
代码语言:javascript复制 //新增一个段前2倍行距段后3倍行距,文本2倍行距的段落
XWPFParagraph firstPar = paragraphBuilder.init(document).initSpacing().spaceInLine(2, 3)
.lineSpace(2, null).build();
如果有一段段落的属性与之前段落相同,你可以在链尾调用savePr(String pPrName),来为保存该属性,并制定名称,当有其他段落要用到次属性时就可以在调用samePrOf(String pPrName)来设定属性避免重复操作。
其中有一点需要注意的是:init方法一定是第一个调用的,不然会出现空指针异常
接下来是文本构建器(思想与段落相同)
代码语言:javascript复制//文本构建器
public class XWPFRunBuilder {
private XWPFRun run = null;
private Map<String, CTRPr> savedRPr;
public XWPFRunBuilder init(XWPFParagraph paragraph) {
return init(paragraph, false);
}
public XWPFRunBuilder init(XWPFParagraph paragraph, boolean newLine) {
this.run = paragraph.createRun();
if (newLine) {
run.addBreak();
}
return this;
}
/**
* insert a new Run in RunArray
*
* @param pos The position at which the new run should be added.
*/
public XWPFRunBuilder init(XWPFParagraph paragraph, int pos) {
this.run = paragraph.insertNewRun(pos);
if (this.run == null) {
return init(paragraph, false);
}
return this;
}
public XWPFRunBuilder init(XWPFRun run) {
if (run == null) {
throw new IllegalArgumentException("the run should not be null");
}
this.run = run;
return this;
}
public XWPFRunBuilder content(String content) {
ensureInit();
if (StringUtils.isNotBlank(content)) {
// pRun.setText(content);
if (content.contains("n")) {// System.properties("line.separator")
String[] lines = content.split("n");
run.setText(lines[0], 0); // set first line into XWPFRun
for (int i = 1; i < lines.length; i ) {
// add break and insert new text
run.addBreak();
run.setText(lines[i]);
}
} else {
run.setText(content, 0);
}
}
return this;
}
//加粗
public XWPFRunBuilder bold(boolean bold) {
ensureInit();
run.setBold(bold);
return this;
}
//斜体
public XWPFRunBuilder italic(boolean italic) {
ensureInit();
run.setItalic(italic);
return this;
}
//删除线
public XWPFRunBuilder strike(boolean strike) {
ensureInit();
run.setStrike(strike);
return this;
}
//字体设置,中文字体、英文字体、字号
public XWPFRunBuilder font(String cnFontFamily, String enFontFamily, String fontSize) {
ensureInit();
CTRPr rPr = getPrOfRun(run);
// 设置字体
CTFonts fonts = rPr.isSetRFonts() ? rPr.getRFonts() : rPr.addNewRFonts();
if (StringUtils.isNotBlank(enFontFamily)) {
fonts.setAscii(enFontFamily);
fonts.setHAnsi(enFontFamily);
}
if (StringUtils.isNotBlank(cnFontFamily)) {
fonts.setEastAsia(cnFontFamily);
fonts.setHint(STHint.EAST_ASIA);
}
// 设置字体大小
CTHpsMeasure sz = rPr.isSetSz() ? rPr.getSz() : rPr.addNewSz();
sz.setVal(new BigInteger(fontSize));
CTHpsMeasure szCs = rPr.isSetSzCs() ? rPr.getSzCs() : rPr
.addNewSzCs();
szCs.setVal(new BigInteger(fontSize));
return this;
}
//底纹
public XWPFRunBuilder shade(STShd.Enum shdStyle, String shdColor) {
ensureInit();
CTRPr rPr = getPrOfRun(run);
// 设置底纹
CTShd shd = rPr.isSetShd() ? rPr.getShd() : rPr.addNewShd();
if (shdStyle != null) {
shd.setVal(shdStyle);
}
if (shdColor != null) {
shd.setColor(shdColor);
shd.setFill(shdColor);
}
return this;
}
/**
* @param position 字符垂直方向上间距位置; >0:提升; <0:降低;=磅值*2
* @return
*/
public XWPFRunBuilder position(int position) {
ensureInit();
if (position != 0) {
run.setTextPosition(position);
}
return this;
}
//字符间距
public XWPFRunBuilder space(int spacingValue) {
ensureInit();
if (spacingValue > 0) {
CTRPr rPr = getPrOfRun(run);
CTSignedTwipsMeasure measure = rPr.isSetSpacing() ? rPr.getSpacing() : rPr.addNewSpacing();
measure.setVal(new BigInteger(String.valueOf(spacingValue)));
}
return this;
}
/**
* @param verticalAlign SUPERSCRIPT:上标;SUBSCRIPT:下标
* @return
*/
public XWPFRunBuilder verticalAlign(VerticalAlign verticalAlign) {
ensureInit();
if (verticalAlign != null) {
run.setSubscript(verticalAlign);
}
return this;
}
//下划线
public XWPFRunBuilder underLine(STUnderline.Enum underStyle, String underLineColor) {
ensureInit();
CTRPr rPr = getPrOfRun(run);
CTUnderline udLine = rPr.addNewU();
udLine.setVal(underStyle);
udLine.setColor(underLineColor);
return this;
}
//高亮
public XWPFRunBuilder highLight(STHighlightColor.Enum highStyle) {
ensureInit();
CTRPr rPr = getPrOfRun(run);
if (highStyle != null) {
CTHighlight highLight = rPr.isSetHighlight() ? rPr.getHighlight() : rPr.addNewHighlight();
highLight.setVal(highStyle);
}
return this;
}
public XWPFRunBuilder savePr(String rPrName) {
ensureInit();
if (savedRPr == null) {
savedRPr = new HashedMap<String, CTRPr>();
}
savedRPr.put(rPrName, getPrOfRun(run));
return this;
}
public XWPFRunBuilder samePrOf(String rPrName) {
ensureInit();
if (savedRPr != null && savedRPr.containsKey(rPrName)) {
return samePrOf(savedRPr.get(rPrName));
}
return this;
}
public XWPFRunBuilder samePrOf(CTRPr rPr) {
ensureInit();
if (rPr != null) {
run.getCTR().setRPr(rPr);
}
return this;
}
public XWPFRunBuilder samePrOf(XWPFRun otherRun) {
ensureInit();
run.getCTR().setRPr(getPrOfRun(otherRun));
return this;
}
public XWPFRun build() {
return run;
}
private void ensureInit() {
if (this.run == null) {
throw new IllegalStateException("the init method must be invoked firstly");
}
}
}
代码语言:javascript复制public static CTPPr getPrOfParagraph(XWPFParagraph p) {
CTPPr pPr = null;
if (p.getCTP() != null) {
if (p.getCTP().getPPr() != null) {
pPr = p.getCTP().getPPr();
} else {
pPr = p.getCTP().addNewPPr();
}
}
return pPr;
}
文本构建器大体上与段落构建器类似,基本上能满足大多数操作。
以后有时间会研究操作table,这个是重点。加油!
测试结果
备注:因为只是贴出部分代码,其中有两个工具方法未给出,全部代码在github可以看到(https://github.com/jadezhang123/learning/blob/master/apple/src/main/java/own/jadezhang/learning/apple/utils/WordHolder.java)