Java POI word 工具類統(tǒng)計(jì)及水印處理

一吟策、下載依賴
采用免費(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();
        }
    }
}

}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市意鲸,隨后出現(xiàn)的幾起案子烦周,更是在濱河造成了極大的恐慌尽爆,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,744評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件读慎,死亡現(xiàn)場(chǎng)離奇詭異漱贱,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)夭委,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門幅狮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人株灸,你說(shuō)我怎么就攤上這事崇摄。” “怎么了慌烧?”我有些...
    開(kāi)封第一講書人閱讀 163,105評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵逐抑,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我屹蚊,道長(zhǎng)厕氨,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,242評(píng)論 1 292
  • 正文 為了忘掉前任汹粤,我火速辦了婚禮命斧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘嘱兼。我一直安慰自己国葬,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,269評(píng)論 6 389
  • 文/花漫 我一把揭開(kāi)白布遭京。 她就那樣靜靜地躺著胃惜,像睡著了一般泞莉。 火紅的嫁衣襯著肌膚如雪哪雕。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 51,215評(píng)論 1 299
  • 那天鲫趁,我揣著相機(jī)與錄音斯嚎,去河邊找鬼。 笑死挨厚,一個(gè)胖子當(dāng)著我的面吹牛堡僻,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播疫剃,決...
    沈念sama閱讀 40,096評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼钉疫,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了巢价?” 一聲冷哼從身側(cè)響起牲阁,我...
    開(kāi)封第一講書人閱讀 38,939評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤固阁,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后城菊,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體备燃,經(jīng)...
    沈念sama閱讀 45,354評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,573評(píng)論 2 333
  • 正文 我和宋清朗相戀三年凌唬,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了并齐。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,745評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡客税,死狀恐怖况褪,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情更耻,我是刑警寧澤窝剖,帶...
    沈念sama閱讀 35,448評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站酥夭,受9級(jí)特大地震影響赐纱,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜熬北,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,048評(píng)論 3 327
  • 文/蒙蒙 一疙描、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧讶隐,春花似錦起胰、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,683評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至炉峰,卻和暖如春畏妖,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背疼阔。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,838評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工戒劫, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人婆廊。 一個(gè)月前我還...
    沈念sama閱讀 47,776評(píng)論 2 369
  • 正文 我出身青樓迅细,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親淘邻。 傳聞我的和親對(duì)象是個(gè)殘疾皇子茵典,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,652評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容