|
|
@@ -0,0 +1,656 @@
|
|
|
+package com.zjrs.appcomm.template.word.util;
|
|
|
+
|
|
|
+import com.zjrs.appcomm.template.word.exception.IllegalMarkException;
|
|
|
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
|
|
|
+import org.apache.poi.xwpf.usermodel.*;
|
|
|
+import org.apache.xmlbeans.XmlCursor;
|
|
|
+import org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObject;
|
|
|
+import org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObjectData;
|
|
|
+import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps;
|
|
|
+import org.openxmlformats.schemas.drawingml.x2006.picture.CTPicture;
|
|
|
+import org.openxmlformats.schemas.drawingml.x2006.picture.CTPictureNonVisual;
|
|
|
+import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTInline;
|
|
|
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDrawing;
|
|
|
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR;
|
|
|
+import org.springframework.util.ObjectUtils;
|
|
|
+import org.w3c.dom.NamedNodeMap;
|
|
|
+import org.w3c.dom.Node;
|
|
|
+
|
|
|
+import java.lang.reflect.InvocationTargetException;
|
|
|
+import java.lang.reflect.Method;
|
|
|
+import java.util.*;
|
|
|
+
|
|
|
+public class POIUtil {
|
|
|
+
|
|
|
+ /** mark的开始标记 */
|
|
|
+ public static final String MARK_PREFIX = "${";
|
|
|
+ /** mark的伪属性分隔标记 */
|
|
|
+ public static final String MARK_PSEUDO = ":";
|
|
|
+ /** mark的属性分割标记 */
|
|
|
+ public static final String MARK_ATTRIBUTE = ".";
|
|
|
+ /** mark的结束标记 */
|
|
|
+ public static final String MARK_POSTFIX = "}";
|
|
|
+
|
|
|
+ public enum Pseudo {
|
|
|
+ BEGIN, END, ITEM, INDEX;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static void handleDocument(XWPFDocument document, Object data) {
|
|
|
+ List<IBodyElement> sortedParagraphAndTable = new ArrayList<>(document.getBodyElements());
|
|
|
+
|
|
|
+ List<Integer> indexList = new ArrayList<>();
|
|
|
+
|
|
|
+ replaceBodyElementsMarks(sortedParagraphAndTable, data, indexList);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static final int RETURN_ACTION_OK = 0;
|
|
|
+ private static final int RETURN_ACTION_REPEAT = 1;
|
|
|
+ private static final int RETURN_ACTION_OVER = 2;
|
|
|
+
|
|
|
+ private static int replaceBodyElementsMarks(List<IBodyElement> sortedParagraphAndTable, Object data, List<Integer> indexList) {
|
|
|
+ for (int i = 0; i < sortedParagraphAndTable.size(); i++) {
|
|
|
+ IBodyElement paragraphOrTable = sortedParagraphAndTable.get(i);
|
|
|
+ int action = replaceBodyElementMarks(paragraphOrTable, data, indexList);
|
|
|
+ if (action == RETURN_ACTION_REPEAT) {
|
|
|
+ int endIndex = findRepeatBodyElements(sortedParagraphAndTable, i);
|
|
|
+ List<IBodyElement> repeatBodyElements = new ArrayList<>();
|
|
|
+ for (int j = endIndex; j >= i; j--) {
|
|
|
+ repeatBodyElements.add(0, sortedParagraphAndTable.remove(j));
|
|
|
+ }
|
|
|
+ XWPFParagraph beginParagraph = (XWPFParagraph) repeatBodyElements.remove(0);
|
|
|
+ XWPFParagraph endParagraph = (XWPFParagraph) repeatBodyElements.remove(repeatBodyElements.size() - 1);
|
|
|
+
|
|
|
+ indexList.add(0);
|
|
|
+ int repeatIndexListI = indexList.size() - 1;
|
|
|
+ while (true) {
|
|
|
+ List<IBodyElement> newBodyElements = copyBodyElements(repeatBodyElements, beginParagraph);
|
|
|
+ int repeatAction = replaceBodyElementsMarks(newBodyElements, data, indexList);
|
|
|
+ if (repeatAction == RETURN_ACTION_OVER) {
|
|
|
+ removeBodyElements(newBodyElements);
|
|
|
+ indexList.remove(indexList.size() - 1);
|
|
|
+ break;
|
|
|
+ } else {
|
|
|
+ indexList.set(repeatIndexListI, indexList.get(repeatIndexListI) + 1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ removeParagraph(endParagraph);
|
|
|
+ removeBodyElements(repeatBodyElements);
|
|
|
+ removeParagraph(beginParagraph);
|
|
|
+ } else if (action == RETURN_ACTION_OVER) {
|
|
|
+ removeBodyElements(sortedParagraphAndTable);
|
|
|
+ return RETURN_ACTION_OVER;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return RETURN_ACTION_OK;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void removeBodyElements(List<IBodyElement> bodyElements) {
|
|
|
+ for (int i = bodyElements.size() - 1; i >= 0; i--) {
|
|
|
+ IBodyElement bodyElement = bodyElements.get(i);
|
|
|
+ if (bodyElement instanceof XWPFParagraph) {
|
|
|
+ removeParagraph((XWPFParagraph) bodyElement);
|
|
|
+ } else if (bodyElement instanceof XWPFTable) {
|
|
|
+ removeTable((XWPFTable) bodyElement);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void removeParagraph(XWPFParagraph paragraph) {
|
|
|
+ XWPFDocument document = paragraph.getDocument();
|
|
|
+ for (int i = 0; i < document.getBodyElements().size(); i++) {
|
|
|
+ if (document.getBodyElements().get(i).equals(paragraph)) {
|
|
|
+ document.removeBodyElement(i);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void removeTable(XWPFTable table) {
|
|
|
+ XWPFDocument document = table.getBody().getXWPFDocument();
|
|
|
+ for (int i = 0; i < document.getBodyElements().size(); i++) {
|
|
|
+ if (document.getBodyElements().get(i).equals(table)) {
|
|
|
+ document.removeBodyElement(i);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static List<IBodyElement> copyBodyElements(List<IBodyElement> bodyElements, XWPFParagraph paragraph) {
|
|
|
+ List<IBodyElement> newBodyElements = new ArrayList<>();
|
|
|
+ for (IBodyElement bodyElement: bodyElements) {
|
|
|
+ if (bodyElement instanceof XWPFParagraph) {
|
|
|
+ newBodyElements.add(copyParagraph((XWPFParagraph) bodyElement, paragraph.getCTP().newCursor()));
|
|
|
+ } else if (bodyElement instanceof XWPFTable) {
|
|
|
+ newBodyElements.add(copyTable((XWPFTable) bodyElement, paragraph.getCTP().newCursor()));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return newBodyElements;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static XWPFParagraph copyParagraph(XWPFParagraph paragraph, XmlCursor cursor) {
|
|
|
+ XWPFParagraph newParagraph = paragraph.getDocument().insertNewParagraph(cursor);
|
|
|
+ if (paragraph.getCTP().getPPr() != null) {
|
|
|
+ newParagraph.getCTP().addNewPPr().set(paragraph.getCTP().getPPr().copy());
|
|
|
+ }
|
|
|
+ for (XWPFRun sourceRun: paragraph.getRuns()) {
|
|
|
+ XWPFRun targetRun = newParagraph.createRun();
|
|
|
+ targetRun.getCTR().addNewRPr().set(sourceRun.getCTR().getRPr().copy());
|
|
|
+ targetRun.setText(getText(sourceRun), 0);
|
|
|
+ }
|
|
|
+ return newParagraph;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static XWPFTable copyTable(XWPFTable table, XmlCursor cursor) {
|
|
|
+ XWPFDocument doc = table.getBody().getXWPFDocument();
|
|
|
+ XWPFTable newTable = doc.insertNewTbl(cursor);
|
|
|
+ newTable.removeRow(0);
|
|
|
+ newTable.getCTTbl().getTblPr().set(table.getCTTbl().getTblPr().copy());
|
|
|
+ for (int r = 0; r < table.getRows().size(); r++) {
|
|
|
+ XWPFTableRow row = table.getRows().get(r);
|
|
|
+ copyRow(newTable, row, r);
|
|
|
+ }
|
|
|
+ return newTable;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static XWPFTableRow copyRow(XWPFTable table, XWPFTableRow row, int pos) {
|
|
|
+ XWPFTableRow newRow = table.insertNewTableRow(pos);
|
|
|
+ if (row.getCtRow().getTrPr() != null) {
|
|
|
+ newRow.getCtRow().addNewTrPr().set(row.getCtRow().getTrPr().copy());
|
|
|
+ }
|
|
|
+ for (XWPFTableCell sourceCell: row.getTableCells()) {
|
|
|
+ XWPFTableCell targetCell = newRow.addNewTableCell();
|
|
|
+ targetCell.getCTTc().addNewTcPr().set(sourceCell.getCTTc().getTcPr().copy());
|
|
|
+ targetCell.removeParagraph(0);
|
|
|
+ for (XWPFParagraph sourceParagraph: sourceCell.getParagraphs()) {
|
|
|
+ XWPFParagraph targetParagraph = targetCell.addParagraph();
|
|
|
+ targetParagraph.getCTP().addNewPPr().set(sourceParagraph.getCTP().getPPr().copy());
|
|
|
+ for (XWPFRun sourceRun: sourceParagraph.getRuns()) {
|
|
|
+ XWPFRun targetRun = targetParagraph.createRun();
|
|
|
+ targetRun.getCTR().addNewRPr().set(sourceRun.getCTR().getRPr().copy());
|
|
|
+ targetRun.setText(getText(sourceRun), 0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return newRow;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static int findRepeatBodyElements(List<IBodyElement> sortedParagraphAndTable, int beginIndex) {
|
|
|
+ int subBeginNum = 0;
|
|
|
+ for (int i = beginIndex + 1; i < sortedParagraphAndTable.size(); i++) {
|
|
|
+ IBodyElement paragraphOrTable = sortedParagraphAndTable.get(i);
|
|
|
+ if (paragraphOrTable instanceof XWPFParagraph) {
|
|
|
+ Set<String> marks = POIUtil.findMarks((XWPFParagraph) paragraphOrTable);
|
|
|
+ for (String mark: marks) {
|
|
|
+ String[] markSeg = mark.split("\\" + MARK_ATTRIBUTE);
|
|
|
+ String[] markPse = markSeg[markSeg.length - 1].split(MARK_PSEUDO);
|
|
|
+ Pseudo pseudo = null;
|
|
|
+ if (markPse.length >= 2) {
|
|
|
+ try {
|
|
|
+ pseudo = Pseudo.valueOf(markPse[1].toUpperCase());
|
|
|
+ } catch (RuntimeException e) {
|
|
|
+ throw new IllegalMarkException("mark伪属性未知", mark, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (pseudo == Pseudo.BEGIN) {
|
|
|
+ subBeginNum++;
|
|
|
+ } else if (pseudo == Pseudo.END) {
|
|
|
+ if (subBeginNum == 0) {
|
|
|
+ return i;
|
|
|
+ } else {
|
|
|
+ subBeginNum--;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ throw new IllegalMarkException("mark伪属性BEGIN未配对", null);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static int replaceBodyElementMarks(IBodyElement paragraphOrTable, Object data, List<Integer> indexList) {
|
|
|
+ if (paragraphOrTable instanceof XWPFParagraph) {
|
|
|
+ int action = replaceParagraphMarks((XWPFParagraph) paragraphOrTable, data, indexList);
|
|
|
+ if (action == RETURN_ACTION_REPEAT) {
|
|
|
+ return RETURN_ACTION_REPEAT;
|
|
|
+ } else if (action == RETURN_ACTION_OVER) {
|
|
|
+ return RETURN_ACTION_OVER;
|
|
|
+ }
|
|
|
+ } else if (paragraphOrTable instanceof XWPFTable) {
|
|
|
+ replaceTableMarks((XWPFTable) paragraphOrTable, data, indexList);
|
|
|
+ }
|
|
|
+ return RETURN_ACTION_OK;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static int replaceParagraphMarks(XWPFParagraph paragraph, Object data, List<Integer> indexList) {
|
|
|
+ Set<String> marks = POIUtil.findMarks(paragraph);
|
|
|
+ for (String mark: marks) {
|
|
|
+ String[] markSeg = mark.split("\\" + MARK_ATTRIBUTE);
|
|
|
+ String[] markPse = markSeg[markSeg.length - 1].split(MARK_PSEUDO);
|
|
|
+ Pseudo pseudo = null;
|
|
|
+ if (markPse.length >= 2) {
|
|
|
+ try {
|
|
|
+ pseudo = Pseudo.valueOf(markPse[1].toUpperCase());
|
|
|
+ } catch (RuntimeException e) {
|
|
|
+ throw new IllegalMarkException("mark伪属性未知", mark, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (pseudo == Pseudo.BEGIN) {
|
|
|
+ return RETURN_ACTION_REPEAT;
|
|
|
+ } else if (pseudo == Pseudo.END) {
|
|
|
+ throw new IllegalMarkException("mark伪属性END未配对", mark);
|
|
|
+ } else {
|
|
|
+ try {
|
|
|
+ String d = reflectData(mark, data, indexList, String.class);
|
|
|
+ POIUtil.replaceOneText(paragraph, MARK_PREFIX + mark + MARK_POSTFIX, d);
|
|
|
+ } catch (NeedRepeat e) {
|
|
|
+ return RETURN_ACTION_REPEAT;
|
|
|
+ } catch (NoMoreData e) {
|
|
|
+ return RETURN_ACTION_OVER;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ for (XWPFRun run: paragraph.getRuns()) {
|
|
|
+ String mark = findPictureMark(run);
|
|
|
+ if (mark != null) {
|
|
|
+ byte[] d = reflectData(mark, data, indexList, byte[].class);
|
|
|
+ try {
|
|
|
+ replacePicture(run, d);
|
|
|
+ } catch (InvalidFormatException e) {
|
|
|
+ throw new RuntimeException(e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return RETURN_ACTION_OK;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void replaceTableMarks(XWPFTable table, Object data, List<Integer> indexList) {
|
|
|
+ int needDeleteRowTemplate = -1;
|
|
|
+ int needDeleteRowOver = -1;
|
|
|
+ for (int rowI = 0; rowI < table.getRows().size(); rowI++) {
|
|
|
+ XWPFTableRow row = table.getRows().get(rowI);
|
|
|
+ cellLine: for (XWPFTableCell cell: row.getTableCells()) {
|
|
|
+ for (XWPFParagraph paragraph: cell.getParagraphs()) {
|
|
|
+ int action = replaceParagraphMarks(paragraph, data, indexList);
|
|
|
+ if (action == RETURN_ACTION_REPEAT) {
|
|
|
+ needDeleteRowTemplate = rowI;
|
|
|
+ indexList.add(-1);
|
|
|
+ break cellLine;
|
|
|
+ } else if (action == RETURN_ACTION_OVER) {
|
|
|
+ needDeleteRowOver = rowI;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (needDeleteRowTemplate != -1 && needDeleteRowOver == -1) {
|
|
|
+ copyRow(table, table.getRows().get(needDeleteRowTemplate), rowI + 1);
|
|
|
+ indexList.add(indexList.remove(indexList.size() - 1) + 1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (needDeleteRowTemplate != -1) {
|
|
|
+ table.removeRow(needDeleteRowOver);
|
|
|
+ table.removeRow(needDeleteRowTemplate);
|
|
|
+ indexList.remove(indexList.size() - 1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static class NeedRepeat extends RuntimeException {
|
|
|
+ private static final long serialVersionUID = 7504615595418414701L;
|
|
|
+ }
|
|
|
+ private static class NoMoreData extends RuntimeException {
|
|
|
+ private static final long serialVersionUID = -3558032302829950554L;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static <E> E reflectData(String mark, Object data, List<Integer> indexList, Class<E> clazz) {
|
|
|
+ Object currentData = data;
|
|
|
+ int indexI = 0;
|
|
|
+ String[] markSeg = mark.split("\\" + MARK_ATTRIBUTE);
|
|
|
+ for (int i = 0; i < markSeg.length; i++) {
|
|
|
+ String markS = markSeg[i];
|
|
|
+ String[] markPse = markS.split(MARK_PSEUDO);
|
|
|
+ Pseudo pseudo = null;
|
|
|
+ if (markPse.length == 2) {
|
|
|
+ try {
|
|
|
+ pseudo = Pseudo.valueOf(markPse[1].toUpperCase());
|
|
|
+ } catch (RuntimeException e) {
|
|
|
+ throw new IllegalMarkException("mark伪属性未知", mark, e);
|
|
|
+ }
|
|
|
+ } else if (markPse.length > 2) {
|
|
|
+ throw new IllegalMarkException("mark伪属性个数过多", mark);
|
|
|
+ }
|
|
|
+ if (i == markSeg.length - 1) {
|
|
|
+ if (pseudo == null) {
|
|
|
+ if (String.class.equals(clazz)) {
|
|
|
+ return (E) reflectStringData(mark, markPse[0], currentData);
|
|
|
+ } else if (byte[].class.equals(clazz)) {
|
|
|
+ return (E) reflectByteArrayData(mark, markPse[0], currentData);
|
|
|
+ } else {
|
|
|
+ throw new IllegalArgumentException("" + clazz);
|
|
|
+ }
|
|
|
+ } else if (pseudo == Pseudo.INDEX) {
|
|
|
+ List<?> list = reflectListData(mark, markPse[0], currentData);
|
|
|
+ if (indexI >= indexList.size()) {
|
|
|
+ throw new NeedRepeat();
|
|
|
+ }
|
|
|
+ int listI = indexList.get(indexI);
|
|
|
+ if (listI >= list.size()) {
|
|
|
+ throw new NoMoreData();
|
|
|
+ } else {
|
|
|
+ if (String.class.equals(clazz)) {
|
|
|
+ return (E) ((listI + 1) + "");
|
|
|
+ } else if (byte[].class.equals(clazz)) {
|
|
|
+ throw new IllegalMarkException("该mark不应出现在图片中", mark);
|
|
|
+ } else {
|
|
|
+ throw new IllegalArgumentException("" + clazz);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else if (pseudo == Pseudo.ITEM) {
|
|
|
+ List<?> list = reflectListData(mark, markPse[0], currentData);
|
|
|
+ if (indexI >= indexList.size()) {
|
|
|
+ throw new NeedRepeat();
|
|
|
+ }
|
|
|
+ int listI = indexList.get(indexI);
|
|
|
+ if (listI >= list.size()) {
|
|
|
+ throw new NoMoreData();
|
|
|
+ } else {
|
|
|
+ if (String.class.equals(clazz)) {
|
|
|
+ return (E) ObjectUtils.getDisplayString(reflectListItemData(list, listI, mark));
|
|
|
+ } else if (byte[].class.equals(clazz)) {
|
|
|
+ return (E) reflectListItemData(list, listI, mark);
|
|
|
+ } else {
|
|
|
+ throw new IllegalArgumentException("" + clazz);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ throw new IllegalMarkException("mark伪属性错误", mark);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (pseudo == null) {
|
|
|
+ currentData = reflectData(mark, markPse[0], currentData);
|
|
|
+ if (currentData == null) {
|
|
|
+ if (String.class.equals(clazz)) {
|
|
|
+ return (E) "";
|
|
|
+ } else if (byte[].class.equals(clazz)) {
|
|
|
+ throw new IllegalMarkException("mark对应数据不能为null", mark);
|
|
|
+ } else {
|
|
|
+ throw new IllegalArgumentException("" + clazz);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else if (pseudo == Pseudo.INDEX) {
|
|
|
+ throw new IllegalMarkException("错误mark", mark);
|
|
|
+ } else if (pseudo == Pseudo.ITEM) {
|
|
|
+ List<?> list = reflectListData(mark, markPse[0], currentData);
|
|
|
+ int listI = -1;
|
|
|
+ if (indexI >= indexList.size()) {
|
|
|
+ throw new NeedRepeat();
|
|
|
+ } else {
|
|
|
+ listI = indexList.get(indexI);
|
|
|
+ }
|
|
|
+ if (listI >= list.size()) {
|
|
|
+ throw new NoMoreData();
|
|
|
+ } else {
|
|
|
+ currentData = reflectListItemData(list, listI, mark);
|
|
|
+ indexI++;
|
|
|
+ if (currentData == null) {
|
|
|
+ if (String.class.equals(clazz)) {
|
|
|
+ return (E) "";
|
|
|
+ } else if (byte[].class.equals(clazz)) {
|
|
|
+ throw new IllegalMarkException("mark对应数据不能为null", mark);
|
|
|
+ } else {
|
|
|
+ throw new IllegalArgumentException("" + clazz);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ throw new IllegalMarkException("mark伪属性错误", mark);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ throw new IllegalMarkException("mark遍历数据异常", mark);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static Object reflectListItemData(List<?> list, int index, String mark) {
|
|
|
+ try {
|
|
|
+ return list.get(index);
|
|
|
+ } catch (ArrayIndexOutOfBoundsException e) {
|
|
|
+ System.err.println("找不到指定ListItem数据: " + mark);
|
|
|
+ return MARK_PREFIX + mark + MARK_POSTFIX;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static String reflectStringData(String mark, String markS, Object data) {
|
|
|
+ return ObjectUtils.getDisplayString(reflectData(mark, markS, data));
|
|
|
+ }
|
|
|
+ private static byte[] reflectByteArrayData(String mark, String markS, Object data) {
|
|
|
+ return (byte[]) reflectData(mark, markS, data);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static List<?> reflectListData(String mark, String markS, Object data) {
|
|
|
+ Object d = reflectData(mark, markS, data);
|
|
|
+ if (d == null) {
|
|
|
+ return Collections.emptyList();
|
|
|
+ } else if (d instanceof List) {
|
|
|
+ return (List<?>) d;
|
|
|
+ } else {
|
|
|
+ throw new IllegalMarkException("mark对应数据类型异常", mark);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static Object reflectData(String mark, String markS, Object data) {
|
|
|
+ String getMethod = "get" + NameUtil.toFirstUpperCase(markS);
|
|
|
+ try {
|
|
|
+ Method method = data.getClass().getMethod(getMethod);
|
|
|
+ return method.invoke(data);
|
|
|
+ } catch (SecurityException | NoSuchMethodException e) {
|
|
|
+ throw new IllegalMarkException("无mark对应数据", mark, e);
|
|
|
+ } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
|
|
+ throw new IllegalMarkException("获取mark对应数据异常", mark, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public static String findPictureMark(XWPFRun run) {
|
|
|
+ List<XWPFPicture> pictures = run.getEmbeddedPictures();
|
|
|
+ if (pictures.isEmpty() || pictures.get(0) == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ CTPicture picture = pictures.get(0).getCTPicture();
|
|
|
+ if (picture == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ CTPictureNonVisual pictureNonVisual = picture.getNvPicPr();
|
|
|
+ if (pictureNonVisual == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ CTNonVisualDrawingProps nonVisualDrawingProps = pictureNonVisual.getCNvPr();
|
|
|
+ if (nonVisualDrawingProps == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ String name = nonVisualDrawingProps.getName();
|
|
|
+ if (name != null && name.startsWith(MARK_PREFIX) && name.endsWith(MARK_POSTFIX)) {
|
|
|
+ return name.substring(MARK_PREFIX.length(), name.length() - MARK_POSTFIX.length());
|
|
|
+ } else {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public static Set<String> findMarks(XWPFParagraph paragraph) {
|
|
|
+ Set<String> markSet = new HashSet<>();
|
|
|
+ String text = getText(paragraph);
|
|
|
+ int index = 0;
|
|
|
+ while (true) {
|
|
|
+ int preIndex = text.indexOf(MARK_PREFIX, index);
|
|
|
+ if (preIndex < 0) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ int postIndex = text.indexOf(MARK_POSTFIX, preIndex + MARK_PREFIX.length());
|
|
|
+ if (postIndex < 0) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ String mark = text.substring(preIndex + MARK_PREFIX.length(), postIndex);
|
|
|
+ preIndex = mark.lastIndexOf(MARK_PREFIX);
|
|
|
+ if (preIndex >= 0) {
|
|
|
+ mark = mark.substring(preIndex + MARK_PREFIX.length());
|
|
|
+ }
|
|
|
+ if (mark.length() > 0) {
|
|
|
+ markSet.add(mark);
|
|
|
+ }
|
|
|
+ index = postIndex + MARK_POSTFIX.length();
|
|
|
+ }
|
|
|
+ return markSet;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static boolean replaceOneText(XWPFParagraph paragraph, String key, String value) {
|
|
|
+ /* 没有可替换的数据时直接返回 */
|
|
|
+ String paragraphText = getText(paragraph);
|
|
|
+ int index = paragraphText.indexOf(key);
|
|
|
+ if (index < 0) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ List<XWPFRun> runs = paragraph.getRuns();
|
|
|
+ int runBeginIndex = -1; // 需要替换的字符串所在的起始run
|
|
|
+ int runBeginStrIndex = -1; // 需要替换你的字符串所在起始run的下标
|
|
|
+ int runEndIndex = -1; // 需要替换的字符串所在的结束run
|
|
|
+ int runEndStrIndex = -1; // 需要替换的字符串所在结束run的下标
|
|
|
+
|
|
|
+ int runIndexT = 0; // 遍历run的临时下标
|
|
|
+ int runStrLengthT = 0; // 当前共遍历的字符串长度
|
|
|
+ for (; runIndexT < runs.size(); runIndexT++) {
|
|
|
+ int lastRunStrLengthT = runStrLengthT;
|
|
|
+ runStrLengthT += getText(runs.get(runIndexT)).length();
|
|
|
+ /* 找到需要替换的字符串后记录其所在的位置 */
|
|
|
+ if (runStrLengthT > index) {
|
|
|
+ runBeginIndex = runIndexT;
|
|
|
+ runBeginStrIndex = index - lastRunStrLengthT;
|
|
|
+ /* 当起始run与结束run在同一个run时,则记录结束run的位置 */
|
|
|
+ if (runStrLengthT >= index + key.length()) {
|
|
|
+ runEndIndex = runIndexT;
|
|
|
+ runEndStrIndex = runBeginStrIndex + key.length();
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /* 当起始run与结束run不在同一个run时,继续进行寻找 */
|
|
|
+ if (runEndIndex == -1) {
|
|
|
+ for (runIndexT++; runIndexT < runs.size(); runIndexT++) {
|
|
|
+ int lastRunStrLengthT = runStrLengthT;
|
|
|
+ runStrLengthT += getText(runs.get(runIndexT)).length();
|
|
|
+ /* 找到需要替换的字符串的结束run的位置 */
|
|
|
+ if (runStrLengthT >= index + key.length()) {
|
|
|
+ runEndIndex = runIndexT;
|
|
|
+ runEndStrIndex = index + key.length() - lastRunStrLengthT;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (runBeginIndex == runEndIndex) {
|
|
|
+ /* 当需要替换的字符串的起始run与结束run在同一个run时,则直接进行替换 */
|
|
|
+ XWPFRun run = runs.get(runBeginIndex);
|
|
|
+ String runText = getText(run);
|
|
|
+ runText = runText.substring(0, runBeginStrIndex) + value + runText.substring(runEndStrIndex); // TODO: 替换换行数据时,第二行存在行首空格
|
|
|
+ run.setText(runText, 0);
|
|
|
+ } else {
|
|
|
+ /* 当需要替换的字符串的起始run与结束run不在同一个run时,则起始run进行替换,后续run均进行删除 */
|
|
|
+ XWPFRun runBegin = runs.get(runBeginIndex);
|
|
|
+ String runBeginText = getText(runBegin);
|
|
|
+ runBeginText = runBeginText.substring(0, runBeginStrIndex) + value; // TODO: 替换换行数据时,第二行存在行首空格
|
|
|
+ runBegin.setText(runBeginText, 0);
|
|
|
+
|
|
|
+ XWPFRun runEnd = runs.get(runEndIndex);
|
|
|
+ String runEndText = getText(runEnd);
|
|
|
+ runEndText = runEndText.substring(runEndStrIndex);
|
|
|
+ runEnd.setText(runEndText, 0);
|
|
|
+
|
|
|
+ /* 删除多于的run */
|
|
|
+ for (int i = runEndIndex - 1; i > runBeginIndex; i--) {
|
|
|
+ paragraph.removeRun(i);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void replacePicture(XWPFRun run, byte[] picture) throws InvalidFormatException {
|
|
|
+ String id = run.getDocument().addPictureData(picture, getPictureType(picture));
|
|
|
+
|
|
|
+ CTR ctr = run.getCTR();
|
|
|
+ if (ctr == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ List<CTDrawing> drawingList = ctr.getDrawingList();
|
|
|
+ if (drawingList.isEmpty() || drawingList.get(0) == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ List<CTInline> inlineList = drawingList.get(0).getInlineList();
|
|
|
+ if (inlineList.isEmpty() || inlineList.get(0) == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ CTGraphicalObject graphic = inlineList.get(0).getGraphic();
|
|
|
+ if (graphic == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ CTGraphicalObjectData graphicalData = graphic.getGraphicData();
|
|
|
+ if (graphicalData == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ Node graphicalDataNode = graphicalData.getDomNode();
|
|
|
+ if (graphicalDataNode == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ for (int graphicalDataChildIndex = 0; graphicalDataChildIndex < graphicalDataNode.getChildNodes().getLength(); graphicalDataChildIndex++) {
|
|
|
+ Node graphicalDataChild = graphicalDataNode.getChildNodes().item(graphicalDataChildIndex);
|
|
|
+ if (graphicalDataChild.getNodeName().equals("pic:pic")) {
|
|
|
+ for (int picChildIndex = 0; picChildIndex < graphicalDataChild.getChildNodes().getLength(); picChildIndex++) {
|
|
|
+ Node picChild = graphicalDataChild.getChildNodes().item(picChildIndex);
|
|
|
+ if (picChild.getNodeName().equals("pic:blipFill")) {
|
|
|
+ for (int blipFillChildIndex = 0; blipFillChildIndex < picChild.getChildNodes().getLength(); blipFillChildIndex++) {
|
|
|
+ Node blipFillChild = picChild.getChildNodes().item(blipFillChildIndex);
|
|
|
+ if (blipFillChild.getNodeName().equals("a:blip")) {
|
|
|
+ NamedNodeMap blipAttributes = blipFillChild.getAttributes();
|
|
|
+ Node blipAttributesNode = blipAttributes.getNamedItem("r:embed");
|
|
|
+ blipAttributesNode.setNodeValue(id);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static int getPictureType(byte[] b) {
|
|
|
+ if (b.length >= 8 && b[0] == (byte) 0x89 && b[1] == (byte) 0x50 && b[2] == (byte) 0x4E && b[3] == (byte) 0x47 && b[4] == (byte) 0x0D && b[5] == (byte) 0x0A && b[6] == (byte) 0x1A && b[7] == (byte) 0x0A) {
|
|
|
+ return Document.PICTURE_TYPE_PNG;
|
|
|
+ } else if (b.length >= 4 && b[0] == (byte) 0xff && b[1] == (byte) 0xd8 && b[2] == (byte) 0xff && b[3] == (byte) 0xd9) {
|
|
|
+ return Document.PICTURE_TYPE_JPEG;
|
|
|
+ } else if (b.length >= 4 && b[0] == (byte) 0xff && b[1] == (byte) 0xd8 && b[2] == (byte) 0xff && b[3] == (byte) 0xe0) {
|
|
|
+ return Document.PICTURE_TYPE_JPEG;
|
|
|
+ } else if (b.length >= 4 && b[0] == (byte) 0xff && b[1] == (byte) 0xd8 && b[2] == (byte) 0xff && b[3] == (byte) 0xe1) {
|
|
|
+ return Document.PICTURE_TYPE_JPEG;
|
|
|
+ } else if (b.length >= 4 && b[0] == (byte) 0xff && b[1] == (byte) 0xd8 && b[2] == (byte) 0xff && b[3] == (byte) 0xe8) {
|
|
|
+ return Document.PICTURE_TYPE_JPEG;
|
|
|
+ }else {
|
|
|
+ String bStr = "";
|
|
|
+ for (int i = 0; i < 8 && i < b.length; i++) {
|
|
|
+ bStr += b[i] + ",";
|
|
|
+ }
|
|
|
+ if (b.length > 16) {
|
|
|
+ bStr += "...,";
|
|
|
+ }
|
|
|
+ for (int i = Math.max(b.length - 8, 8); i < b.length; i++) {
|
|
|
+ bStr += b[i] + ",";
|
|
|
+ }
|
|
|
+ throw new IllegalArgumentException("不支持的图片文件类型: " + bStr);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static String getText(XWPFParagraph paragraph) {
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
+ for (XWPFRun run: paragraph.getRuns()) {
|
|
|
+ sb.append(getText(run));
|
|
|
+ }
|
|
|
+ return sb.toString();
|
|
|
+ // return paragraph.getText(); 为兼容旧版POI调整方法
|
|
|
+ }
|
|
|
+ private static String getText(XWPFRun run) {
|
|
|
+ // return run.text(); 为兼容旧版POI调整方法
|
|
|
+ return run.toString();
|
|
|
+ }
|
|
|
+}
|