概述

Apache POI 简介是用Java编写的免费开源的跨平台的 Java API,Apache POI提供API给Java程式对Microsoft Office(Excel、WORD、PowerPoint、Visio等)格式档案读和写的功能。

官网: http://poi.apache.org/index.html

API帮助文档: http://poi.apache.org/apidocs/index.html

Apache POI常用的类

  • HSSF - 提供读写Microsoft Excel XLS格式档案的功能。
  • XSSF - 提供读写Microsoft Excel OOXML XLSX格式档案的功能。
  • HWPF - 提供读写Microsoft Word DOC97格式档案的功能。
  • XWPF- 提供读写Microsoft Word DOC2003格式档案的功能。
  • HSLF - 提供读写Microsoft PowerPoint格式档案的功能。
  • HDGF - 提供读Microsoft Visio格式档案的功能。
  • HPBF - 提供读Microsoft Publisher格式档案的功能。
  • HSMF - 提供读Microsoft Outlook格式档案的功能。

Maven依赖

 <dependency>
     <groupId>org.apache.poi</groupId>
     <artifactId>poi</artifactId>
     <version>4.0.0</version>
 </dependency>

<dependency>
     <groupId>org.apache.poi</groupId>
     <artifactId>poi-ooxml</artifactId>
     <version>4.0.0</version>
 </dependency>

Excel(HSSF/XSSF)

概述

​ HSSF是POI项目对Excel '97(.xls)文件格式的纯Java实现。XSSF是POI项目对Excel 2007 OOXML(.xlsx)文 件格式的纯Java实现。

​ HSSF和XSSF提供了读取电子表格的方法,以创建,修改,读取和写入XLS电子表格。

创建工作薄

Workbook wb = new HSSFWorkbook();
...
try (OutputStream fileOut = new FileOutputStream("workbook.xls")) {
    wb.write(fileOut);
}
或者
Workbook wb = new XSSFWorkbook();
...
try (OutputStream fileOut = new FileOutputStream("workbook.xlsx")) {
    wb.write(fileOut);
}

创建工作表

//创建默认名称的Sheet 
Sheet sheet = wb.createSheet();
//创建指定名称的Sheet
Sheet my_sheet = wb.createSheet("my_sheet");

创建单元格

 // 创建行
 Row row = sheet.createRow(0);
 // 创建单元格
 Cell cell = row.createCell(0);
 //设置值
 cell.setCellValue(1);

 或者链式编程
 sheet.createRow(0).createCell(1).setCellValue(88);
 sheet.createRow(0).createCell(2).setCellValue(true);

创建日期类单元格

CellStyle cellStyle = wb.createCellStyle();
CreationHelper creationHelper = wb.getCreationHelper();
cellStyle.setDataFormat(creationHelper.createDataFormat().getFormat("yyyy-MM-dd HH:mm:ss"));
cell = row.createCell(3);
cell.setCellValue(new Date());
cell.setCellStyle(cellStyle);
//你也能使用日期工具类  java.util.Calendar
cell = row.createCell(4);
cell.setCellValue(Calendar.getInstance());
cell.setCellStyle(cellStyle);

Files vs InputStreams

当打开工作簿(.xls HSSFWorkbook或.xlsx XSSFWorkbook)时,可以从FileInputStream加载工作簿。使用File对象可以减少内存消耗,而InputStream需要更多内存,因为它必须缓冲整个文件。

如果使用WorkbookFactory,则使用其中一个非常容易:

// Use a file
Workbook wb = WorkbookFactory.create(new File("MyExcel.xls"));
// Use an InputStream, needs more memory
Workbook wb = WorkbookFactory.create(new FileInputStream("MyExcel.xlsx"));

展示各种对齐方式

