/* * Copyright (c) 2018-2028, Chill Zhuang All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * Neither the name of the dreamlu.net developer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * Author: Chill 庄骞 (smallchill@163.com) */ package org.sxkj.gd.workorder.utils; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.util.Units; import org.apache.poi.xwpf.usermodel.ParagraphAlignment; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.apache.poi.xwpf.usermodel.XWPFParagraph; import org.apache.poi.xwpf.usermodel.XWPFRun; import org.apache.poi.xwpf.usermodel.XWPFTable; import org.apache.poi.xwpf.usermodel.XWPFTableCell; import org.apache.poi.xwpf.usermodel.XWPFTableRow; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblGrid; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblPr; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblWidth; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTcBorders; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTcPr; import org.openxmlformats.schemas.wordprocessingml.x2006.main.STBorder; import org.openxmlformats.schemas.wordprocessingml.x2006.main.STMerge; import org.openxmlformats.schemas.wordprocessingml.x2006.main.STTblWidth; import org.openxmlformats.schemas.wordprocessingml.x2006.main.STTextDirection; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springblade.core.tool.utils.StringUtil; import org.sxkj.gd.utils.GdGeoAddressUtil; import org.sxkj.gd.workorder.entity.GdPatrolTaskEntity; import org.sxkj.gd.workorder.entity.GdTaskResultEntity; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.math.BigInteger; import java.net.HttpURLConnection; import java.net.URL; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.Collections; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; public final class GdPatrolReportWordUtil { private static final Logger logger = LoggerFactory.getLogger(GdPatrolReportWordUtil.class); private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm"); private static final int CONNECT_TIMEOUT = 3000; private static final int READ_TIMEOUT = 8000; private static final ExecutorService RESOURCE_EXECUTOR = new ThreadPoolExecutor( 8, 16, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(200), new ThreadPoolExecutor.CallerRunsPolicy() ); private GdPatrolReportWordUtil() { } public static File generateReportFile(GdPatrolTaskEntity taskEntity, List resultList, String creatorName, String deptName) throws IOException, InvalidFormatException, InterruptedException, ExecutionException { String title = StringUtil.isBlank(taskEntity.getPatrolTaskName()) ? "巡查任务" : taskEntity.getPatrolTaskName(); List results = resultList == null ? Collections.emptyList() : resultList; File reportFile = createReportTempFile(taskEntity); try (XWPFDocument document = new XWPFDocument(); FileOutputStream fos = new FileOutputStream(reportFile)) { CompletableFuture> imageFuture = CompletableFuture.supplyAsync( () -> preloadImages(results), RESOURCE_EXECUTOR ); CompletableFuture> qrCodeFuture = CompletableFuture.supplyAsync( () -> preloadQrCodes(results), RESOURCE_EXECUTOR ); CompletableFuture> addressFuture = CompletableFuture.supplyAsync( () -> preloadAddresses(results), RESOURCE_EXECUTOR ); CompletableFuture.allOf(imageFuture, qrCodeFuture, addressFuture).join(); Map imageCache = imageFuture.get(); Map qrCodeCache = qrCodeFuture.get(); Map addressCache = addressFuture.get(); createTitle(document, title); createSectionHeader(document, "一、任务详情"); addTaskDetails(document, taskEntity, creatorName, deptName); int resultCount = results.size(); createSectionHeader(document, "二、巡查结果(" + resultCount + "件)"); addResultDetails(document, results, imageCache, qrCodeCache, addressCache); document.write(fos); return reportFile; } } private static File createReportTempFile(GdPatrolTaskEntity taskEntity) throws IOException { String taskNo = taskEntity != null ? taskEntity.getTaskNo() : null; String safePrefix = buildSafeFilePrefix(taskNo); return File.createTempFile(safePrefix, ".docx"); } private static String buildSafeFilePrefix(String taskNo) { String baseName = StringUtil.isBlank(taskNo) ? "gd_patrol_report" : taskNo; String safeName = baseName.replaceAll("[^a-zA-Z0-9_-]", "_"); if (safeName.length() < 3) { safeName = "gd_patrol_report"; } if (safeName.length() > 50) { safeName = safeName.substring(0, 50); } return safeName + "_"; } private static void addTaskDetails(XWPFDocument document, GdPatrolTaskEntity taskEntity, String creatorName, String deptName) { XWPFTable table = document.createTable(4, 2); table.setWidth("100%"); table.getCTTbl().addNewTblGrid().addNewGridCol().setW(BigInteger.valueOf(3000)); table.getCTTbl().getTblGrid().addNewGridCol().setW(BigInteger.valueOf(6000)); table.getCTTbl().addNewTblPr().addNewTblBorders(); setTableCellWithGrayBackground(table, 0, 0, "任务编号"); setTableCellWithSpacing(table, 0, 1, formatString(taskEntity.getTaskNo())); setTableCellWithGrayBackground(table, 1, 0, "创建人"); setTableCellWithSpacing(table, 1, 1, formatString(creatorName)); setTableCellWithGrayBackground(table, 2, 0, "所属部门"); setTableCellWithSpacing(table, 2, 1, formatString(deptName)); setTableCellWithGrayBackground(table, 3, 0, "任务时间"); Date taskTime = taskEntity.getExecuteTime() != null ? taskEntity.getExecuteTime() : taskEntity.getCreateTime(); setTableCellWithSpacing(table, 3, 1, formatDate(taskTime)); } private static void addResultDetails(XWPFDocument document, List results, Map imageCache, Map qrCodeCache, Map addressCache) throws IOException, InvalidFormatException { if (results == null || results.isEmpty()) { addDetailItem(document, "/"); return; } int totalRows = results.size() * 4 + (results.size() - 1); XWPFTable resultTable = document.createTable(totalRows, 3); CTTbl ctTbl = resultTable.getCTTbl(); CTTblGrid tblGrid = ctTbl.getTblGrid(); if (tblGrid == null) { tblGrid = ctTbl.addNewTblGrid(); } while (tblGrid.sizeOfGridColArray() < 3) { tblGrid.addNewGridCol(); } tblGrid.getGridColArray(0).setW(BigInteger.valueOf(600)); tblGrid.getGridColArray(1).setW(BigInteger.valueOf(1500)); tblGrid.getGridColArray(2).setW(BigInteger.valueOf(3900)); XWPFTableRow firstRow = resultTable.getRow(0); firstRow.getCell(0).setWidth("600"); firstRow.getCell(1).setWidth("1500"); firstRow.getCell(2).setWidth("3900"); CTTblPr tblPr = ctTbl.getTblPr(); if (tblPr == null) { tblPr = ctTbl.addNewTblPr(); } CTTblWidth tblWidth = tblPr.isSetTblW() ? tblPr.getTblW() : tblPr.addNewTblW(); tblWidth.setW(BigInteger.valueOf(6000)); tblWidth.setType(STTblWidth.DXA); int currentRow = 0; for (int i = 0; i < results.size(); i++) { GdTaskResultEntity result = results.get(i); String resultPrefix = String.format("%02d", i + 1); setTableCellWithSequence(resultTable, currentRow, 0, resultPrefix, 4); setTableCellWithBoldAndCenter(resultTable, currentRow, 1, "成果编号"); setTableCellWithSpacing(resultTable, currentRow, 2, formatString(result.getResultCode())); currentRow++; setTableCellWithBoldAndCenter(resultTable, currentRow, 1, "成果地址"); String address = getAddressFromCache(result, addressCache); setTableCellWithSpacing(resultTable, currentRow, 2, formatString(address)); currentRow++; setTableCellWithBoldAndCenter(resultTable, currentRow, 1, "成果定位"); XWPFTableCell navCell = resultTable.getRow(currentRow).getCell(2); addLocationInfoToCell(navCell, result, qrCodeCache); currentRow++; setTableCellWithBoldAndCenter(resultTable, currentRow, 1, "成果图片"); XWPFTableCell imageCell = resultTable.getRow(currentRow).getCell(2); addImageToCell(imageCell, result.getResultUrl(), imageCache); currentRow++; if (i < results.size() - 1) { createUnifiedSeparatorRow(resultTable, currentRow); currentRow++; } } } private static void createUnifiedSeparatorRow(XWPFTable table, int row) { XWPFTableCell cell0 = table.getRow(row).getCell(0); XWPFTableCell cell1 = table.getRow(row).getCell(1); XWPFTableCell cell2 = table.getRow(row).getCell(2); clearCellParagraphs(cell0); clearCellParagraphs(cell1); clearCellParagraphs(cell2); XWPFParagraph para0 = cell0.addParagraph(); XWPFParagraph para1 = cell1.addParagraph(); XWPFParagraph para2 = cell2.addParagraph(); para0.setSpacingBefore(80); para0.setSpacingAfter(80); para1.setSpacingBefore(80); para1.setSpacingAfter(80); para2.setSpacingBefore(80); para2.setSpacingAfter(80); cell0.setColor("C0C0C0"); cell1.setColor("C0C0C0"); cell2.setColor("C0C0C0"); setCellBorders(cell0, true, false, true, true); setCellBorders(cell1, false, false, true, true); setCellBorders(cell2, false, true, true, true); para0.createRun().setText(""); para1.createRun().setText(""); para2.createRun().setText(""); } private static void setCellBorders(XWPFTableCell cell, boolean left, boolean right, boolean top, boolean bottom) { CTTcPr tcPr = cell.getCTTc().getTcPr(); if (tcPr == null) { tcPr = cell.getCTTc().addNewTcPr(); } CTTcBorders borders = tcPr.isSetTcBorders() ? tcPr.getTcBorders() : tcPr.addNewTcBorders(); if (borders.getLeft() == null) { borders.addNewLeft(); } borders.getLeft().setVal(left ? STBorder.SINGLE : STBorder.NIL); if (borders.getRight() == null) { borders.addNewRight(); } borders.getRight().setVal(right ? STBorder.SINGLE : STBorder.NIL); if (borders.getTop() == null) { borders.addNewTop(); } borders.getTop().setVal(top ? STBorder.SINGLE : STBorder.NIL); if (borders.getBottom() == null) { borders.addNewBottom(); } borders.getBottom().setVal(bottom ? STBorder.SINGLE : STBorder.NIL); } private static void setTableCellWithSequence(XWPFTable table, int row, int col, String text, int rowSpan) { XWPFTableCell cell = table.getRow(row).getCell(col); clearCellParagraphs(cell); XWPFParagraph para = cell.addParagraph(); para.setAlignment(ParagraphAlignment.CENTER); cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER); para.setWordWrapped(false); if (cell.getCTTc().getTcPr() == null) { cell.getCTTc().addNewTcPr(); } cell.getCTTc().getTcPr().addNewNoWrap(); CTTcPr tcPr = cell.getCTTc().getTcPr(); if (tcPr.getTextDirection() == null) { tcPr.addNewTextDirection(); } tcPr.getTextDirection().setVal(STTextDirection.LR_TB); XWPFRun run = para.createRun(); run.setText(text); run.setBold(true); if (rowSpan > 1) { CTTcPr mergeTcPr = cell.getCTTc().getTcPr(); if (mergeTcPr == null) { mergeTcPr = cell.getCTTc().addNewTcPr(); } mergeTcPr.addNewVMerge().setVal(STMerge.RESTART); for (int i = 1; i < rowSpan; i++) { XWPFTableCell mergeCell = table.getRow(row + i).getCell(col); CTTcPr mergeCellPr = mergeCell.getCTTc().getTcPr(); if (mergeCellPr == null) { mergeCellPr = mergeCell.getCTTc().addNewTcPr(); } mergeCellPr.addNewVMerge().setVal(STMerge.CONTINUE); mergeCell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER); if (!mergeCell.getParagraphs().isEmpty()) { mergeCell.getParagraphs().get(0).setWordWrapped(false); } } } } private static void setTableCellWithBoldAndCenter(XWPFTable table, int row, int col, String text) { XWPFTableCell cell = table.getRow(row).getCell(col); clearCellParagraphs(cell); XWPFParagraph para = cell.addParagraph(); para.setAlignment(ParagraphAlignment.CENTER); cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER); para.setWordWrapped(false); if (cell.getCTTc().getTcPr() == null) { cell.getCTTc().addNewTcPr(); } cell.getCTTc().getTcPr().addNewNoWrap(); XWPFRun run = para.createRun(); run.setText(formatString(text)); run.setBold(true); } private static void setTableCellWithGrayBackground(XWPFTable table, int row, int col, String text) { XWPFTableCell cell = table.getRow(row).getCell(col); clearCellParagraphs(cell); XWPFParagraph para = cell.addParagraph(); para.setAlignment(ParagraphAlignment.CENTER); cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER); para.setWordWrapped(false); if (cell.getCTTc().getTcPr() == null) { cell.getCTTc().addNewTcPr(); } cell.getCTTc().getTcPr().addNewNoWrap(); XWPFRun run = para.createRun(); run.setText(formatString(text)); cell.setColor("D0D0D0"); } private static void setTableCellWithSpacing(XWPFTable table, int row, int col, String text) { XWPFTableCell cell = table.getRow(row).getCell(col); clearCellParagraphs(cell); XWPFParagraph para = cell.addParagraph(); cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER); para.setWordWrapped(false); if (cell.getCTTc().getTcPr() == null) { cell.getCTTc().addNewTcPr(); } cell.getCTTc().getTcPr().addNewNoWrap(); XWPFRun run = para.createRun(); String formattedText = (text != null && !"/".equals(text)) ? " " + text : formatString(text); run.setText(formattedText); } private static void addLocationInfoToCell(XWPFTableCell cell, GdTaskResultEntity result, Map qrCodeCache) throws IOException, InvalidFormatException { clearCellParagraphs(cell); XWPFParagraph para = cell.addParagraph(); cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER); para.setWordWrapped(false); para.setAlignment(ParagraphAlignment.LEFT); if (result.getLongitude() != null && result.getLatitude() != null) { String longitude = formatCoordinateValue(result.getLongitude()); String latitude = formatCoordinateValue(result.getLatitude()); if (StringUtil.isBlank(longitude) || StringUtil.isBlank(latitude)) { para.setAlignment(ParagraphAlignment.CENTER); para.createRun().setText("/"); return; } XWPFRun coordinateRun = para.createRun(); coordinateRun.setText(longitude + "," + latitude + " "); coordinateRun.setFontSize(10); String qrKey = longitude + "," + latitude; byte[] qrCodeData = qrCodeCache.get(qrKey); if (qrCodeData != null && qrCodeData.length > 0) { try { para.setIndentationLeft(100); para.setIndentationRight(100); para.setSpacingBefore(50); para.setSpacingAfter(50); XWPFRun qrRun = para.createRun(); qrRun.addPicture( new ByteArrayInputStream(qrCodeData), XWPFDocument.PICTURE_TYPE_PNG, "qrcode.png", Units.toEMU(80), Units.toEMU(80) ); } catch (Exception e) { logger.error("二维码插入失败,成果编号:{}", result.getResultCode(), e); XWPFRun errorRun = para.createRun(); errorRun.setText("(二维码生成失败)"); } } else { XWPFRun errorRun = para.createRun(); errorRun.setText("(二维码生成失败)"); } } else { para.setAlignment(ParagraphAlignment.CENTER); para.createRun().setText("/"); } } private static void addImageToCell(XWPFTableCell cell, String imageUrl, Map imageCache) throws IOException, InvalidFormatException { clearCellParagraphs(cell); XWPFParagraph para = cell.addParagraph(); para.setAlignment(ParagraphAlignment.CENTER); cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER); para.setWordWrapped(false); XWPFRun run = para.createRun(); if (StringUtil.isBlank(imageUrl)) { run.setText("(无图片)"); return; } byte[] imageData = imageCache.get(imageUrl); if (imageData == null || imageData.length == 0) { run.setText("(图片加载失败)"); return; } try { int format = findImageFormat(imageUrl); para.setSpacingBefore(150); para.setSpacingAfter(100); para.setIndentationLeft(50); para.setIndentationRight(50); run.addPicture(new ByteArrayInputStream(imageData), format, "image.tmp", Units.toEMU(320), Units.toEMU(180)); } catch (Exception e) { logger.error("图片插入失败,地址:{}", imageUrl, e); run.setText("(图片插入失败)"); } } private static Map preloadImages(List results) { Set uniqueUrls = new HashSet<>(); if (results != null) { results.stream() .map(GdTaskResultEntity::getResultUrl) .filter(url -> !StringUtil.isBlank(url)) .forEach(uniqueUrls::add); } if (uniqueUrls.isEmpty()) { return Collections.emptyMap(); } Map resultMap = new ConcurrentHashMap<>(); List> futures = uniqueUrls.stream() .map(url -> CompletableFuture.runAsync(() -> { try { byte[] data = downloadImage(url); if (data != null && data.length > 0) { resultMap.put(url, data); } } catch (Exception e) { logger.error("图片下载失败,地址:{}", url, e); } }, RESOURCE_EXECUTOR)) .collect(Collectors.toList()); try { CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join(); } catch (Exception e) { logger.error("等待图片下载完成时发生异常", e); } return resultMap; } private static Map preloadQrCodes(List results) { if (results == null || results.isEmpty()) { return Collections.emptyMap(); } List needQrCodeResults = results.stream() .filter(result -> result.getLongitude() != null && result.getLatitude() != null) .collect(Collectors.toList()); if (needQrCodeResults.isEmpty()) { return Collections.emptyMap(); } Map qrCodeCache = new ConcurrentHashMap<>(); List> futures = needQrCodeResults.stream() .map(result -> CompletableFuture.runAsync(() -> { String longitude = formatCoordinateValue(result.getLongitude()); String latitude = formatCoordinateValue(result.getLatitude()); if (StringUtil.isBlank(longitude) || StringUtil.isBlank(latitude)) { return; } String destination = StringUtil.isBlank(result.getResultCode()) ? "巡查结果" : result.getResultCode(); String navUrl = GdQrCodeUtil.generateAmapNavigationUrl(longitude, latitude, destination); if (StringUtil.isBlank(navUrl)) { return; } try { byte[] qrCode = GdQrCodeUtil.generateQrCodeImageBytes(navUrl, 100, 100); if (qrCode != null && qrCode.length > 0) { qrCodeCache.put(longitude + "," + latitude, qrCode); } } catch (Exception e) { logger.error("二维码生成失败,成果编号:{}", result.getResultCode(), e); } }, RESOURCE_EXECUTOR)) .collect(Collectors.toList()); try { CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join(); } catch (Exception e) { logger.error("等待二维码生成完成时发生异常", e); } return qrCodeCache; } private static Map preloadAddresses(List results) { if (results == null || results.isEmpty()) { return Collections.emptyMap(); } List needAddressResults = results.stream() .filter(result -> result.getLongitude() != null && result.getLatitude() != null) .collect(Collectors.toList()); if (needAddressResults.isEmpty()) { return Collections.emptyMap(); } Map addressCache = new ConcurrentHashMap<>(); List> futures = needAddressResults.stream() .map(result -> CompletableFuture.runAsync(() -> { String key = buildCoordinateKey(result.getLongitude(), result.getLatitude()); if (StringUtil.isBlank(key) || addressCache.containsKey(key)) { return; } String address = GdGeoAddressUtil.getFormattedAddress(result.getLongitude(), result.getLatitude()); if (!StringUtil.isBlank(address)) { addressCache.put(key, address); } }, RESOURCE_EXECUTOR)) .collect(Collectors.toList()); try { CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join(); } catch (Exception e) { logger.error("等待成果地址获取完成时发生异常", e); } return addressCache; } private static byte[] downloadImage(String urlString) throws IOException { URL url = new URL(urlString); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); try { conn.setConnectTimeout(CONNECT_TIMEOUT); conn.setReadTimeout(READ_TIMEOUT); conn.setRequestMethod("GET"); conn.setRequestProperty("User-Agent", "Mozilla/5.0"); conn.setInstanceFollowRedirects(false); int responseCode = conn.getResponseCode(); if (responseCode >= 300 && responseCode < 400) { String newUrl = conn.getHeaderField("Location"); if (!StringUtil.isBlank(newUrl)) { conn.disconnect(); return downloadImage(newUrl); } } if (responseCode != HttpURLConnection.HTTP_OK) { throw new IOException("图片请求失败,状态码:" + responseCode); } try (InputStream is = conn.getInputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream()) { byte[] buffer = new byte[16384]; int bytesRead; while ((bytesRead = is.read(buffer)) != -1) { baos.write(buffer, 0, bytesRead); } return baos.toByteArray(); } } finally { conn.disconnect(); } } private static void createTitle(XWPFDocument document, String text) { XWPFParagraph p = document.createParagraph(); p.setAlignment(ParagraphAlignment.CENTER); XWPFRun run = p.createRun(); run.setText(text); run.setBold(true); run.setFontSize(20); } private static void createSectionHeader(XWPFDocument document, String text) { XWPFParagraph p = document.createParagraph(); XWPFRun run = p.createRun(); run.setText(text); run.setBold(true); run.setFontSize(16); } private static void addDetailItem(XWPFDocument document, String text) { document.createParagraph().createRun().setText(text); } private static String formatString(String text) { return !StringUtil.isBlank(text) ? text : "/"; } private static String formatDate(Date date) { if (date == null) { return "/"; } return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime().format(DATE_TIME_FORMATTER); } private static String formatCoordinateValue(Double coordinate) { if (coordinate == null) { return ""; } return String.format(Locale.CHINA, "%.6f", coordinate); } private static String getAddressFromCache(GdTaskResultEntity result, Map addressCache) { if (result == null || addressCache == null || addressCache.isEmpty()) { return null; } String key = buildCoordinateKey(result.getLongitude(), result.getLatitude()); if (StringUtil.isBlank(key)) { return null; } return addressCache.get(key); } private static String buildCoordinateKey(Double longitude, Double latitude) { if (longitude == null || latitude == null) { return ""; } return formatCoordinateValue(longitude) + "," + formatCoordinateValue(latitude); } private static int findImageFormat(String url) { String cleanUrl = url; int queryIndex = url.indexOf("?"); if (queryIndex > -1) { cleanUrl = url.substring(0, queryIndex); } String lowerUrl = cleanUrl.toLowerCase(Locale.ROOT); if (lowerUrl.endsWith(".jpeg") || lowerUrl.endsWith(".jpg")) { return XWPFDocument.PICTURE_TYPE_JPEG; } if (lowerUrl.endsWith(".png")) { return XWPFDocument.PICTURE_TYPE_PNG; } if (lowerUrl.endsWith(".gif")) { return XWPFDocument.PICTURE_TYPE_GIF; } return XWPFDocument.PICTURE_TYPE_JPEG; } private static void clearCellParagraphs(XWPFTableCell cell) { int paragraphCount = cell.getParagraphs().size(); for (int i = paragraphCount - 1; i >= 0; i--) { cell.removeParagraph(i); } } }