利用docx4j完美导出word文档(标签替换、插入图片、生成表格)

最近公司让我实现一个利用原有word模板,导出word文档的功能模块,发现docx4j是个很不错的工具,但是之前从来没有用过,对此并不了解,于是上网查找相关资料,也是非常少之,于是便自己开始摸索。

1、原有word模板内容如下:

利用docx4j完美导出word文档(标签替换、插入图片、生成表格)_第1张图片

2、在想要插入数据的地方,插入书签,之后会在代码中获取书签并遍历插入内容。

利用docx4j完美导出word文档(标签替换、插入图片、生成表格)_第2张图片

3、直接上代码。

(1)首先我将word模板文件路径配置在config.properties文件中

soil_word_template=/template/soil-report.docx
soil_word_filename=soil-report.docx
(2)然后是controller层
/**
	 * 根据表名统计土壤墒情数据并导出word文档
	 * 
	 * @param table
	 *            表名
	 * @param cropDate
	 *            监测日期
	 * @param file
	 *            BASE64编码的图片文件
	 * @param request
	 * @param response
	 * @return
	 */
	@ApiOperation("根据表名统计土壤墒情数据并导出word文档")
	@RequestMapping(value = "/exportWordReport", method = RequestMethod.POST)
	@ApiImplicitParams({ @ApiImplicitParam(name = "table", value = "表名",required=false,dataType="query"), @ApiImplicitParam(name="cropDate",value="监测日期",required=false,dataType="query"),
		@ApiImplicitParam(name = "file", value = "BASE64编码的图片字符",required=false,dataType="query")})
	public void exportWordReport(String table, String cropDate, String file, HttpServletRequest request,
			HttpServletResponse response) {
		// soil_word_template=/template/soil-report.docx
		// soil_word_filename=soil-report.docx
		ServletOutputStream out = null;
		InputStream in = null;
		File resultFile = null;
		try {
			// 土壤墒情word文档模板路径
			String path = request.getServletContext().getRealPath("") + PropertyUtil.get("soil_word_template");
			String tempPath = request.getServletContext().getRealPath("");
			WordprocessingMLPackage wPackage = soilService.exportWordReport(table, cropDate, file, path);
			// 设置文件属性
			// wPackage.save(new File("C:\\Users\\admin\\Desktop\\" +
			// PropertyUtil.get("soil_word_filename")));
			String fileName = PropertyUtil.get("soil_word_filename");
			String msg = new String((fileName).getBytes("UTF-8"), "ISO8859-1");
			response.setContentType("application/msword");
			response.addHeader("Content-Disposition", "attachment;filename=" + msg);

			// response.setContentType("application/octet-stream");
			// response.setContentType("application/vnd.ms-excel");
			response.setCharacterEncoding("UTF-8");
			// response.addHeader("result", "ok");
			// response.setContentType("multipart/form-data");
			out = response.getOutputStream();
			String strDate = DateUtil.date2String(new Date(), DateUtil.DATE_FORMAT2);
			resultFile = new File(tempPath + "/tempfile", "soil-report(" + strDate + ").docx");
			wPackage.save(resultFile);
			in = new FileInputStream(resultFile);
			byte[] buffer = new byte[1024];
			int len = 0;
			while ((len = in.read(buffer)) > 0) {
				out.write(buffer, 0, len);
			}
			// wPackage.save(out);
			// wPackage.save(new File("C:\\Users\\admin\\Desktop\\",
			// "dddd1111.docx"));
		} catch (Exception e) {
			Log.error(e);
		} finally {
			try {
				if (out != null) {
					out.flush();
					out.close();
				}
				if (in != null) {
					in.close();
					if (resultFile != null && resultFile.exists() && resultFile.isFile()) {
						resultFile.delete();
					}
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

controller 层中需要注意的地方的是,最开始我用

wPackage.save(out);//直接输出到response的ServletOutPutStream流中,但是这种方式会报错,好像是socket异常之类的问题。

后来改成了代码中的方式,先将文件导出到项目src的webapp文件夹的tempfile文件夹中,然后再从此文件夹中读取该文件,并用out.write()方式输出便能成功执行导出word,前端紧接着下载该word即可。

                        int len = 0;
			while ((len = in.read(buffer)) > 0) {
				out.write(buffer, 0, len);
			}

(3)service层以及serviceImpl层代码

首先来看看咱们需要利用docx4j完美导出word文档(标签替换、插入图片、生成表格)_第3张图片

是SoilService层代码:

	/**SoilService层
	 * 根据表名进行统计并导出word报告文档
	 * 
	 * @param tableName 表名
	 * @param cropDate 监测日期
	 * @param file  base64编码的图片文件字符串
	 * @param path  word模板文件路径
	 * @return Map
	 */
	WordprocessingMLPackage exportWordReport(String tableName, String cropDate, String file, String path);

然后贴上SoilServiceImpl代码:

	/**
	 * 根据表名进行统计并导出word报告文档
	 * 
	 * @param tableName
	 * @param where
	 * @param file
	 * @return Map
	 */
	@Override
	public WordprocessingMLPackage exportWordReport(String tableName, String cropDate, String file, String path) {
		// TODO Auto-generated method stub

		Map resultMap = new HashMap();
		// DecimalFormat decimalFormat = new DecimalFormat("0.00");
		// 此处可能添加过滤非监测区的条件
		String where = " \"GRIDCODE\"<8 ";
		resultMap = findStatisticsDateByTableName(tableName, where);
		JSONArray jsonArray = (JSONArray) resultMap.get("data1");
		double totalArea = 0.0; // 有效监测区面积
		double tooMuchArea = 0.0; // 过多面积1
		double suitableArea = 0.0;// 适宜面积2
		double notEnoughArea = 0.0;// 不足面积3
		double lightDroughtArea = 0.0;// 轻旱面积4
		double middleDroughtArea = 0.0;// 中旱面积5
		double heavyDroughtArea = 0.0;// 重旱面积6
		double extremeDroughtArea = 0.0;// 极旱面积7
		// double notMonitorArea =0.0; //非监测区8

		double tooMuchRate = 0.0; // 过多面积占比1
		double suitableRate = 0.0;// 适宜面积2
		double notEnoughRate = 0.0;// 不足面积3
		double lightDroughtRate = 0.0;// 轻旱面积4
		double middleDroughtRate = 0.0;// 中旱面积5
		double heavyDroughtRate = 0.0;// 重旱面积6
		double extremeDroughtRate = 0.0;// 极旱面积7
		// double notMonitorRate =0.0; //非监测区8

		for (Object obj : jsonArray) {
			JSONObject jsonObject = (JSONObject) obj;
			String gridcode = jsonObject.getString("gridcode");
			double area = jsonObject.getDoubleValue("area");
			if ("1".equals(gridcode)) {
				tooMuchArea = area * Constant.I;
			} else if ("2".equals(gridcode)) {
				suitableArea = area * Constant.I;
			} else if ("3".equals(gridcode)) {
				notEnoughArea = area * Constant.I;
			} else if ("4".equals(gridcode)) {
				lightDroughtArea = area * Constant.I;
			} else if ("5".equals(gridcode)) {
				middleDroughtArea = area * Constant.I;
			} else if ("6".equals(gridcode)) {
				heavyDroughtArea = area * Constant.I;
			} else if ("7".equals(gridcode)) {
				extremeDroughtArea = area * Constant.I;
			}
			// }else if("8".equals(gridcode)){
			// notMonitorArea =area;
			// }
			totalArea += area * Constant.I;
		}
		if (totalArea != 0.0) {
			tooMuchRate = Double.valueOf(DECIMAL_FORMAT.format((tooMuchArea / totalArea) * 100));
			suitableRate = Double.valueOf(DECIMAL_FORMAT.format((suitableArea / totalArea) * 100));
			notEnoughRate = Double.valueOf(DECIMAL_FORMAT.format((notEnoughArea / totalArea) * 100));
			lightDroughtRate = Double.valueOf(DECIMAL_FORMAT.format((lightDroughtArea / totalArea) * 100));
			middleDroughtRate = Double.valueOf(DECIMAL_FORMAT.format((middleDroughtArea / totalArea) * 100));
			heavyDroughtRate = Double.valueOf(DECIMAL_FORMAT.format((heavyDroughtArea / totalArea) * 100));
			extremeDroughtRate = Double.valueOf(DECIMAL_FORMAT.format((extremeDroughtArea / totalArea) * 100));
		}
		Map map = new HashMap();

		map.put("cropDate", cropDate);
		map.put("totalArea", DECIMAL_FORMAT.format(totalArea));
		map.put("mTotalArea", DECIMAL_FORMAT.format(totalArea / Constant.I * Constant.M));
		map.put("tooMuchArea", DECIMAL_FORMAT.format(tooMuchArea));
		map.put("suitableArea", DECIMAL_FORMAT.format(suitableArea));
		map.put("notEnoughArea", DECIMAL_FORMAT.format(notEnoughArea));
		map.put("lightDroughtArea", DECIMAL_FORMAT.format(lightDroughtArea)); 
		map.put("middleDroughtArea", DECIMAL_FORMAT.format(middleDroughtArea));
		map.put("heavyDroughtArea", DECIMAL_FORMAT.format(heavyDroughtArea));
		map.put("extremeDroughtArea", DECIMAL_FORMAT.format(extremeDroughtArea));
		
		map.put("tooMuchAreaM", DECIMAL_FORMAT.format(tooMuchArea/ Constant.I * Constant.M));
		map.put("suitableAreaM", DECIMAL_FORMAT.format(suitableArea/ Constant.I * Constant.M));
		map.put("notEnoughAreaM", DECIMAL_FORMAT.format(notEnoughArea/ Constant.I * Constant.M));
		map.put("lightDroughtAreaM", DECIMAL_FORMAT.format(lightDroughtArea/ Constant.I * Constant.M)); 
		map.put("middleDroughtAreaM", DECIMAL_FORMAT.format(middleDroughtArea/ Constant.I * Constant.M));
		map.put("heavyDroughtAreaM", DECIMAL_FORMAT.format(heavyDroughtArea/ Constant.I * Constant.M));
		map.put("extremeDroughtAreaM", DECIMAL_FORMAT.format(extremeDroughtArea/ Constant.I * Constant.M));

		map.put("tooMuchRate", tooMuchRate);
		map.put("suitableRate", suitableRate);
		map.put("notEnoughRate", notEnoughRate);
		map.put("lightDroughtRate", lightDroughtRate);
		map.put("middleDroughtRate", middleDroughtRate);
		map.put("heavyDroughtRate", heavyDroughtRate);
		map.put("extremeDroughtRate", extremeDroughtRate);

		String sql = "SELECT *  from (SELECT \"PROVINCE\",\"CITY\",\"COUNTY\",\"TOWN\",\"CROPTYPE\",\"GRIDCODE\",SUM(\"AREA\") \"AREA\"  FROM \""
				+ tableName
				+ "\" WHERE \"GRIDCODE\"<8 AND \"GRIDCODE\"  IS NOT NULL group by \"PROVINCE\",\"CITY\",\"COUNTY\",\"TOWN\",\"CROPTYPE\",\"GRIDCODE\") A "
				+ "WHERE A.\"AREA\"!=0 AND A.\"AREA\" IS NOT NULL";
		String totalAreaSql = "SELECT SUM(\"AREA\") \"TOTALAREA\"  from (SELECT \"PROVINCE\",\"CITY\",\"COUNTY\",\"TOWN\",\"CROPTYPE\",\"GRIDCODE\",SUM(\"AREA\") \"AREA\"  FROM \""
				+ tableName
				+ "\" WHERE \"GRIDCODE\"<8 AND \"GRIDCODE\"  IS NOT NULL group by \"PROVINCE\",\"CITY\",\"COUNTY\",\"TOWN\",\"CROPTYPE\",\"GRIDCODE\") A "
				+ "WHERE A.\"AREA\"!=0 AND A.\"AREA\" IS NOT NULL";
		List list = getBySql(sql, null);
		double soilTotalArea = Double.valueOf(Objects.toString(getBySql(totalAreaSql, null).get(0), "0.0"))
				* Constant.M;
		List soilList = list.parallelStream().map(x -> coverProperty(x, soilTotalArea))
				.collect(Collectors.toList());
		map.put("table", soilList);
		// map.put("soilTotalArea", soilTotalArea);
		map.put("img", file);
		try {
			WordprocessingMLPackage wPackage = WordprocessingMLPackage.load(new FileInputStream(path));
			replaceContentByBookmark(wPackage, map);
			// wPackage.save(new File("C:\\Users\\admin\\Desktop\\" +
			// PropertyUtil.get("soil_word_filename")));
			return wPackage;
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (Docx4JException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}
/**
	 * 根据多个标签替换段落中的内容
	 * 
	 * @param map
	 */
	public void replaceContentByBookmark(WordprocessingMLPackage wPackage, Map map) {
		// 载入模板文件
		try {
			// 提取正文
			MainDocumentPart main = wPackage.getMainDocumentPart();
			Document doc = main.getContents();
			Body body = doc.getBody();
			// ObjectFactory factory = Context.getWmlObjectFactory();
			// 获取段落
			List paragraphs = body.getContent();
			// 提取书签并创建书签的游标
			RangeFinder rt = new RangeFinder("CTBookmark", "CTMarkupRange");
			new TraversalUtil(paragraphs, rt);

			// 遍历书签(在书签处插入文本内容和书签)
			for (CTBookmark bm : rt.getStarts()) {
				// 这儿可以对单个书签进行操作,也可以用一个map对所有的书签进行处理
				if (!bm.getName().equals(DocUtil.tableName)) {
					if (bm.getName().equals(DocUtil.imageBookMarkName)) { // 图片
						String file = (String) map.get(bm.getName());
						if (StringUtils.isNotBlank(file)) {
							// InputStream inputStream =file.getInputStream();
							DocUtil.addImage(wPackage, bm, file);
						}
					} else {
						DocUtil.replaceText(bm, map.get(bm.getName()));
					}
				} else { // 表格
					List list = (List) map.get(bm.getName());
					// 创建表格
					int rowIndex = 2; // 从第三行开始写起(表格从0开始)
					Tbl tbl = Doc4JUtil.createTable(wPackage, list.size() + rowIndex, 6);
					Doc4JUtil.mergeCellsHorizontal(tbl, 0, 0, 5);
					// 设置表格大标题
					P p = Doc4JUtil.createP("农作物墒情统计列表", "36");
					Doc4JUtil.setTcValue(tbl, 0, 0, p);
					// 设置表格副标题
					Doc4JUtil.setTcValue(tbl, 1, 0, "县区");
					Doc4JUtil.setTcValue(tbl, 1, 1, "乡镇");
					Doc4JUtil.setTcValue(tbl, 1, 2, "作物");
					Doc4JUtil.setTcValue(tbl, 1, 3, "墒情等级");
					Doc4JUtil.setTcValue(tbl, 1, 4, "面积(亩)");
					Doc4JUtil.setTcValue(tbl, 1, 5, "占比");

					Map> provinceCollect = list.stream()
							.collect(Collectors.groupingBy(x -> x.getProvince() == null ? "" : x.getProvince()));

					for (Entry> proEntry : provinceCollect.entrySet()) {

						Map> cityCollect = proEntry.getValue().stream()
								.collect(Collectors.groupingBy(x -> x.getCity() == null ? "" : x.getCity()));
						for (Entry> cityEntry : cityCollect.entrySet()) {
							Map> countyCollect = cityEntry.getValue().stream()
									.collect(Collectors.groupingBy(x -> x.getCounty() == null ? "" : x.getCounty()));

							for (Entry> countyEntry : countyCollect.entrySet()) {
								operateCounty(countyEntry, rowIndex, tbl);
								rowIndex += countyEntry.getValue().size();
							}

						}

					}
					Doc4JUtil.setTblAllJcAlign(tbl, JcEnumeration.CENTER);
					Doc4JUtil.setTblAllVAlign(tbl, STVerticalJc.CENTER);
					wPackage.getMainDocumentPart().addObject(tbl);
				}
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * 写入县区数据
	 * 
	 * @param countyEntry
	 * @param rowIndex
	 * @param tbl
	 */
	private void operateCounty(Entry> countyEntry, int rowIndex, Tbl tbl) {
		// TODO Auto-generated method stub
		// 获取每个entry的记录条数
		int beginRow = rowIndex;
		int size = countyEntry.getValue().size();
		// 处理县区
		// 合并单元格
		Doc4JUtil.mergeCellsVertically(tbl, 0, beginRow, beginRow + size);
		Doc4JUtil.setTcValue(tbl, beginRow, 0, countyEntry.getKey());
		// 处理镇
		Map> townCollect = countyEntry.getValue().stream()
				.collect(Collectors.groupingBy(x -> x.getTown() == null ? "" : x.getTown()));
		for (Entry> townEntry : townCollect.entrySet()) {
			operateTown(townEntry, beginRow, tbl);
			beginRow += townEntry.getValue().size();
		}
	}

	/**
	 * 写入镇数据
	 * 
	 * @param townEntry
	 * @param rowIndex
	 * @param tbl
	 */
	private void operateTown(Entry> townEntry, int rowIndex, Tbl tbl) {
		// TODO Auto-generated method stub
		// 获取每个entry的记录条数
		int beginRow = rowIndex;
		int size = townEntry.getValue().size();
		// 处理县区
		// 合并单元格
		Doc4JUtil.mergeCellsVertically(tbl, 1, beginRow, beginRow + size);
		Doc4JUtil.setTcValue(tbl, beginRow, 1, townEntry.getKey());
		Map> cropCollect = townEntry.getValue().stream().sorted((x, j) -> {
			return (int) (x.getGridcode() - j.getGridcode());
		}).collect(Collectors.groupingBy(x -> x.getCroptype() == null ? "" : x.getCroptype()));
		for (Entry> cropEntry : cropCollect.entrySet()) {
			operateCropType(cropEntry, beginRow, tbl);
			beginRow += cropEntry.getValue().size();
		}

	}

	/**
	 * 写入种植类型、土壤墒情等级,面积,占比
	 * 
	 * @param cropEntry
	 * @param rowIndex
	 * @param tbl
	 */
	private void operateCropType(Entry> cropEntry, int rowIndex, Tbl tbl) {
		// TODO Auto-generated method stub

		int beginRow = rowIndex;
		int size = cropEntry.getValue().size();
		// 处理县区
		// 合并单元格
		Doc4JUtil.mergeCellsVertically(tbl, 2, beginRow, beginRow + size);
		Doc4JUtil.setTcValue(tbl, beginRow, 2, cropEntry.getKey());
		List soilList = cropEntry.getValue();
		for (int i = 0; i < size; i++) {
			Doc4JUtil.setTcValue(tbl, beginRow + i, 3, gridCodeMap.get(soilList.get(i).getGridcode()));
			Doc4JUtil.setTcValue(tbl, beginRow + i, 4, String.valueOf(soilList.get(i).getArea()));
			Doc4JUtil.setTcValue(tbl, beginRow + i, 5, soilList.get(i).getRate());
		}
	} 
  
public SoilDtoEntityRate coverProperty(Object[] x, double totalSoilArea) {
		String rate = "0.00%";
		double area = Double.valueOf(Objects.toString(x[6], "0")) * Constant.M;
		area = Double.valueOf(DECIMAL_FORMAT.format(area));
		if (totalSoilArea != 0) {
			rate = DECIMAL_FORMAT.format((area / totalSoilArea) * 100) + "%";
		}
		return new SoilDtoEntityRate(Objects.toString(x[0], ""), Objects.toString(x[1], ""), Objects.toString(x[2], ""),
				Objects.toString(x[3], ""), Objects.toString(x[4], ""), Double.valueOf(Objects.toString(x[5], "0")),
				area, rate);
	}

接下来是以上业务代码中用到的工具类中的方法:

  /**
     * @Description:创建表格(默认水平居中,垂直居中)
     */
    public static Tbl createTable(WordprocessingMLPackage wordPackage, int rowNum,
                           int colsNum) throws Exception {
        colsNum = Math.max(1, colsNum);
        rowNum = Math.max(1, rowNum);
        int widthTwips = getWritableWidth(wordPackage);
        int colWidth = widthTwips / colsNum;
        int[] widthArr = new int[colsNum];
        for (int i = 0; i < colsNum; i++) {
            widthArr[i] = colWidth;
        }
        return createTable(rowNum, colsNum, widthArr);
    }

    /**
     * @Description:创建表格(默认水平居中,垂直居中)
     */
    public static Tbl createTable(int rowNum, int colsNum, int[] widthArr)
            throws Exception {
        colsNum = Math.max(1, Math.min(colsNum, widthArr.length));
        rowNum = Math.max(1, rowNum);
        Tbl tbl = new Tbl();
        StringBuffer tblSb = new StringBuffer();
        tblSb.append("");
        tblSb.append("");
        tblSb.append("");
        // 上边框
        tblSb.append("");
        tblSb.append("");
        // 左边框
        tblSb.append("");
        // 下边框
        tblSb.append("");
        // 右边框
        tblSb.append("");
        tblSb.append("");
        tblSb.append("");
        tblSb.append("");
        tblSb.append("");
        TblPr tblPr = null;
        tblPr = (TblPr) XmlUtils.unmarshalString(tblSb.toString());
        Jc jc = new Jc();
        // 单元格居中对齐
        jc.setVal(JcEnumeration.CENTER);
        tblPr.setJc(jc);

        tbl.setTblPr(tblPr);

        // 设定各单元格宽度
        TblGrid tblGrid = new TblGrid();
        tbl.setTblGrid(tblGrid);
        for (int i = 0; i < colsNum; i++) {
            TblGridCol gridCol = new TblGridCol();
            gridCol.setW(BigInteger.valueOf(widthArr[i]));
            tblGrid.getGridCol().add(gridCol);
        }
        // 新增行
        for (int j = 0; j < rowNum; j++) {
            Tr tr = new Tr();
            tbl.getContent().add(tr);
            // 列
            for (int i = 0; i < colsNum; i++) {
                Tc tc = new Tc();
                tr.getContent().add(tc);

                TcPr tcPr = new TcPr();
                TblWidth cellWidth = new TblWidth();
                cellWidth.setType("dxa");
                cellWidth.setW(BigInteger.valueOf(widthArr[i]));
                tcPr.setTcW(cellWidth);
                tc.setTcPr(tcPr);

                // 垂直居中
                setTcVAlign(tc, STVerticalJc.CENTER);
                P p = new P();
                PPr pPr = new PPr();
                pPr.setJc(jc);
                p.setPPr(pPr);
                R run = new R();
                p.getContent().add(run);
                tc.getContent().add(p);
            }
        }
        return tbl;
    }
  /**
     * @Description: 跨列合并
     */
    public static void mergeCellsHorizontal(Tbl tbl, int row, int fromCell, int toCell) {
        if (row < 0 || fromCell < 0 || toCell < 0) {
            return;
        }
        List trList = getTblAllTr(tbl);
        if (row > trList.size()) {
            return;
        }
        Tr tr = trList.get(row);
        List tcList = getTrAllCell(tr);
        for (int cellIndex = fromCell, len = Math
                .min(tcList.size() - 1, toCell); cellIndex <= len; cellIndex++) {
            Tc tc = tcList.get(cellIndex);
            TcPr tcPr = getTcPr(tc);
            HMerge hMerge = tcPr.getHMerge();
            if (hMerge == null) {
                hMerge = new HMerge();
                tcPr.setHMerge(hMerge);
            }
            if (cellIndex == fromCell) {
                hMerge.setVal("restart");
            } else {
                hMerge.setVal("continue");
            }
        }
    }
 /**
     * 插入图片
     * @param wPackage
     * @param bm
     * @param file
     */
	public static void addImage(WordprocessingMLPackage wPackage, CTBookmark bm, String file) {
		try {
			// 这儿可以对单个书签进行操作,也可以用一个map对所有的书签进行处理
			// 获取该书签的父级段落
			P p = (P) (bm.getParent());
			// R对象是匿名的复杂类型,然而我并不知道具体啥意思,估计这个要好好去看看ooxml才知道
			R run = factory.createR();
			// 读入图片并转化为字节数组,因为docx4j只能字节数组的方式插入图片
	        byte[] bytes = FileUtil.getByteFormBase64DataByImage(file);

		//	InputStream is = new FileInputStream;
		//	byte[] bytes = IOUtils.toByteArray(inputStream);
			// byte[] bytes = FileUtil.getByteFormBase64DataByImage("");
			// 开始创建一个行内图片
			BinaryPartAbstractImage imagePart = BinaryPartAbstractImage.createImagePart(wPackage, bytes);
			// createImageInline函数的前四个参数我都没有找到具体啥意思,,,,
			// 最有一个是限制图片的宽度,缩放的依据
			Inline inline = imagePart.createImageInline(null, null, 0, 1, false, 0);
			// 获取该书签的父级段落
			// drawing理解为画布?
			Drawing drawing = factory.createDrawing();
			drawing.getAnchorOrInline().add(inline);
			run.getContent().add(drawing);
			p.getContent().add(run);
		} catch (Exception e) {
			// TODO: handle exception
			Log.error(e);
		}
	}

/**
	 * 在标签处插入内容
	 * 
	 * @param bm
	 * @param wPackage
	 * @param object
	 * @throws Exception
	 */
	public static void replaceText(CTBookmark bm,  Object object) throws Exception {
		if (object == null) {
			return;
		}
		// do we have data for this one?
		if (bm.getName() == null)
			return;
		String value = object.toString();
		Log.info("标签名称:"+bm.getName());
		try {
			// Can't just remove the object from the parent,
			// since in the parent, it may be wrapped in a JAXBElement
			List theList = null;
			ParaRPr rpr = null;
			if (bm.getParent() instanceof P) {
				PPr pprTemp = ((P) (bm.getParent())).getPPr();
				if (pprTemp == null) {
					rpr = null;
				} else {
					rpr = ((P) (bm.getParent())).getPPr().getRPr();
				}
				theList = ((ContentAccessor) (bm.getParent())).getContent();
			} else {
				return;
			}
			int rangeStart = -1;
			int rangeEnd = -1;
			int i = 0;
			for (Object ox : theList) {
				Object listEntry = XmlUtils.unwrap(ox);
				if (listEntry.equals(bm)) {

					if (((CTBookmark) listEntry).getName() != null) {

						rangeStart = i + 1;

					}
				} else if (listEntry instanceof CTMarkupRange) {
					if (((CTMarkupRange) listEntry).getId().equals(bm.getId())) {
						rangeEnd = i - 1;

						break;
					}
				}
				i++;
			}
			int x = i - 1;
			//if (rangeStart > 0 && x >= rangeStart) {
				// Delete the bookmark range
				for (int j = x; j >= rangeStart; j--) {
					theList.remove(j);
				}
				// now add a run
				org.docx4j.wml.R run = factory.createR();
				org.docx4j.wml.Text t = factory.createText();
				// if (rpr != null)
				// run.setRPr(paraRPr2RPr(rpr));
				t.setValue(value);
				run.getContent().add(t);
				//t.setValue(value);

				theList.add(rangeStart, run);
			//}
		} catch (ClassCastException cce) {
			Log.error(cce);
		}
	} 
  
 /**
     * @Description: 跨行合并
     */
    public static void mergeCellsVertically(Tbl tbl, int col, int fromRow, int toRow) {
        if (col < 0 || fromRow < 0 || toRow < 0) {
            return;
        }
        for (int rowIndex = fromRow; rowIndex <= toRow; rowIndex++) {
            Tc tc = getTc(tbl, rowIndex, col);
            if (tc == null) {
                break;
            }
            TcPr tcPr = getTcPr(tc);
            VMerge vMerge = tcPr.getVMerge();
            if (vMerge == null) {
                vMerge = new VMerge();
                tcPr.setVMerge(vMerge);
            }
            if (rowIndex == fromRow) {
                vMerge.setVal("restart");
            } else {
                vMerge.setVal("continue");
            }
        }
    }

至此,大家就可以看到导出的word文档效果了,如下图:

利用docx4j完美导出word文档(标签替换、插入图片、生成表格)_第4张图片

最后,大家在遇到此类问题的时候也可参考本篇博客,同时本人初次发表博客,时间仓促,尚且还有不妥之处,欢迎指正!

你可能感兴趣的:(javaweb)