public static void main(String[] args) throws Exception {
        Workbook wb = new XSSFWorkbook(); //or new HSSFWorkbook();
        Sheet sheet = wb.createSheet();
        Row row = sheet.createRow(2);
        row.setHeightInPoints(30);
        createCell(wb, row, 0, HorizontalAlignment.CENTER, VerticalAlignment.BOTTOM);
        createCell(wb, row, 1, HorizontalAlignment.CENTER_SELECTION,                 	         VerticalAlignment.BOTTOM);
        createCell(wb, row, 2, HorizontalAlignment.FILL, VerticalAlignment.CENTER);
        createCell(wb, row, 3, HorizontalAlignment.GENERAL, VerticalAlignment.CENTER);
        createCell(wb, row, 4, HorizontalAlignment.JUSTIFY, VerticalAlignment.JUSTIFY);
        createCell(wb, row, 5, HorizontalAlignment.LEFT, VerticalAlignment.TOP);
        createCell(wb, row, 6, HorizontalAlignment.RIGHT, VerticalAlignment.TOP);
        // Write the output to a file
        try (OutputStream fileOut = new FileOutputStream("writeExcel.xlsx")) {
            wb.write(fileOut);
        }
        wb.close();
    }
    private static void createCell(Workbook wb, Row row, int column, HorizontalAlignment     halign, VerticalAlignment valign) {
        Cell cell = row.createCell(column);
        cell.setCellValue("Align It");
        CellStyle cellStyle = wb.createCellStyle();
        cellStyle.setAlignment(halign);
        cellStyle.setVerticalAlignment(valign);
        cell.setCellStyle(cellStyle);
    }

边界处理

设置边框的样式与颜色

CellStyle style = wb.createCellStyle();
style.setBorderBottom(BorderStyle.THIN);
style.setBottomBorderColor(IndexedColors.BLACK.getIndex());
style.setBorderLeft(BorderStyle.THIN);
style.setLeftBorderColor(IndexedColors.GREEN.getIndex());
style.setBorderRight(BorderStyle.THIN);
style.setRightBorderColor(IndexedColors.BLUE.getIndex());
style.setBorderTop(BorderStyle.MEDIUM_DASHED);
style.setTopBorderColor(IndexedColors.BLACK.getIndex());
cell.setCellStyle(style);

获取单元格内容

要获取单元格的内容,您首先需要知道它是哪种单元格.

@Test
public void cellValue() throws IOException {
    HSSFWorkbook wb = new HSSFWorkbook(new FileInputStream("cellValue.xls"));
    DataFormatter formatter = new DataFormatter();
    Sheet sheet1 = wb.getSheetAt(0);
    for (Row row : sheet1) {
        for (Cell cell : row) {
            CellReference cellRef = new CellReference(row.getRowNum(),       		                 cell.getColumnIndex());
            System.out.print(cellRef.formatAsString());
            System.out.print(" - ");
            //能够获取任何单元格类型的值
            String text = formatter.formatCellValue(cell);
            System.out.println(text);
            //或者
            // 判断单元格类型
            switch (cell.getCellType()) {
                case STRING:
                    System.out.println(cell.getRichStringCellValue().getString());
                    break;
                case NUMERIC:
                    if (DateUtil.isCellDateFormatted(cell)) {
                        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd   	                             HH:mm:ss");
                        String value = sdf.format(cell.getDateCellValue());
                        System.out.println(value);
                    } else {
                        System.out.println(cell.getNumericCellValue());
                    }
                    break;
                case BOOLEAN:
                    System.out.println(cell.getBooleanCellValue());
                    break;
                case FORMULA:
                    System.out.println(cell.getCellFormula());
                    break;
                case BLANK:
                    System.out.println("");
                    break;
                case ERROR:
                    System.out.println("未知错误");
                default:
                    System.out.println("未知类型");
            }
        }
    }
}

文字提取

对于大多数文本提取要求,标准的ExcelExtractor类应提供您所需要的全部。

	@Test
	public void textExtraction() throws IOException {
        InputStream inp = new FileInputStream("cellValue.xls");
        HSSFWorkbook wb = new HSSFWorkbook(new POIFSFileSystem(inp));
        ExcelExtractor extractor = new ExcelExtractor(wb);
        extractor.setFormulasNotResults(true);
        extractor.setIncludeSheetNames(false);
        String text = extractor.getText();
        System.out.println(text);
        wb.close();
	}

填充和颜色

 CellStyle style = wb.createCellStyle();
 style.setFillBackgroundColor(IndexedColors.AQUA.getIndex());//填充背景颜色
 style.setFillPattern(FillPatternType.BIG_SPOTS);            //填充模式
 Cell cell = row.createCell(1);
 cell.setCellStyle(style);

 style.setFillForegroundColor(IndexedColors.ORANGE.getIndex());//填充前景色
 style.setFillPattern(FillPatternType.SOLID_FOREGROUND);       //填充模式

合并单元格

//参数1: first row (0-based) 开始行
//参数2: last row  (0-based) 结束行
//参数3: first column (0-based) 开始列
//参数4: last column  (0-based) 结束列
sheet.addMergedRegion(new CellRangeAddress(1, 1, 1, 2));

