1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > poi 读取word文字图片表格

poi 读取word文字图片表格

时间:2018-10-02 08:25:37

相关推荐

poi 读取word文字图片表格

Poi 读取word

介绍:

Apache POI 简介是用Java编写的免费开源的跨平台的 Java API,Apache POI提供API给Java程式对Microsoft Office(Excel、WORD、PowerPoint、Visio等)格式档案读和写的功能。POI为“Poor Obfuscation Implementation”的首字母缩写,意为“可怜的模糊实现”。官方主页: /index.html

API文档: /apidocs/index.html

maven引入:

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

word的格式:

可以将通过另存为为xml文件查看,因为poi读取也是根据此,方便理解api的作用

得到的结果:

在这里可以发现word中由段落组成,每个段落中包括很多run: <w:p> 代表paragraph<w:r> 代表run 其他的可以自己去探索,可以看到poi中的设置是与此对应的:

知道这两个就可以完成最基本的读取操作了。

太懒了,后续再继续整理吧,代码贴下面先:

PoiReadDocxText:

import org.apache.poi.openxml4j.exceptions.InvalidFormatException;import org.apache.poi.xwpf.usermodel.*;import org.apache.xmlbeans.XmlException;import org.openxmlformats.schemas.wordprocessingml.x.main.*;import org.w3c.dom.NamedNodeMap;import org.w3c.dom.Node;import org.w3c.dom.NodeList;import sun.misc.BASE64Encoder;import java.io.FileInputStream;import java.io.FileWriter;import java.io.IOException;import java.io.InputStream;import java.text.DecimalFormat;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;/*** @description: docx 的读取,包括格式* @author: xuzhijian xuzhijian1314@* @date: -04-13 17:53**/public class PoiReadDocxText3 {/*** poi获取的是缇(twip) 转换为 磅(pt)** @Author: xuzhijian xuzhijian1314@* @Date: /4/15 16:37*/public String convertTwToPt(String tw) {double pt = Double.parseDouble(tw) / 20;DecimalFormat df = new DecimalFormat("#.00");return df.format(pt);}/*** poi获取的是缇(twip) 转换为 像素(px)** @Author: xuzhijian xuzhijian1314@* @Date: /4/25 17:04*/public String convertTwToPx(String tw) {double px = Double.parseDouble(tw);DecimalFormat df = new DecimalFormat("#.00");// 比例测试出来的return df.format(px / 20 / 0.95);}/*** poi获取的是缇(twip) 转换为 像素(cm)** @Author: xuzhijian xuzhijian1314@* @Date: /5/12 10:52*/public String convertTwpToCm(String tw) {double Cm = Double.parseDouble(tw) / 567;DecimalFormat df = new DecimalFormat("#.00");return df.format(Cm);}/*** poi图片获取的是emu 转换为 像素(cm)** @Param: [tw]* @return: java.lang.String* @Author: xuzhijian xuzhijian1314@* @Date: /5/23 15:05*/public String converEmuToCm(String tw) {double Cm = Double.parseDouble(tw) / 635 / 1440 * 2.54;DecimalFormat df = new DecimalFormat("#.00");return df.format(Cm);}/*** 获取子节点** @Param: [node, nodeName]* @return: org.w3c.dom.Node* @Author: xuzhijian xuzhijian1314@* @Date: /5/30 11:48*/private Node getChildNode(Node node, String nodeName) {if (!node.hasChildNodes()) {return null;}NodeList childNodes = node.getChildNodes();for (int i = 0; i < childNodes.getLength(); i++) {Node childNode = childNodes.item(i);if (nodeName.equals(childNode.getNodeName())) {return childNode;}childNode = getChildNode(childNode, nodeName);if (childNode != null) {return childNode;}}return null;}/*** 在段落中 分run去读取信息** @Author: xuzhijian xuzhijian1314@* @Date: /4/15 16:45*/private String DefaultFontSizeAsDouble = "";private Map<String, String> map = new HashMap<String, String>();public void readPara(XWPFParagraph paragraph, List<Map<String, String>> ans, String num) throws IOException {//读取ParaRpr 也就是段落中的rpr 和run中的rpr需要稍作区分 一般是回车符CTParaRPr prPr = paragraph.getCTPPr().getRPr();if (prPr != null) {Map<String, String> prprInformation = new HashMap<>(); //用来存储try {CTHpsMeasure[] szArray = prPr.getSzArray();if (szArray.length > 0) {prprInformation.put("sz", Double.toString(Double.parseDouble(szArray[0].getVal().toString()) / 2));prprInformation.put("WhatRunIs", "enter");//属性设为回车ans.add(prprInformation);}} catch (Exception e) {throw e;}}// 每一个run进行读取List<XWPFRun> runsLists = paragraph.getRuns(); //获取段落中所有的runfor (int i = 0; i < runsLists.size(); i++) { // 遍历runXWPFRun run = runsLists.get(i);Map<String, String> runInformation = new HashMap<>();//保存run的信息if (run.getCTR().sizeOfLastRenderedPageBreakArray() > 0) { // 查看是否有分页符runInformation.put("PageBack", "True");} else {runInformation.put("PageBack", "False");}runInformation.put("WhatRunIs", "words");//此run是文字还是图片Node node = run.getCTR().getDomNode();Node drawingNode = getChildNode(node, "w:drawing"); // 有w:drawing为图片if (drawingNode != null) { //对图片进行处理Node extentNode = getChildNode(drawingNode, "wp:extent");NamedNodeMap extentAttrs = extentNode.getAttributes();runInformation.put("WhatRunIs", "picture");runInformation.put("pictureW", converEmuToCm(extentAttrs.getNamedItem("cx").getNodeValue()));//获取图片宽度runInformation.put("pictureH", converEmuToCm(extentAttrs.getNamedItem("cy").getNodeValue()));//获取图片高度//获取图片ridString runXmlText = run.getCTR().xmlText();int rIdIndex = runXmlText.indexOf("r:embed");if (rIdIndex == -1) {continue;}int rIdEndIndex = runXmlText.indexOf("/>", rIdIndex);String rIdText = runXmlText.substring(rIdIndex, rIdEndIndex);String id = rIdText.split("\"")[1];//runInformation.put("pictureD", map.get(id)); //根据rid获取图片base64值} else { //不是图片runInformation.put("text", num + run.getText(0));//保存text 文字信息num = "";//只有每一段开头才又编号,添加之后就作废Double FontSizeAsDouble = run.getFontSizeAsDouble(); //字体大小CTRPr rPr = run.getCTR().getRPr();try {runInformation.put("FontSizeAsDouble", Double.toString(FontSizeAsDouble));} catch (Exception e) {runInformation.put("FontSizeAsDouble", DefaultFontSizeAsDouble); //拿不到就使用全局默认值}try {runInformation.put("CharacterSpacing", convertTwpToCm(rPr.getSpacingArray(0).getVal().toString())); // 字符间距} catch (Exception e) {runInformation.put("CharacterSpacing", "0");}String fontName = run.getFontName(); //字体名称if (fontName == "" || fontName == null) {runInformation.put("FontName", "等线");} else {runInformation.put("FontName", run.getFontName());}runInformation.put("isBold", Boolean.toString(run.isBold())); //加粗runInformation.put("isItalic", Boolean.toString(run.isItalic()));//斜体}ans.add(runInformation); //把信息添加到ans}}//memory structure for storing the numbering level counterprivate Map<Integer, Map<Integer, Integer>> numIDLvlCnt = new HashMap<Integer, Map<Integer, Integer>>();//memory structure for storing the previous numbering levelprivate Map<Integer, Integer> numIDPrevNumIlv = new HashMap<Integer, Integer>();/*** 读取word文档** @Param: [fileName]* @return: java.util.List<java.util.List < java.util.Map < java.lang.String, java.lang.String>>>* @Author: xuzhijian xuzhijian1314@* @Date: /5/30 11:55*/public List<List<Map<String, String>>> readByParagraph(String fileName) throws IOException {InputStream in = new FileInputStream(fileName);XWPFDocument docx = new XWPFDocument(in);Map<String, String> paraTitleInformation = new HashMap<>(); // 存储段落信息List<Map<String, String>> ans = new ArrayList<>();//每一个段落的结果List<List<Map<String, String>>> fans = new ArrayList<>();//总的结果//整个文档的默认字体大小try {DefaultFontSizeAsDouble = docx.getStyles().getDefaultRunStyle().getFontSizeAsDouble().toString();} catch (Exception exception) {DefaultFontSizeAsDouble = "10.5";}//获取所有的图片(转为base64)和它的rid存到map中去List<XWPFPictureData> allPictures = docx.getAllPictures();for (XWPFPictureData xwpfPictureData : allPictures) {String id = xwpfPictureData.getParent().getRelationId(xwpfPictureData);byte[] data = xwpfPictureData.getData();BASE64Encoder encoder = new BASE64Encoder();map.put(id, data != null ? encoder.encode(data) : "");}//页面信息CTDocument1 ctdoc = docx.getDocument();paraTitleInformation.put("Top", convertTwpToCm(ctdoc.getBody().getSectPr().getPgMar().getTop().toString()));paraTitleInformation.put("Bottom", convertTwpToCm(ctdoc.getBody().getSectPr().getPgMar().getBottom().toString()));paraTitleInformation.put("Left", convertTwpToCm(ctdoc.getBody().getSectPr().getPgMar().getLeft().toString()));paraTitleInformation.put("Right", convertTwpToCm(ctdoc.getBody().getSectPr().getPgMar().getRight().toString()));paraTitleInformation.put("pageSizeH", convertTwpToCm(ctdoc.getBody().getSectPr().getPgSz().getH().toString()));paraTitleInformation.put("pageSizeW", convertTwpToCm(ctdoc.getBody().getSectPr().getPgSz().getW().toString()));ans.add(paraTitleInformation);fans.add(ans);List<IBodyElement> ibs = docx.getBodyElements(); //获取文档中的元素for (IBodyElement ib : ibs) { //遍历所有的元素BodyElementType bet = ib.getElementType(); //获取当前元素的类型if (bet == BodyElementType.TABLE) { //处理表格Map<String, String> paraInformation = new HashMap<>();List<Map<String, String>> ans1 = new ArrayList<>();XWPFTable table = (XWPFTable) ib;ReadWordTable readWordTable = new ReadWordTable();//调用之前的读取表格的类paraInformation.put("WhatAmI", "Table");paraInformation.put("table", readWordTable.readTable(table));ans1.add(paraInformation);fans.add(ans1);} else if (bet == BodyElementType.PARAGRAPH) {//处理段落XWPFParagraph para = (XWPFParagraph) ib;Map<String, String> paraInformation = new HashMap<>(); //保存段落信息List<Map<String, String>> ans1 = new ArrayList<>();CTPPr pPr = para.getCTP().getPPr();String num = ""; //用来存储编号PoiReadNum poiReadNum = new PoiReadNum();if (para.getNumID() != null) { //if paragraph has numberingnum = poiReadNum.getCurrentNumber(para, numIDLvlCnt, numIDPrevNumIlv); //调用读取大纲的方法}paraInformation.put("FirstLineIndent", convertTwpToCm(Integer.toString(para.getFirstLineIndent()))); //首行缩进paraInformation.put("Alignment", para.getAlignment().toString()); //对齐方式if (pPr != null) {CTSpacing pSpacing = pPr.getSpacing() != null ? pPr.getSpacing() : pPr.addNewSpacing();paraInformation.put("SpacingBetween", convertTwpToCm(pSpacing.getLine().toString())); //行距paraInformation.put("SpacingBefore", convertTwpToCm(pSpacing.getBefore().toString()));//段后paraInformation.put("SpacingAfter", convertTwpToCm(pSpacing.getAfter().toString()));//段前} else { //未设置就默认为0paraInformation.put("SpacingBetween", "0");paraInformation.put("SpacingBefore", "0");paraInformation.put("SpacingAfter", "0");}paraInformation.put("WhatAmI", "Paragraph");ans1.add(paraInformation);readPara(para, ans1, num);fans.add(ans1);} else if (bet == BodyElementType.CONTENTCONTROL) { //处理目录Map<String, String> paraInformation = new HashMap<>();List<Map<String, String>> ans1 = new ArrayList<>();XWPFSDT sdt = (XWPFSDT) ib;paraInformation.put("WhatAmI", "SDT");paraInformation.put("sdt", sdt.getContent().toString());//获取目录ans1.add(paraInformation);fans.add(ans1);}}return fans;}public static void main(String[] args) throws IOException, XmlException, InvalidFormatException {//String filepath = "D:\\work\\工作文档\\测试文件\\文档比对系统需求分析-杨鑫帅(1).docx";String filepath = "C:\\Users\\xuzhi\\Documents\\WeChat Files\\wxid_126ivvz875z622\\FileStorage\\File\\-05\\文档比对系统需求分析-杨鑫帅(1).docx";PoiReadDocxText3 poiReadDocxText = new PoiReadDocxText3();List<List<Map<String, String>>> ans = poiReadDocxText.readByParagraph(filepath);FileWriter fileWriter = new FileWriter("C:\\Users\\xuzhi\\Desktop\\1.txt");fileWriter.append(ans.toString());for(List<Map<String, String>> a:ans){System.out.println(a);}}}

ReadWordTable:

import org.apache.poi.xwpf.usermodel.*;import org.openxmlformats.schemas.wordprocessingml.x.main.CTDecimalNumber;import org.openxmlformats.schemas.wordprocessingml.x.main.CTTcPr;import java.io.FileInputStream;import java.io.IOException;import java.math.BigInteger;import java.util.ArrayList;import java.util.List;/*** @description: read word table* @author: xuzhijian xuzhijian1314@* @date: -03-18 18:03**/public class ReadWordTable {/*** 保存生成HTML时需要被忽略的单元格*/private List<String> omitCellsList = new ArrayList<>();/*** 生成忽略的单元格列表中的格式** @param row* @param col* @return*/public String generateOmitCellStr(int row, int col) {return row + ":" + col;}/*** 获取当前单元格的colspan(列合并)的列数** @param tcPr 单元格属性* @return*/public int getColspan(CTTcPr tcPr) {// 判断是否存在列合并CTDecimalNumber gridSpan = null;if ((gridSpan = tcPr.getGridSpan()) != null) { // 合并的起始列// 获取合并的列数BigInteger num = gridSpan.getVal();return num.intValue();} else { // 其他被合并的列或正常列return 1;}}/*** 获取当前单元格的rowspan(行合并)的行数** @param table 表格* @param row 行值* @param col 列值* @return*/public int getRowspan(XWPFTable table, int row, int col) {XWPFTableCell cell = table.getRow(row).getCell(col);// 正常独立单元格if (!isContinueRow(cell) && !isRestartRow(cell)) {return 1;}// 当前单元格的宽度int cellWidth = getCellWidth(table, row, col);// 当前单元格距离左侧边框的距离int leftWidth = getLeftWidth(table, row, col);// 用户保存当前单元格行合并的单元格数-1(因为不包含自身)List<Boolean> list = new ArrayList<>();getRowspan(table, row, cellWidth, leftWidth, list);return list.size() + 1;}private void getRowspan(XWPFTable table, int row, int cellWidth, int leftWidth,List<Boolean> list) {// 已达到最后一行if (row + 1 >= table.getNumberOfRows()) {return;}row = row + 1;int colsNum = table.getRow(row).getTableCells().size();// 因为列合并单元格可能导致行合并的单元格并不在同一列,所以从头遍历列,通过属性、宽度以及距离左边框间距来判断是否是行合并for (int i = 0; i < colsNum; i++) {XWPFTableCell testTable = table.getRow(row).getCell(i);// 是否为合并单元格的中间行(包括结尾行)if (isContinueRow(testTable)) {// 是被上一行单元格合并的单元格if (getCellWidth(table, row, i) == cellWidth&& getLeftWidth(table, row, i) == leftWidth) {list.add(true);// 被合并的单元格在生成html时需要忽略addOmitCell(row, i);// 去下一行继续查找getRowspan(table, row, cellWidth, leftWidth, list);break;}}}}/*** 判断是否是合并行的起始行单元格** @param tableCell* @return*/public boolean isRestartRow(XWPFTableCell tableCell) {CTTcPr tcPr = tableCell.getCTTc().getTcPr();if (tcPr.getVMerge() == null) {return false;}if (tcPr.getVMerge().getVal() == null) {return false;}if (tcPr.getVMerge().getVal().toString().equalsIgnoreCase("restart")) {return true;}return false;}/*** 判断是否是合并行的中间行单元格(包括结尾的最后一行的单元格)** @param tableCell* @return*/public boolean isContinueRow(XWPFTableCell tableCell) {CTTcPr tcPr = tableCell.getCTTc().getTcPr();if (tcPr.getVMerge() == null) {return false;}if (tcPr.getVMerge().getVal() == null) {return true;}return false;}public int getLeftWidth(XWPFTable table, int row, int col) {int leftWidth = 0;for (int i = 0; i < col; i++) {leftWidth += getCellWidth(table, row, i);}return leftWidth;}public int getCellWidth(XWPFTable table, int row, int col) {BigInteger width = (BigInteger) table.getRow(row).getCell(col).getCTTc().getTcPr().getTcW().getW();return width.intValue();}/*** 添加忽略的单元格(被行合并的单元格,生成HTML时需要忽略)** @param row* @param col*/public void addOmitCell(int row, int col) {String omitCellStr = generateOmitCellStr(row, col);omitCellsList.add(omitCellStr);}public boolean isOmitCell(int row, int col) {String cellStr = generateOmitCellStr(row, col);return omitCellsList.contains(cellStr);}public String readTable(XWPFTable table) throws IOException {// 表格行数int tableRowsSize = table.getRows().size();StringBuilder tableToHtmlStr = new StringBuilder("<table border=\"1\"> ");for (int i = 0; i < tableRowsSize; i++) {tableToHtmlStr.append("<tr>");int tableCellsSize = table.getRow(i).getTableCells().size();for (int j = 0; j < tableCellsSize; j++) {if (isOmitCell(i, j)) {continue;}XWPFTableCell tableCell = table.getRow(i).getCell(j);// 获取单元格的属性CTTcPr tcPr = tableCell.getCTTc().getTcPr();int colspan = getColspan(tcPr);if (colspan > 1) { // 合并的列tableToHtmlStr.append("<td colspan='" + colspan + "'");} else { // 正常列tableToHtmlStr.append("<td colspan='' ");}int rowspan = getRowspan(table, i, j);if (rowspan > 1) { // 合并的行tableToHtmlStr.append(" rowspan='" + rowspan + "'>");} else {tableToHtmlStr.append("rowspan=''>");}String text = tableCell.getText();tableToHtmlStr.append(text + "</td>");}tableToHtmlStr.append("</tr>");}tableToHtmlStr.append("</table>");clearTableInfo();return tableToHtmlStr.toString();}public void clearTableInfo() {// System.out.println(omitCellsList);omitCellsList.clear();}public static void main(String[] args) {ReadWordTable readWordTable = new ReadWordTable();try (FileInputStream fileInputStream = new FileInputStream("D:\\work\\工作文档\\测试文件\\文档比对系统需求分析-杨鑫帅(1).docx");XWPFDocument document = new XWPFDocument(fileInputStream);) {List<IBodyElement> ibs = document.getBodyElements();for (IBodyElement ib : ibs){BodyElementType bet = ib.getElementType();if (bet == BodyElementType.TABLE){XWPFTable table = (XWPFTable) ib;System.out.println(readWordTable.readTable(table));}//else{//XWPFParagraph para = (XWPFParagraph) ib;//int suoJin1 = para.getFirstLineIndent();//int suoJin2 = para.getIndentationFirstLine();//List<XWPFRun> runs = para.getRuns();String text = "";for (XWPFRun run : runs){text += "text:"+run.getText(0)+" ";String Bold = Boolean.toString(run.isBold());//加粗text += "Bold:"+Bold+" ";}//String text = para.getText();//String Alignment = para.getAlignment().toString();////System.out.println(suoJin1+","+suoJin2+Alignment+text);//System.out.println(text);//}}} catch (IOException e) {e.printStackTrace();}}}

PoiReadNum:(读取列表编号)

/*** @description: read number list* @author: xuzhijian xuzhijian1314@* @date: -05-09 17:09**/import org.apache.poi.xwpf.usermodel.*;import java.util.Map;import java.util.HashMap;import java.math.BigInteger;public class PoiReadNum {//set numbering level counter for current numbering ID and numbering levelpublic void setNumIDLvlCnt(Integer numID, Integer numIlvl, Map<Integer, Map<Integer, Integer>> numIDLvlCnt,Map<Integer, Integer> numIDPrevNumIlv) {if (numID != null) {//get level counter for numbering IDMap<Integer, Integer> lvlCnt = numIDLvlCnt.get(numID);if (lvlCnt == null) { //if there is no level counter, create a new onelvlCnt = new HashMap<Integer, Integer>();numIDLvlCnt.put(numID, lvlCnt);}Integer prevNumIlv = numIDPrevNumIlv.get(numID);if (prevNumIlv == null) {prevNumIlv = 0;numIDPrevNumIlv.put(numID, prevNumIlv);}if (numIlvl != null) {//if this level is lower than the previous one, then all deeper level counters needs starting newif (numIlvl < prevNumIlv) {lvlCnt.keySet().removeIf(ilvl -> ilvl > numIlvl);}//get current counter for levelInteger cnt = lvlCnt.get(numIlvl);if (cnt == null) { //if there is no counter, set 0lvlCnt.put(numIlvl, 0);}cnt = lvlCnt.get(numIlvl);lvlCnt.put(numIlvl, cnt + 1); //count up 1prevNumIlv = numIlvl; //set this level to be the previous levelnumIDPrevNumIlv.put(numID, prevNumIlv);}}}//get formatted number from number format and level counterpublic String getNoFromCount(String numFmt, Integer cnt) {String no = "";if ("DECIMAL".equalsIgnoreCase(numFmt)) {no = String.valueOf(cnt);} else if ("LOWERLETTER".equalsIgnoreCase(numFmt)) {no = Character.toString((char) (96 + cnt)); //should be done better} else if ("LOWERROMAN".equalsIgnoreCase(numFmt)) {String[] romans = new String[]{"", "i", "ii", "iii", "iv", "v"};if (cnt < romans.length) no = romans[cnt]; //should be done better} else if ("UPPERROMAN".equalsIgnoreCase(numFmt)) {String[] romans = new String[]{"", "I", "II", "III", "IV", "V"};if (cnt < romans.length) no = romans[cnt]; //should be done better} //ToDo: else ...return no;}//get current number from paragraphpublic String getCurrentNumber(XWPFParagraph paragraph,Map<Integer, Map<Integer, Integer>> numIDLvlCnt,Map<Integer, Integer> numIDPrevNumIlv) {String no = "";BigInteger numStartOverride = paragraph.getNumStartOverride(); //ToDo: to take into account//System.out.println(numStartOverride);//get numbering formatString numFmt = paragraph.getNumFmt(); //decimal, lowerletter, roman, ..//get numbering IDBigInteger numID = paragraph.getNumID();//get current numbering levelBigInteger numIlvl = paragraph.getNumIlvl();//set numbering level counter for current numbering ID and numbering levelsetNumIDLvlCnt(numID.intValue(), numIlvl.intValue(),numIDLvlCnt,numIDPrevNumIlv);//get level counter for this numbering IDMap<Integer, Integer> lvlCnt = numIDLvlCnt.get(numID.intValue());//get numbering level textString numLevelText = paragraph.getNumLevelText(); // %1.%2.%3...no = numLevelText;for (Integer ilvl : lvlCnt.keySet()) {int i = ilvl + 1;//replace the placeholders %1, %2, %3, ... with formatted number from number format and level counterno = no.replace("%"+i, getNoFromCount(numFmt, lvlCnt.get(ilvl)));}return no;}}

封装成的格式:

List<List<Map<String,String>>>

最外面的List叫 L1吧

里面的List叫 L2

这样一个word:

读取的数据:

L2[0] 是页边距和滋长大小 单位是Cm

此后每一个L2 都是一个paragraph:

L2 里面的第一个map 是段落信息:短前段后,对其方式,行距信息。。。

接下来的其他map则是runs,word中run的划分是根据:属性一致就划分为一个run

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。