一吟策、下載依賴
采用免費(fèi)的POI來(lái)實(shí)現(xiàn)Word字?jǐn)?shù)統(tǒng)計(jì)和添加水印。
1.1喇完、在項(xiàng)目依賴中添加
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
二蒙谓、工具類(WordUtils)
package com.gientech.jep.service.sys.util;
import com.microsoft.schemas.office.office.CTLock;
import com.microsoft.schemas.vml.*;
mport org.apache.poi.wp.usermodel.*;
import org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFHeader;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.util.List;
import java.util.stream.Stream;
/**
* word 工具處理類
* @author houyinke
* @date 2022/7/19 15:57
*/
public class WordUtils {
static Logger logger = LoggerFactory.getLogger(WordUtils.class);
/**
* 水印字體字體
*/
private static final String fontName = "PingFang SC";
/**
* 一個(gè)字平均長(zhǎng)度,單位pt欺嗤,用于:計(jì)算文本占用的長(zhǎng)度(文本總個(gè)數(shù)*單字長(zhǎng)度)
*/
private static final int widthPerWord = 10;
/**
* word文字水印(調(diào)用poi封裝的createWatermark方法)
* @param doc XWPFDocument對(duì)象
* @param markStr 水印文字
*/
public static void setWordWaterMark(XWPFDocument doc, String markStr,String fontColor) {
XWPFParagraph paragraph = doc.createParagraph();
XWPFHeaderFooterPolicy headerFooterPolicy = doc.getHeaderFooterPolicy();
if (headerFooterPolicy == null) {
headerFooterPolicy = doc.createHeaderFooterPolicy();
}
// create default Watermark - fill color black and not rotated
headerFooterPolicy.createWatermark(markStr);
// get the default header
// Note: createWatermark also sets FIRST and EVEN headers
// but this code does not updating those other headers
XWPFHeader header = headerFooterPolicy.getHeader(XWPFHeaderFooterPolicy.DEFAULT);
paragraph = header.getParagraphArray(0);
// // get com.microsoft.schemas.vml.CTShape where fill color and rotation is set
paragraph.getCTP().newCursor();
org.apache.xmlbeans.XmlObject[] xmlobjects = paragraph.getCTP().getRArray(0).getPictArray(0).selectChildren(
new javax.xml.namespace.QName("urn:schemas-microsoft-com:vml", "shape"));
if (xmlobjects.length > 0) {
CTShape ctshape = (CTShape) xmlobjects[0];
ctshape.setFillcolor(fontColor);
ctshape.setStyle(ctshape.getStyle() + ";rotation:315");
}
}
/**
* 將指定的字符串重復(fù)repeats次.
* @param pattern 字符串
* @param repeats 重復(fù)次數(shù)
* @return 生成的字符串
*/
private static String repeatString(String pattern, int repeats) {
StringBuilder buffer = new StringBuilder(pattern.length() * repeats);
Stream.generate(() -> pattern).limit(repeats).forEach(buffer::append);
return new String(buffer);
}
/**
* 為文檔添加水印
* 實(shí)現(xiàn)參考了{(lán)@link XWPFHeaderFooterPolicy#(String, int)}
* @param doc 需要被處理的docx文檔對(duì)象
* @param customText 水印文本
* @param type 類型:1.平鋪参萄;2.單個(gè)
*/
private static void waterMarkDocXDocument(XWPFDocument doc, String customText, String styleTop, int type,String fontColor,String fontSize,String rotation) {
XWPFHeader header = doc.createHeader(HeaderFooterType.DEFAULT); // 如果之前已經(jīng)創(chuàng)建過(guò) DEFAULT 的Header,將會(huì)復(fù)用之
int size = header.getParagraphs().size();
if (size == 0) {
header.createParagraph();
}
CTP ctp = header.getParagraphArray(0).getCTP();
byte[] rsidr = doc.getDocument().getBody().getPArray(0).getRsidR();
byte[] rsidrdefault = doc.getDocument().getBody().getPArray(0).getRsidRDefault();
ctp.setRsidP(rsidr);
ctp.setRsidRDefault(rsidrdefault);
CTPPr ppr = ctp.addNewPPr();
ppr.addNewPStyle().setVal("Header");
// 開(kāi)始加水印
CTR ctr = ctp.addNewR();
CTRPr ctrpr = ctr.addNewRPr();
ctrpr.addNewNoProof();
CTGroup group = CTGroup.Factory.newInstance();
CTShapetype shapetype = group.addNewShapetype();
CTTextPath shapeTypeTextPath = shapetype.addNewTextpath();
shapeTypeTextPath.setOn(STTrueFalse.T);
shapeTypeTextPath.setFitshape(STTrueFalse.T);
CTLock lock = shapetype.addNewLock();
lock.setExt(STExt.VIEW);
CTShape shape = group.addNewShape();
shape.setId("PowerPlusWaterMarkObject");
shape.setSpid("_x0000_s102");
shape.setType("#_x0000_t136");
// 平鋪或單個(gè)
if(type != 2){
// 設(shè)置形狀樣式(旋轉(zhuǎn)煎饼,位置讹挎,相對(duì)路徑等參數(shù))
shape.setStyle(getShapeStyle(customText, styleTop,rotation));
}else{
// 設(shè)置形狀樣式(旋轉(zhuǎn),位置吆玖,相對(duì)路徑等參數(shù))
shape.setStyle(getShapeStyle());
}
shape.setFillcolor(fontColor);
// 字體設(shè)置為實(shí)心
shape.setStroked(STTrueFalse.FALSE);
// 繪制文本的路徑
CTTextPath shapeTextPath = shape.addNewTextpath();
// 設(shè)置文本字體與大小
shapeTextPath.setStyle("font-family:" + fontName + ";font-size:" + fontSize);
shapeTextPath.setString(customText);
CTPicture pict = ctr.addNewPict();
pict.set(group);
}
/**
* 以藝術(shù)字方式加上水印(平鋪)
* @param docx XWPFDocument對(duì)象
* @param customText 水印文字
*/
public static void makeFullWaterMarkByWordArt(XWPFDocument docx, String customText,String fontColor,String fontSize,String styleRotation) {
// 水印文字之間使用8個(gè)空格分隔
customText = customText + repeatString(" ", 16);
// 一行水印重復(fù)水印文字次數(shù)
customText = repeatString(customText, 10);
// 與頂部的間距
String styleTop = "0pt";
if (docx == null) {
return;
}
// 遍歷文檔筒溃,添加水印
for (int lineIndex = -10; lineIndex < 20; lineIndex++) {
styleTop = 200 * lineIndex + "pt";
waterMarkDocXDocument(docx, customText, styleTop, 1,fontColor, fontSize ,styleRotation);
}
}
/**
* 以藝術(shù)字方式加上水印(單個(gè))
* @param docx XWPFDocument對(duì)象
* @param customText 水印文字
*/
public static void makeWaterMarkByWordArt(XWPFDocument docx, String customText,String fontColor,String fontSize,String rotation) {
// 與頂部的間距
String styleTop = "0pt";
// 判斷文檔是否為空
if (docx == null) {
return;
}
// 添加水印
waterMarkDocXDocument(docx, customText, styleTop, 2,fontColor,fontSize,rotation);
}
/**
* 構(gòu)建Shape的樣式參數(shù)
* @param customText 水印文本
* @return
*/
private static String getShapeStyle(String customText, String styleTop,String styleRotation) {
StringBuilder sb = new StringBuilder();
// 文本path繪制的定位方式
sb.append("position: ").append("absolute");
// 計(jì)算文本占用的長(zhǎng)度(文本總個(gè)數(shù)*單字長(zhǎng)度)
sb.append(";width: ").append(customText.length() * widthPerWord).append("pt");
// 字體高度
sb.append(";height: ").append("20pt");
sb.append(";z-index: ").append("-251654144");
sb.append(";mso-wrap-edited: ").append("f");
sb.append(";margin-top: ").append(styleTop);
sb.append(";mso-position-horizontal-relative: ").append("margin");
sb.append(";mso-position-vertical-relative: ").append("margin");
sb.append(";mso-position-vertical: ").append("left");
sb.append(";mso-position-horizontal: ").append("center");
sb.append(";rotation: ").append(styleRotation);
return sb.toString();
}
/**
* 構(gòu)建Shape的樣式參數(shù)
* @return
*/
private static String getShapeStyle() {
StringBuilder sb = new StringBuilder();
// 文本path繪制的定位方式
sb.append("position: ").append("absolute");
sb.append(";left: ").append("opt");
// 計(jì)算文本占用的長(zhǎng)度(文本總個(gè)數(shù)*單字長(zhǎng)度)
sb.append(";width: ").append("500pt");
// 字體高度
sb.append(";height: ").append("150pt");
sb.append(";z-index: ").append("-251654144");
sb.append(";mso-wrap-edited: ").append("f");
sb.append(";margin-left: ").append("-50pt");
sb.append(";margin-top: ").append("270pt");
sb.append(";mso-position-horizontal-relative: ").append("margin");
sb.append(";mso-position-vertical-relative: ").append("margin");
sb.append(";mso-width-relative: ").append("page");
sb.append(";mso-height-relative: ").append("page");
sb.append(";rotation: ").append("-2949120f");
return sb.toString();
}
/**
* word 字?jǐn)?shù)統(tǒng)計(jì)
* @param xwpfDocument doc文檔
* @return
*/
public static int count(XWPFDocument xwpfDocument){
List<XWPFParagraph> paragraphs = xwpfDocument.getParagraphs();
// 行數(shù),字?jǐn)?shù)
int rowCount = 1, count = 0;
try {
// 循環(huán)讀取字?jǐn)?shù)
for (XWPFParagraph xwpfParagraph : paragraphs) {
int linLength = 0;
String lineStr = "";
List<XWPFRun> xwpfRuns = xwpfParagraph.getRuns();
for (XWPFRun xwpfRun : xwpfRuns) {
linLength += xwpfRun.toString().trim().length();
lineStr += xwpfRun.toString();
count += xwpfRun.toString().trim().length();
}
logger.info("第" + rowCount + "行內(nèi)容,長(zhǎng)度:" + linLength);
rowCount ++;
}
} catch (Exception e) {
e.printStackTrace();
}
return count;
}
/**
* 加載docx格式的word文檔
* @param inputStream
* @return
*/
private static XWPFDocument loadDocXDocument(InputStream inputStream) {
XWPFDocument doc;
try {
doc = new XWPFDocument(inputStream);
} catch (Exception e) {
throw new RuntimeException("文檔加載失敗Kゲ铡羡!");
}
return doc;
}
public static void main(String[] args) throws IOException {
XWPFDocument xwpfDocument = null;
try {
//輸入的docx文檔
InputStream in = new FileInputStream(new File("/Users/houyinke/Desktop/doc/4.doc"));
xwpfDocument = WordUtils.loadDocXDocument(in);
// 統(tǒng)計(jì)字?jǐn)?shù)
int count = WordUtils.count(xwpfDocument);
System.out.println("word字?jǐn)?shù):" + count);
// 添加水印
WordUtils.makeFullWaterMarkByWordArt(xwpfDocument, "中電金信", "#888888", "0.7pt","-30");
// 寫出加水印后的文檔
String filePath = "/Users/houyinke/Desktop/doc/3.docx";
OutputStream os = new FileOutputStream(filePath);
xwpfDocument.write(os);
} catch (IOException e) {
e.printStackTrace();
} finally {
if(xwpfDocument != null){
xwpfDocument.close();
}
}
}
}