字体

 Font font = wb.createFont();
 font.setFontHeightInPoints((short)24);
 font.setFontName("Courier New");
 font.setItalic(true); //斜体
 font.setStrikeout(true); //删除线
 font.setBold(true);//加粗

 CellStyle style = wb.createCellStyle();
 style.setFont(font);

 cell.setCellStyle(style);

遇到的问题

使用POI循环写入数据时发现只有最后一列有数据

检查调用createRow() 是否在外层循环调用的,如果是在内层循环调用就会出现这个问题,因为程序会不停的重新创建行,直至最后一个cell中的数据写入,跳至下一行同上,所以会出现只有最后一列数据的情况。

Excel中写入日期变为一堆#######

原因: 单元格的宽度不够

拉开单元格宽度发现正常显示了

Word(HWPF)

​ Apache poi的hwpf模块是专门用来对word doc文件进行读写操作的。在hwpf里面我们使用HWPFDocument来表示一个word doc文档。在HWPFDocument里面有这么几个概念:

Range:它表示一个范围,这个范围可以是整个文档,也可以是里面的某一小节(Section),也可以是某一个段落(Paragraph),还可以是拥有共同属性的一段文本(CharacterRun)。

Section:word文档的一个小节,一个word文档可以由多个小节构成。

Paragraph:word文档的一个段落,一个小节可以由多个段落构成。

CharacterRun:具有相同属性的一段文本,一个段落可以由多个CharacterRun组成。

Table:一个表格。

TableRow:表格对应的行。

TableCell:表格对应的单元格。

​ Section、Paragraph、CharacterRun和Table都继承自Range。

Maven依赖

<dependency>
     <groupId>org.apache.poi</groupId>
     <artifactId>poi</artifactId>
     <version>4.0.0</version>
</dependency>

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-scratchpad</artifactId>
    <version>4.0.0</version>
</dependency>

读word doc文件

​ 在日常应用中,我们从word文件里面读取信息的情况非常少见,更多的还是把内容写入到word文件中。使用POI从word doc文件读取数据时主要有两种方式:通过WordExtractor读和通过HWPFDocument读。在WordExtractor内部进行信息读取时还是通过HWPFDocument来获取的。

通过WordExtractor读文件

​ 在使用WordExtractor读文件时我们只能读到文件的文本内容和基于文档的一些属性,至于文档内容的属性等是无法读到的。如果要读到文档内容的属性则需要使用HWPFDocument来读取了。下面是使用WordExtractor读取文件的一个示例:

public class HwpfTest {
    @Test
    public void testReadByExtractor() throws IOException {
        InputStream is = new FileInputStream("test.doc");
        WordExtractor extractor = new WordExtractor(is);
        //输出word文档所有的文本
        System.out.println(extractor.getText());
        System.out.println("=======================");
        System.out.println(extractor.getTextFromPieces());
        //输出页眉的内容
        System.out.println("页眉: " + extractor.getHeaderText());
        //输出页脚的内容
        System.out.println("页脚: " + extractor.getFooterText());
        //输出当前word文档的元数据信息,包括作者、文档的修改时间等。
        System.out.println(extractor.getMetadataTextExtractor().getText());
        //获取各个段落的文本
        String paraTexts[] = extractor.getParagraphText();
        for (int i = 0; i < paraTexts.length; i++) {
            System.out.println("Paragraph " + (i + 1) + " : " + paraTexts[i]);
        }
        //输出当前word的一些信息
        printInfo(extractor.getSummaryInformation());
        //输出当前word的一些信息
        this.printInfo(extractor.getDocSummaryInformation());
        this.closeStream(is);
    }

    /**
     * 输出SummaryInfomation
     *
     * @param info
     */
    private void printInfo(SummaryInformation info) {
        //作者
        System.out.println(info.getAuthor());
        //字符统计
        System.out.println(info.getCharCount());
        //页数
        System.out.println(info.getPageCount());
        //标题
        System.out.println(info.getTitle());
        //主题
        System.out.println(info.getSubject());
    }

    /**
     * 输出DocumentSummaryInfomation
     *
     * @param info
     */
    private void printInfo(DocumentSummaryInformation info) {
        //分类
        System.out.println(info.getCategory());
        //公司
        System.out.println(info.getCompany());
    }

    /**
     * 关闭输入流
     *
     * @param is
     */
    private void closeStream(InputStream is) {
        if (is != null) {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
通过HWPFDocument读文件

HWPFDocument是当前Word文档的代表,它的功能比WordExtractor要强。通过它我们可以读取文档中的表格、列表等,还可以对文档的内容进行新增、修改和删除操作。只是在进行完这些新增、修改和删除后相关信息是保存在HWPFDocument中的,也就是说我们改变的是HWPFDocument,而不是磁盘上的文件。如果要使这些修改生效的话,我们可以调用HWPFDocument的write方法把修改后的HWPFDocument输出到指定的输出流中。这可以是原文件的输出流,也可以是新文件的输出流(相当于另存为)或其它输出流。

@Test
public void testReadByDoc() throws Exception {
    InputStream is = new FileInputStream("test.doc");
    HWPFDocument doc = new HWPFDocument(is);
    //输出书签信息
    this.printInfo(doc.getBookmarks());
    //输出文本
    System.out.println(doc.getDocumentText());
    Range range = doc.getRange();
    //  this.insertInfo(range);
    this.printInfo(range);
    //读表格
    this.readTable(range);
    //读列表
    this.readList(range);
    //删除range
    Range r = new Range(2, 5, doc);
    r.delete();//在内存中进行删除,如果需要保存到文件中需要再把它写回文件
    //把当前HWPFDocument写到输出流中
    doc.write(new FileOutputStream("test.doc"));
    this.closeStream(is);
}

/**
 * 关闭输入流
 * @param is
 */
private void closeStream(InputStream is) {
    if (is != null) {
        try {
            is.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

/**
 * 输出书签信息
 * @param bookmarks
 */
private void printInfo(Bookmarks bookmarks) {
    int count = bookmarks.getBookmarksCount();
    System.out.println("书签数量:" + count);
    Bookmark bookmark;
    for (int i=0; i<count; i++) {
        bookmark = bookmarks.getBookmark(i);
        System.out.println("书签" + (i+1) + "的名称是:" + bookmark.getName());
        System.out.println("开始位置:" + bookmark.getStart());
        System.out.println("结束位置:" + bookmark.getEnd());
    }
}

/**
 * 读表格
 * 每一个回车符代表一个段落,所以对于表格而言,每一个单元格至少包含一个段落,每行结束都是一个段落。
 * @param range
 */
private void readTable(Range range) {
    System.out.println("=============readTable start================");
    //遍历range范围内的table。
    TableIterator tableIter = new TableIterator(range);
    Table table;
    TableRow row;
    TableCell cell;
    while (tableIter.hasNext()) {
        table = tableIter.next();
        int rowNum = table.numRows();
        for (int j=0; j<rowNum; j++) {
            row = table.getRow(j);
            int cellNum = row.numCells();
            for (int k=0; k<cellNum; k++) {
                cell = row.getCell(k);
                //输出单元格的文本
                System.out.print(cell.text().trim()+"   ");
            }
            System.out.println();
        }
    }
    System.out.println("=============readTable end==============");
}

/**
 * 读列表
 * @param range
 */
private void readList(Range range) {
    System.out.println("=========readList  start==========");
    int num = range.numParagraphs();
    Paragraph para;
    for (int i=0; i<num; i++) {
        para = range.getParagraph(i);
        if (para.isInList()) {
            System.out.println("list: " + para.text());
        }
    }
    System.out.println("=========readList  end==========");
}

/**
 * 输出Range
 * @param range
 */
private void printInfo(Range range) {
    //获取段落数
    int paraNum = range.numParagraphs();
    System.out.println(paraNum);
    for (int i=0; i<paraNum; i++) {
        System.out.println("段落" + (i+1) + ":" + range.getParagraph(i).text());
        if (i == (paraNum-1)) {
            this.insertInfo(range.getParagraph(i));
        }
    }
    int secNum = range.numSections();
    System.out.println(secNum);
    Section section;
    for (int i=0; i<secNum; i++) {
        section = range.getSection(i);
        System.out.println(section.getMarginLeft());
        System.out.println(section.getMarginRight());
        System.out.println(section.getMarginTop());
        System.out.println(section.getMarginBottom());
        System.out.println(section.getPageHeight());
        System.out.println(section.text());
    }
}

/**
 * 插入内容到Range,这里只会写到内存中
 * @param range
 */
private void insertInfo(Range range) {
    range.insertAfter("Hello");
}

写word doc文件

在实际应用中,我们在生成word文件的时候都是生成某一类文件,该类文件的格式是固定的,只是某些字段不一样罢了。所以在实际应用中,我们大可不必将整个word文件的内容都通过HWPFDocument生成。而是先在磁盘上新建一个word文档,其内容就是我们需要生成的word文件的内容,然后把里面一些属于变量的内容使用类似于“${paramName}”这样的方式代替。这样我们在基于某些信息生成word文件的时候,只需要获取基于该word文件的HWPFDocument,然后调用Range的replaceText()方法把对应的变量替换为对应的值即可,之后再把当前的HWPFDocument写入到新的输出流中。这种方式在实际应用中用的比较多,因为它不但可以减少我们的工作量,还可以让文本的格式更加的清晰。下面我们就来基于这种方式做一个示例。

​ 假设我们现在拥有一些变动的信息,然后需要通过这些信息生成如下格式的word doc文件:model.doc

${title}报告

姓名年龄生日
${name}${age}${birthday}

替换模版中的变量,生成新的doc文件

@Test
public void testWrite() throws Exception {
    String templatePath = "model.doc";
    InputStream is = new FileInputStream(templatePath);
    HWPFDocument doc = new HWPFDocument(is);
    Range range = doc.getRange();
    range.replaceText("${title}", "体检");
    range.replaceText("${name}", "张三");
    range.replaceText("${age}", "22");
    range.replaceText("${birthday}", new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
    OutputStream os = new FileOutputStream("new.doc");
    //把doc输出到输出流中
    doc.write(os);
    os.close();
    is.close();
}

Word(XWPF)

API文档:https://poi.apache.org/apidocs/4.0/index.html

XWPFDocument

这是 org.apache.poi.xwpf.usermodel 包下的类。 它用于创建.docx文件格式的MS-Word文档。

类方法:

No.方法和说明
1commit()提交并保存文档。
2createParagraph()在本文档中追加一个新段落。
3createTable()默认情况下,创建一个包含一行和一列的空表。
4createTOC()创建Word文档的内容表。
5getParagraphs()返回包含页眉或页脚文本的段落。
6getStyle()返回使用的样式对象。

XWPFParagraph

这是 org.apache.poi.xwpf.usermodel 包下的类,用于在Word文档中创建段落。 此实例也用于将所有类型的元素添加到Word文档中。

类方法:

No.方法和说明
1createRun()将新运行附加到此段落。
2getAlignment()返回将应用于本段文本的段落对齐方式。
3setAlignment(ParagraphAlignment align)指定应适用于本段文本的段落对齐方式。
4setBorderBottom(Borders border)指定应显示在一组段落下方的边框,这些段落具有相同的一组段落边框设置。
5setBorderLeft(Borders border)指定应在页面左侧围绕指定段落显示的边框。
6setBorderRight(Borders border)指定应在页面右侧围绕指定段落显示的边框。
7setBorderTop(Borders border)指定应显示在具有相同的一组段落边框设置的一组段落上方的边框。

XWPFRun

这是 org.apache.poi.xwpf.usermodel 包下的类,用于向段落中添加文本区域。

类方法:

No.方法和说明
1addBreak()指定断点应放置在运行内容的当前位置。
2addTab()指定制表符应放置在运行内容中的当前位置。
3setColor(java.lang.String rgbStr)设置文本颜色。
4setFontSize(int size)指定在显示时应用于此运行内容中所有非复杂脚本字符的字体大小。
5setText(java.lang.String value)设置此文本运行的文本。
6setBold(boolean value)指定在文档中显示时,粗体属性是否应用于此运行内容中的所有非复杂脚本字符。

XWPFStyle

这是 org.apache.poi.xwpf.usermodel 包下的类,用于向word文档中的对象元素添加不同的样式。

类方法:

No.方法和说明
1getNextStyleID()它用于获取下一个样式的StyleID。
2getStyleId()它用于获取样式的StyleID。
3getStyles()它用于获取样式。
4setStyleId(java.lang.String styleId)它用于设置styleID。

XWPFTable

这是 org.apache.poi.xwpf.usermodel 包下的类,用于将表数据添加到Word文档中。

类方法:

No.方法和说明
1addNewCol()为此表中的每一行添加一个新列。
2addRow(XWPFTableRow row)向表中添加新行
3addRow(XWPFTableRow row, int pos)向表中指定位置添加新行
4createRow()创建一个新的XWPFTableRow对象,其具有与那一刻定义的列数一样多的单元格。
5setWidth(int width)设置列的宽度。

XWPFWordExtractor

这是 org.apache.poi.xwpf.extractor 包下的类。 它是一个基本的解析器类,用于从Word文档中提取简单文本。

类方法:

No.方法和说明
1getText()检索文档中的所有文本。

Apache POI Word - 文件

创建空白文档

@Test
public void createDocument() throws IOException {
    XWPFDocument document= new XWPFDocument();
    FileOutputStream out = new FileOutputStream(new File("createdocument.docx"));
    document.write(out);
    out.close();
}

Apache POI Word - 段落

创建段落

首先创建一个文档,然后我们可以创建一个段落。

XWPFDocument document = new XWPFDocument();
XWPFParagraph paragraph = document.createParagraph();

运行段落

您可以使用运行输入文本或任何对象元素。 使用Paragraph实例,您可以创建运行

XWPFRun run = paragraph.createRun();

写入段落

run.setText("Hello World");

完整代码

@Test
public void createParagraph() throws IOException {

    XWPFDocument document = new XWPFDocument();
    FileOutputStream out = new FileOutputStream(new File("createparagraph.docx"));
    XWPFParagraph paragraph = document.createParagraph();

    XWPFRun run = paragraph.createRun();

    run.setText("Hello World");
    document.write(out);
    out.close();
}

Apache POI Word - 边框

应用边框

以下代码用于在文档中应用边框:

@Test
public void applyingBorder() throws IOException {
    XWPFDocument document= new XWPFDocument();

    FileOutputStream out = new FileOutputStream(new File("applyingborder.docx"));

    XWPFParagraph paragraph = document.createParagraph();

    XWPFRun run=paragraph.createRun();
    run.setText("您将学习如何使用Java编程将边框应用到段落。");

    paragraph.setBorderBottom(Borders.SINGLE);

    paragraph.setBorderLeft(Borders.SINGLE);

    paragraph.setBorderRight(Borders.SINGLE);

    paragraph.setBorderTop(Borders.SINGLE);

    document.write(out);
    out.close();
}

Apache POI Word - 表格

创建表

@Test
public void createTable() throws IOException {
    XWPFDocument document= new XWPFDocument();

    FileOutputStream out = new FileOutputStream(new File("create_table.docx"));

    //create table
    XWPFTable table = document.createTable();
    //create first row
    XWPFTableRow tableRowOne = table.getRow(0);
    tableRowOne.getCell(0).setText("col one, row one");
    tableRowOne.addNewTableCell().setText("col two, row one");
    tableRowOne.addNewTableCell().setText("col three, row one");
    //create second row
    XWPFTableRow tableRowTwo = table.createRow();
    tableRowTwo.getCell(0).setText("col one, row two");
    tableRowTwo.getCell(1).setText("col two, row two");
    tableRowTwo.getCell(2).setText("col three, row two");
    //create third row
    XWPFTableRow tableRowThree = table.createRow();
    tableRowThree.getCell(0).setText("col one, row three");
    tableRowThree.getCell(1).setText("col two, row three");
    tableRowThree.getCell(2).setText("col three, row three");

    document.write(out);
    out.close();
}

Apache POI Word - 字体样式和对齐方式

通常,字体样式包含:字体大小,类型,粗体,斜体和下划线。 对齐分为左,中,右,对齐。

字体样式

@Test
public void fontStyle() throws IOException {
    XWPFDocument document= new XWPFDocument();

    //Write the Document in file system
    FileOutputStream out = new FileOutputStream(new File("fontstyle.docx"));

    //create paragraph
    XWPFParagraph paragraph = document.createParagraph();

    //Set Bold an Italic
    XWPFRun paragraphOneRunOne = paragraph.createRun();
    paragraphOneRunOne.setBold(true); //加粗
    paragraphOneRunOne.setItalic(true);//斜体
    paragraphOneRunOne.setText("Font Style");
    paragraphOneRunOne.addBreak();

    //Set text Position
    XWPFRun paragraphOneRunTwo = paragraph.createRun();
    paragraphOneRunTwo.setUnderline(UnderlinePatterns.SINGLE); //下划线
    paragraphOneRunTwo.setText("Font Style two");
    paragraphOneRunTwo.setTextPosition(30);

    //Set Strike through and Font Size and Subscript
    XWPFRun paragraphOneRunThree = paragraph.createRun();
    paragraphOneRunThree.setStrike(true);
    paragraphOneRunThree.setFontSize(18);
    paragraphOneRunThree.setSubscript(VerticalAlign.SUBSCRIPT);
    paragraphOneRunThree.setText(" Different Font Styles");

    document.write(out);
    out.close();
}

对齐方式

以下代码用于设置与段落文本的对齐方式:

@Test
    public void alignParagraph() throws IOException {
        XWPFDocument document= new XWPFDocument();

        FileOutputStream out = new FileOutputStream(new File("alignparagraph.docx"));

        //create paragraph
        XWPFParagraph paragraph = document.createParagraph();

        //Set alignment paragraph to RIGHT
        paragraph.setAlignment(ParagraphAlignment.RIGHT);
        XWPFRun run=paragraph.createRun();
        run.setText("At tutorialspoint.com, we strive hard to " +
                "provide quality tutorials for self-learning " +
                "purpose in the domains of Academics, Information " +
                "Technology, Management and Computer Programming " +
                "Languages.");

        //Create Another paragraph
        paragraph=document.createParagraph();

        //Set alignment paragraph to CENTER
        paragraph.setAlignment(ParagraphAlignment.CENTER);
        run=paragraph.createRun();
        run.setText("The endeavour started by Mohtashim, an AMU " +
                "alumni, who is the founder and the managing director " +
                "of Tutorials Point (I) Pvt. Ltd. He came up with the " +
                "website tutorialspoint.com in year 2006 with the help" +
                "of handpicked freelancers, with an array of tutorials" +
                " for computer programming languages. ");
        document.write(out);
        out.close();
    }

操作word模板并生成新的word.docx

要具体操作通过XWPFDocument 可以获得的docx中的各种对象,我们离不开一个对象为XWPFRun对象,API结构org.apache.poi.xwpf.usermodel.XWPFRun。其描述为:XWPFRun object defines a region of text with a common set of properties。通过描述我们不难理解其作用为设置文本对象的各种属性。
通过XWPFDocument 获取对象

//解析docx模板并获取document对象
XWPFDocument document = new XWPFDocument(POIXMLDocument.openPackage(inputUrl));
//获取整个文本对象
List<XWPFParagraph> allParagraph = document.getParagraphs();
//获取整个表格对象
List<XWPFTable> allTable = document.getTables();
//获取图片对象
XWPFPictureData pic = document.getPictureDataByID("PICId");

首先建一个很简单的word模板model.docx,我们通过操作对象获取word中的文本内容

姓名: ${name}

性别: ${sex}

​ 需要替换的表格

家庭地址电话邮箱
${address}${phone}${email}

​ 需要插入的表格

电话QQ微信微博
package com.newland.poi.word;

import org.apache.poi.ooxml.POIXMLDocument;
import org.apache.poi.xwpf.usermodel.*;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.*;

/**
 * @author 张宝奎
 * @date 2021/3/16
 */
public class WordTemplateUtil {
    /**
     * 根据模板生成新word文档
     * 判断表格是需要替换还是需要插入,判断逻辑有$为替换,表格无$为插入
     *
     * @param inputUrl  模板存放地址
     * @param outputUrl 新文档存放地址
     * @param textMap   需要替换的信息集合
     * @param tableList 需要插入的表格信息集合
     * @return 成功返回true, 失败返回false
     */
    public static boolean changWord(String inputUrl, String outputUrl, Map<String, String> textMap, List<String[]> tableList) {

        //模板转换默认成功
        boolean changeFlag = true;
        try {
            //获取docx解析对象
            XWPFDocument document = new XWPFDocument(POIXMLDocument.openPackage(inputUrl));
            //解析替换文本段落对象
            WordTemplateUtil.changeText(document, textMap);
            //解析替换表格对象
            WordTemplateUtil.changeTable(document, textMap, tableList);

            //生成新的word
            File file = new File(outputUrl);
            FileOutputStream stream = new FileOutputStream(file);
            document.write(stream);
            stream.close();

        } catch (IOException e) {
            e.printStackTrace();
            changeFlag = false;
        }

        return changeFlag;

    }

    /**
     * 替换段落文本
     *
     * @param document docx解析对象
     * @param textMap  需要替换的信息集合
     */
    public static void changeText(XWPFDocument document, Map<String, String> textMap) {
        //获取段落集合
        List<XWPFParagraph> paragraphs = document.getParagraphs();

        for (XWPFParagraph paragraph : paragraphs) {
            //判断此段落时候需要进行替换
            String text = paragraph.getText();
            if (checkText(text)) {
                List<XWPFRun> runs = paragraph.getRuns();
                for (XWPFRun run : runs) {
                    //替换模板原来位置
                    run.setText(changeValue(run.toString(), textMap), 0);
                }
            }
        }

    }

    /**
     * 替换表格对象方法
     *
     * @param document  docx解析对象
     * @param textMap   需要替换的信息集合
     * @param tableList 需要插入的表格信息集合
     */
    public static void changeTable(XWPFDocument document, Map<String, String> textMap,
                                   List<String[]> tableList) {
        //获取表格对象集合
        List<XWPFTable> tables = document.getTables();
        for (int i = 0; i < tables.size(); i++) {
            //只处理行数大于等于2的表格,且不循环表头
            XWPFTable table = tables.get(i);
            if (table.getRows().size() > 1) {
                //判断表格是需要替换还是需要插入,判断逻辑有$为替换,表格无$为插入
                if (checkText(table.getText())) {
                    List<XWPFTableRow> rows = table.getRows();
                    //遍历表格,并替换模板
                    eachTable(rows, textMap);
                } else {
//                  System.out.println("插入"+table.getText());
                    insertTable(table, tableList);
                }
            }
        }
    }


    /**
     * 遍历表格
     *
     * @param rows    表格行对象
     * @param textMap 需要替换的信息集合
     */
    public static void eachTable(List<XWPFTableRow> rows, Map<String, String> textMap) {
        for (XWPFTableRow row : rows) {
            List<XWPFTableCell> cells = row.getTableCells();
            for (XWPFTableCell cell : cells) {
                //判断单元格是否需要替换
                if (checkText(cell.getText())) {
                    List<XWPFParagraph> paragraphs = cell.getParagraphs();
                    for (XWPFParagraph paragraph : paragraphs) {
                        List<XWPFRun> runs = paragraph.getRuns();
                        for (XWPFRun run : runs) {
                            run.setText(changeValue(run.toString(), textMap), 0);
                        }
                    }
                }
            }
        }
    }

    /**
     * 为表格插入数据,行数不够添加新行
     *
     * @param table     需要插入数据的表格
     * @param tableList 插入数据集合
     */
    public static void insertTable(XWPFTable table, List<String[]> tableList) {
        //创建行,根据需要插入的数据添加新行,不处理表头
        for (int i = 1; i < tableList.size(); i++) {
            XWPFTableRow row = table.createRow();
        }
        //遍历表格插入数据
        List<XWPFTableRow> rows = table.getRows();
        for (int i = 1; i < rows.size(); i++) {
            XWPFTableRow newRow = table.getRow(i);
            List<XWPFTableCell> cells = newRow.getTableCells();
            for (int j = 0; j < cells.size(); j++) {
                XWPFTableCell cell = cells.get(j);
                cell.setText(tableList.get(i - 1)[j]);
            }
        }
    }

    /**
     * 判断文本中时候包含$
     *
     * @param text 文本
     * @return 包含返回true, 不包含返回false
     */
    public static boolean checkText(String text) {
        boolean check = false;
        if (text.indexOf("$") != -1) {
            check = true;
        }
        return check;
    }

    /**
     * 匹配传入信息集合与模板
     *
     * @param value   模板需要替换的区域
     * @param textMap 传入信息集合
     * @return 模板需要替换区域信息集合对应值
     */
    public static String changeValue(String value, Map<String, String> textMap) {
        Set<Map.Entry<String, String>> textSets = textMap.entrySet();
        for (Map.Entry<String, String> textSet : textSets) {
            //匹配模板与替换值 格式${key}
            String key = "${" + textSet.getKey() + "}";
            if (value.indexOf(key) != -1) {
                value = textSet.getValue();
            }
        }
        //模板未匹配到区域替换为空
        if (checkText(value)) {
            value = "";
        }
        return value;
    }


    public static void main(String[] args) {
        //模板文件地址
        String inputUrl = "model.docx";
        //新生产的模板文件
        String outputUrl = "new.docx";

        Map<String, String> testMap = new HashMap<String, String>();
        testMap.put("name", "小明");
        testMap.put("sex", "男");
        testMap.put("address", "软件园");
        testMap.put("phone", "88888888");

        List<String[]> testList = new ArrayList<String[]>();
        testList.add(new String[]{"1", "1AA", "1BB", "1CC"});
        testList.add(new String[]{"2", "2AA", "2BB", "2CC"});
        testList.add(new String[]{"3", "3AA", "3BB", "3CC"});
        testList.add(new String[]{"4", "4AA", "4BB", "4CC"});

        WordTemplateUtil.changWord(inputUrl, outputUrl, testMap, testList);
    }

}
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