diff --git a/README.md b/README.md index 462ee04..199776e 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ -# document - +# document + 文书服务 -api下为调用的接口 +api下为调用的接口,在需要处理文书的项目中引用,以`fegin方式`配置Service后进行调用 -server下为使用jacob处理word文书,使用openoffice来进行pdf转换 +server下为使用jacob处理word文书,使用openoffice来进行pdf转换,`java -jar xxx.jar`启动 -tl-server下仅进行word转pdf,使用的docto调用office来转换 \ No newline at end of file +tl-server下仅进行word转pdf,使用的docto调用office来转换,`java -jar xxx.jar`启动 \ No newline at end of file diff --git a/api/pom.xml b/api/pom.xml new file mode 100644 index 0000000..cac84eb --- /dev/null +++ b/api/pom.xml @@ -0,0 +1,33 @@ + + + 4.0.0 + com.optima + document-api + 1.0.0 + + + + org.apache.maven.plugins + maven-compiler-plugin + + 8 + 8 + + + + + document api + 文档操作api + + 1.8 + + + + org.projectlombok + lombok + 1.16.20 + provided + + + diff --git a/api/src/main/java/com/optima/document/api/DocumentService.java b/api/src/main/java/com/optima/document/api/DocumentService.java new file mode 100644 index 0000000..8cc94d1 --- /dev/null +++ b/api/src/main/java/com/optima/document/api/DocumentService.java @@ -0,0 +1,71 @@ +package com.optima.document.api; + +import java.util.List; +import java.util.Map; + +/** + * 文档接口 + * @author Elias + * @date 2021-09-28 16:00 + */ +public interface DocumentService { + /** + * generate word + * @param templateData word模版流 + * @param dataModel 数据模型 + * @return 修改后的文档流 + */ + default byte[] generateWord(byte[] templateData, Map dataModel) { + throw new UnsupportedOperationException(); + } + + /** + * word to pdf + * @param templateData word模版流 + * @param clear 是否清除占位符 + * @return + */ + default byte[] wordToPdf(byte[] templateData, boolean clear) { + throw new UnsupportedOperationException(); + } + + /** + * word to image + * @param templateData word模版流 + * @param targetFormat 目标格式 支持jpeg, jpg, gif, tiff or png + * @return + */ + default byte[] wordToImage(byte[] templateData, String targetFormat) { + throw new UnsupportedOperationException(); + } + + /** + * + * @param source 文档 + * @param toFindText 需要替换的文本 + * @param imgSource 图片 + * @param width 宽度 + * @param height 高度 + * @return 修改后的文档 + */ + default byte[] insertJpeg(byte[] source, String toFindText, byte[] imgSource, int width, int height){ + throw new UnsupportedOperationException(); + } + + /** + * + * @param source + * @param toFindText + * @param imgSource + * @param width + * @param height + * @return + */ + default byte[] insertJpeg(byte[] source, String toFindText, List imgSource, int width, int height) { + throw new UnsupportedOperationException(); + } + + default byte[] fieldToWord(byte[] source, Map infoMap) { + throw new UnsupportedOperationException(); + } +} diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..b213655 --- /dev/null +++ b/pom.xml @@ -0,0 +1,17 @@ + + + 4.0.0 + com.optima + document + 1.0.0 + pom + document + 文档操作模块 + + api + server + tl-server + + + diff --git a/server/pom.xml b/server/pom.xml new file mode 100644 index 0000000..9af7075 --- /dev/null +++ b/server/pom.xml @@ -0,0 +1,135 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.0.3.RELEASE + + + com.optima + document-server + 1.0.0 + document server + 文档操作服务 + + 1.8 + + + + org.springframework.boot + spring-boot-starter-web + + + + com.optima + document-api + 1.0.0 + + + + org.apache.commons + commons-lang3 + 3.3.2 + + + + net.sf.jacob-project + jacob + 1.18 + system + ${basedir}/src/main/resources/libs/jacob.jar + + + com.artofsolving + jodconverter + 2.2.2 + system + ${basedir}/src/main/resources/libs/jodconverter-2.2.2.jar + + + + apache + poi2 + 1.0 + system + ${basedir}/src/main/resources/libs/poi2-1.0.jar + + + + org.apache.poi + poi + 3.14 + + + org.apache.poi + poi-ooxml + 3.14 + + + + org.apache.poi + poi-scratchpad + 3.14 + + + joda-time + joda-time + 2.1 + + + org.projectlombok + lombok + 1.16.20 + provided + + + org.springframework.boot + spring-boot-starter-test + test + + + org.jodconverter + jodconverter-core + 4.0.0-RELEASE + + + commons-io + commons-io + + + commons-lang3 + org.apache.commons + + + json + org.json + + + + + org.apache.commons + commons-io + 1.3.2 + + + org.apache.pdfbox + pdfbox-tools + 2.0.25 + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + true + + + + + + diff --git a/server/src/main/java/com/optima/document/server/DocumentServer.java b/server/src/main/java/com/optima/document/server/DocumentServer.java new file mode 100644 index 0000000..437ed56 --- /dev/null +++ b/server/src/main/java/com/optima/document/server/DocumentServer.java @@ -0,0 +1,13 @@ +package com.optima.document.server; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class DocumentServer { + + public static void main(String[] args) { + SpringApplication.run(DocumentServer.class, args); + } + +} diff --git a/server/src/main/java/com/optima/document/server/api/DocumentServiceImpl.java b/server/src/main/java/com/optima/document/server/api/DocumentServiceImpl.java new file mode 100644 index 0000000..cf2d7f9 --- /dev/null +++ b/server/src/main/java/com/optima/document/server/api/DocumentServiceImpl.java @@ -0,0 +1,82 @@ +package com.optima.document.server.api; + +import com.artofsolving.jodconverter.DefaultDocumentFormatRegistry; +import com.artofsolving.jodconverter.DocumentConverter; +import com.artofsolving.jodconverter.openoffice.connection.OpenOfficeConnection; +import com.artofsolving.jodconverter.openoffice.connection.SocketOpenOfficeConnection; +import com.artofsolving.jodconverter.openoffice.converter.OpenOfficeDocumentConverter; +import com.optima.document.api.DocumentService; +import com.optima.document.server.utils.DocToPdfUtil; +import lombok.extern.slf4j.Slf4j; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.rendering.ImageType; +import org.apache.pdfbox.rendering.PDFRenderer; +import org.apache.pdfbox.tools.imageio.ImageIOUtil; + +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.util.List; +import java.util.Map; + +/** + * 服务接口实现 + * @author Elias + * @date 2021-09-28 16:18 + */ +@Slf4j +public class DocumentServiceImpl implements DocumentService { + + public byte[] fieldToWord(byte[] source, Map infoMap) { + return DocToPdfUtil.fieldToWord(source, infoMap); + } + + public byte[] insertJpeg(byte[] source, String toFindText, byte[] imgSource, int width, int height) { + return DocToPdfUtil.insertJpeg(source, toFindText, imgSource, width, height); + } + + public byte[] insertJpeg(byte[] source, String toFindText, List imgSource, int width, int height) { + return DocToPdfUtil.insertJpeg(source, toFindText, imgSource, width, height); + } + + public byte[] wordToPdf(byte[] source, String sourceFormat, boolean clear) { + try { + if (clear) { + source = DocToPdfUtil.clearPlaceholder(source); + } + long t1 = System.currentTimeMillis(); + String command = "D:\\OpenOffice4\\program\\soffice.exe -headless -accept=\"socket,host=127.0.0.1,port=8100;urp;\" -nofirststartwizard"; + Process p = Runtime.getRuntime().exec(command); + OpenOfficeConnection connection = new SocketOpenOfficeConnection(); + connection.connect(); + DocumentConverter converter = new OpenOfficeDocumentConverter(connection); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + DefaultDocumentFormatRegistry formatRegistry = new DefaultDocumentFormatRegistry(); + converter.convert(new ByteArrayInputStream(source), formatRegistry.getFormatByFileExtension(sourceFormat), bos, formatRegistry.getFormatByFileExtension("pdf")); + connection.disconnect(); + p.destroy(); + log.info("word to pdf=======consuming:{} milliseconds", System.currentTimeMillis() - t1); + return bos.toByteArray(); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + public byte[] wordToImage(byte[] source, String sourceFormat, String targetFormat) { + try { + byte[] pdfBytes = wordToPdf(source, sourceFormat, true); + PDDocument document = PDDocument.load(new ByteArrayInputStream(pdfBytes)); + PDFRenderer pdfRenderer = new PDFRenderer(document); + BufferedImage bim = pdfRenderer.renderImageWithDPI( + 0, 300, ImageType.RGB); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ImageIOUtil.writeImage(bim, targetFormat, bos, 300); + document.close(); + return bos.toByteArray(); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/server/src/main/java/com/optima/document/server/config/DocumentConfig.java b/server/src/main/java/com/optima/document/server/config/DocumentConfig.java new file mode 100644 index 0000000..d997137 --- /dev/null +++ b/server/src/main/java/com/optima/document/server/config/DocumentConfig.java @@ -0,0 +1,37 @@ +package com.optima.document.server.config; + +import com.optima.document.api.DocumentService; +import com.optima.document.server.api.DocumentServiceImpl; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter; + +/** + * 服务端配置 + * @author Elias + * @date 2021-09-28 16:12 + */ +@Configuration +@ConfigurationProperties(prefix = "document") +@Data +public class DocumentConfig { + + private String tempDir; + + private String openOfficeHome; + + /** + * 文档接口 + * @return httpinvoker + */ + @Bean(name = "/document-service") + HttpInvokerServiceExporter wordService() { + HttpInvokerServiceExporter exporter = new HttpInvokerServiceExporter(); + exporter.setService( new DocumentServiceImpl() ); + exporter.setServiceInterface(DocumentService.class); + return exporter; + } + +} diff --git a/server/src/main/java/com/optima/document/server/utils/CastUtil.java b/server/src/main/java/com/optima/document/server/utils/CastUtil.java new file mode 100644 index 0000000..5192f39 --- /dev/null +++ b/server/src/main/java/com/optima/document/server/utils/CastUtil.java @@ -0,0 +1,493 @@ +package com.optima.document.server.utils; + + + +import org.apache.commons.lang3.StringUtils; +import org.joda.time.DateTime; + +import java.math.BigDecimal; +import java.text.DateFormat; +import java.text.DecimalFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Created by xianjun on 2016/6/17. + */ + +/** + * 转型操作工具类 + */ +public final class CastUtil { + + /** + * 转为String 型 默认为空 + */ + public static String castString(Object obj) { + return CastUtil.castString(obj, ""); + } + + /** + * 转为String 型(提供默认值) + */ + public static String castString(Object obj, String defaultValue) { + return obj != null ? String.valueOf(obj) : defaultValue; + } + + /** + * 转为double型 默认为0 + */ + public static double castDouble(Object obj) { + return CastUtil.castDouble(obj, 0); + } + + /** + * 转为double型(提供默认值) + */ + public static double castDouble(Object obj, double defaultValue) { + double doubleValue = defaultValue; + if (obj != null) { + String strValue = castString(obj); + if (StringUtils.isNotEmpty(strValue)) { + try { + doubleValue = Double.parseDouble(strValue); + } catch (NumberFormatException e) { + doubleValue = defaultValue; + } + } + } + return doubleValue; + } + + /** + * 转为long型,默认值为0 + */ + public static long castLong(Object obj) { + return CastUtil.castLong(obj, 0); + } + + /** + * 转换为long型(提供默认值) + */ + public static long castLong(Object obj, long defaultValue) { + long longValue = defaultValue; + if (obj != null) { + String strValue = castString(obj); + if (!StringUtils.isEmpty(strValue)) { + try { + longValue = Long.parseLong(strValue); + } catch (NumberFormatException e) { + longValue = defaultValue; + } + } + } + return longValue; + } + + /** + * 转为int型 默认值为0 + */ + public static int castInt(Object obj) { + return CastUtil.castInt(obj, 0); + } + + /** + * 转为int型(提供默认值) + */ + public static int castInt(Object obj, int defaultValue) { + int intValue = defaultValue; + if (obj != null) { + String strValue = castString(obj); + if (StringUtils.isNotEmpty(strValue)) { + try { + intValue = Integer.parseInt(strValue); + } catch (NumberFormatException e) { + intValue = defaultValue; + } + } + } + return intValue; + } + + /** + * 转为double型,默认值为false + */ + public static boolean castBoolean(Object obj) { + return CastUtil.castBoolean(obj, false); + } + + /** + * 转为double型,提供默认值 + */ + public static boolean castBoolean(Object obj, boolean defaultValue) { + boolean booleanValue = defaultValue; + if (obj != null) { + booleanValue = Boolean.parseBoolean(castString(obj)); + } + return booleanValue; + } + + /** + * 转时间戳为date类型 + * + * @param time + * @param defaultValue + * @return + */ + public static Date castDate(long time, Date defaultValue) { + Date dateValue = defaultValue; + if (time != 0) { + DateTime dateTime = new DateTime(time); + dateValue = dateTime.toDate(); + } + + return dateValue; + } + + public static Date castDateNo(long time) { + return castDate(time, new Date(0)); + } + + /** + * 转时间戳为date类型 + */ + public static Date castDate(long time) { + return castDate(time, new Date()); + } + + /** + * 获取开始时间 + * + * @Title: getStartTime + * @author: xianjun + * @Description: TODO + * @param date + * @param monthTime + * @param dateTime + * @param hour + * @return + * @throws + */ + public static Date getStartTime(Date date, int monthTime, int dateTime, int hour) { + Calendar calendar = Calendar.getInstance(); + calendar.set(CastUtil.castInt(CastUtil.castStringByDate(date, "yyyy")), date.getMonth() + monthTime, date.getDate() + dateTime, 0 + hour, 0, 0); + return calendar.getTime(); + } + + /** + * 获取结束时间 + * + * @Title: getEndTime + * @author: xianjun + * @Description: TODO + * @param date + * @param monthTime + * @param dateTime + * @param hour + * @return + * @throws + */ + public static Date getEndTime(Date date, int monthTime, int dateTime, int hour) { + Calendar calendar = Calendar.getInstance(); + calendar.set(CastUtil.castInt(CastUtil.castStringByDate(date, "yyyy")), date.getMonth() + monthTime, date.getDate() + dateTime, 23 + hour, 59, 59); + return calendar.getTime(); + } + + /** + * 转日期格式为字符串 + */ + public static String castStringDate(Date date, Date defaultValue) { + SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + Date dateValue = defaultValue; + String dateString = ""; + if (date != null) { + dateString = fmt.format(date); + } else { + dateString = fmt.format(dateValue); + } + + return dateString; + } + + /** + * 转换日期格式为年-月-日 + * @Title: castStringIntegerDate + * @author: xianjun + * @Description: TODO + * @param date + * @return + * @throws + */ + public static String castStringIntegerDate(Date date) { + return castStringIntegerDate(date, new Date()); + } + + /** + * 转换格式为年-月-日 + * @Title: castStringIntegerDate + * @author: xianjun + * @Description: TODO + * @param date + * @param defaultValue + * @return + * @throws + */ + public static String castStringIntegerDate(Date date, Date defaultValue) { + SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd"); + Date dateValue = defaultValue; + String dateString = ""; + if (date != null) { + dateString = fmt.format(date); + } else { + dateString = fmt.format(dateValue); + } + + return dateString; + } + + /** + * 根据传入的格式,转换为不同的日期 + * + * @Title: castStringByDate + * @author: xianjun + * @Description: TODO + * @param date + * @param pattern + * @return + * @throws + */ + public static String castStringByDate(Date date, String pattern) { + SimpleDateFormat fmt = new SimpleDateFormat(pattern); + Date dateValue = new Date(); + String dateString = ""; + if (date != null) { + dateString = fmt.format(date); + } else { + dateString = fmt.format(dateValue); + } + return dateString; + } + + /** + * 转日期格式为字符串 + */ + public static String castStringDate(Date date) { + return castStringDate(date, new Date()); + } + + /** + * 转 字符串为日期格式 + */ + public static Date castDateByString(String defaultDate){ + SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + try { + Date date = fmt.parse(defaultDate); + return date; + } catch (ParseException e) { + e.printStackTrace(); + return new Date(); + } + } + + /** + * 转BigDecimal为字符串 + */ + public static String castStringByDecimal(BigDecimal value, String pattern) { + DecimalFormat fmt = new DecimalFormat(pattern); + try { + String tempValue = fmt.format(value.stripTrailingZeros()); + return tempValue; + } catch (Exception e) { + // TODO: handle exception + e.printStackTrace(); + return "0"; + } + } + + /** + * 转换字符串,判断是否非空 + * @Title: castStringByString + * @author: xianjun + * @Description: TODO + * @param defaultString + * @return + * @throws + */ + public static String castStringByString(String defaultString) { + String value = defaultString == null ? "" : defaultString; + return value; + } + + /** + * 去除html标签 + * @Title: delHTMLTag + * @author: xianjun + * @Description: TODO + * @param htmlStr + * @return + * @throws + */ + public static String delHTMLTag(String htmlStr){ + String regEx_script="]*?>[\\s\\S]*?<\\/script>"; //定义script的正则表达式 + String regEx_style="]*?>[\\s\\S]*?<\\/style>"; //定义style的正则表达式 + String regEx_html="<[^>]+>"; //定义HTML标签的正则表达式 + + Pattern p_script=Pattern.compile(regEx_script,Pattern.CASE_INSENSITIVE); + Matcher m_script=p_script.matcher(htmlStr); + htmlStr=m_script.replaceAll(""); //过滤script标签 + + Pattern p_style=Pattern.compile(regEx_style,Pattern.CASE_INSENSITIVE); + Matcher m_style=p_style.matcher(htmlStr); + htmlStr=m_style.replaceAll(""); //过滤style标签 + + Pattern p_html=Pattern.compile(regEx_html,Pattern.CASE_INSENSITIVE); + Matcher m_html=p_html.matcher(htmlStr); + htmlStr=m_html.replaceAll(""); //过滤html标签 + + return htmlStr.trim(); //返回文本字符串 + } + + /** + * 通过身份证号码获取出生日期、性别、年龄 + * @author xianjun + * @param certificateNo + * @return 返回的出生日期格式:1990-01-01 性别格式:F-女,M-男 + */ + public static Map getBirAgeSex(String certificateNo) { + String birthday = ""; + String age = ""; + String sexCode = ""; + + int year = Calendar.getInstance().get(Calendar.YEAR); + char[] number = certificateNo.toCharArray(); + boolean flag = true; + if (number.length == 15) { + for (int x = 0; x < number.length; x++) { + if (!flag) return new HashMap(); + flag = Character.isDigit(number[x]); + } + } else if (number.length == 18) { + for (int x = 0; x < number.length - 1; x++) { + if (!flag) return new HashMap(); + flag = Character.isDigit(number[x]); + } + } + if (flag && certificateNo.length() == 15) { + birthday = "19" + certificateNo.substring(6, 8) + "-" + + certificateNo.substring(8, 10) + "-" + + certificateNo.substring(10, 12); + sexCode = Integer.parseInt(certificateNo.substring(certificateNo.length() - 3, certificateNo.length())) % 2 == 0 ? "F" : "M"; + age = (year - Integer.parseInt("19" + certificateNo.substring(6, 8))) + ""; + } else if (flag && certificateNo.length() == 18) { + birthday = certificateNo.substring(6, 10) + "-" + + certificateNo.substring(10, 12) + "-" + + certificateNo.substring(12, 14); + sexCode = Integer.parseInt(certificateNo.substring(certificateNo.length() - 4, certificateNo.length() - 1)) % 2 == 0 ? "F" : "M"; + age = (year - Integer.parseInt(certificateNo.substring(6, 10))) + ""; + } + Map map = new HashMap(); + map.put("birthday", birthday); + map.put("age", age); + map.put("sexCode", sexCode); + return map; + } + + /** + * @Description 将Date类型的时间转为long类型的时间戳 + * @Author SuXingYong + * @Date 2020/1/10 10:40 + * @Param [date] + * @Return long + **/ + public static long castDateForLong(Date date){ + long result = 0; + if (date != null){ + result = date.getTime(); + } + return result; + } + + /** + * @Description 将 yyyy-MM-dd HH:mm:ss 类型的时间字符串转换为long类型的时间戳 + * @Author SuXingYong + * @Date 2020/1/10 10:46 + * @Param [dateStr] + * @Return long + **/ + public static long castDateStrForLong(String dateStr){ + long result = 0; + Date date = CastUtil.castDateByString(dateStr); + if (date != null){ + result = date.getTime(); + } + return result; + } + + /** + * @Description 将java.sql.Timestamp转化为对应格式的String + * @Author SuXingYong + * @Date 2020/3/10 15:13 + * @Param [time, strFormat] + * @Return java.lang.String + **/ + public static String castTimestampForString(java.sql.Timestamp time, String strFormat) { + DateFormat df = new SimpleDateFormat(strFormat); + String str = df.format(time); + return str; + } + + /** + * 获取某年某月的第一天 + * @author xianjun + * @param year + * @param month + * @return + */ + public static String getFirstDayByMonth(int year, int month) { + Calendar cal = Calendar.getInstance(); + // 设置年份 + cal.set(Calendar.YEAR,year); + // 设置月份 + cal.set(Calendar.MONTH, month-1); + // 获取某月最小天数 + int firstDay = cal.getActualMinimum(Calendar.DAY_OF_MONTH); + // 设置最小天数 + cal.set(Calendar.DAY_OF_MONTH, firstDay); + cal.set(Calendar.HOUR_OF_DAY, 0); + // 格式化 + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String firstDayOfMonth = sdf.format(cal.getTime()); + return firstDayOfMonth; + } + + /** + * 获取某年某月的最后一天 + * + * @author xianjun + * @param year + * @param month + * @return + */ + public static String getLastDayByMonth(int year, int month) { + Calendar cal = Calendar.getInstance(); + // 设置年份 + cal.set(Calendar.YEAR, year); + // 设置月份 + cal.set(Calendar.MONTH, month-1); + // 获取某月最大天数 + int lastDay = cal.getActualMaximum(Calendar.DAY_OF_MONTH); + // 设置日历中月份的最大天数 + cal.set(Calendar.DAY_OF_MONTH, lastDay); + cal.set(Calendar.HOUR_OF_DAY, 23); + // 格式化时间 + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String lastDayOfMonth = sdf.format(cal.getTime()); + return lastDayOfMonth; + } +} diff --git a/server/src/main/java/com/optima/document/server/utils/DocToPdfUtil.java b/server/src/main/java/com/optima/document/server/utils/DocToPdfUtil.java new file mode 100644 index 0000000..04d3d62 --- /dev/null +++ b/server/src/main/java/com/optima/document/server/utils/DocToPdfUtil.java @@ -0,0 +1,792 @@ +package com.optima.document.server.utils; + +import com.jacob.activeX.ActiveXComponent; +import com.jacob.com.ComThread; +import com.jacob.com.Dispatch; +import com.jacob.com.Variant; +import lombok.extern.slf4j.Slf4j; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; + +/** + * @author xianjun + * @version 2017年4月20日 上午10:48:46 word转pdf工具类 + */ +@Slf4j +public class DocToPdfUtil { + + private static final int wdFormatPDF = 17; // pdf格式 + private static final int xlTypePDF = 0; + private static final int ppSaveAsPDF = 32; + // 代表一个word 程序 + private static ActiveXComponent MsWordApp = null; + // 代表进行处理的word 文档 + private static Dispatch document = null; + // word文档 + private Dispatch doc; + // word运行程序 + private ActiveXComponent word; + // 所有word文档集合 + private Dispatch documents; + // 选定的范围和插入点 + private Dispatch selection; + + private boolean saveOnExit = true; + + public DocToPdfUtil() throws Exception { + ComThread.InitSTA(); + if (word == null) { + word = new ActiveXComponent("Word.Application"); + // 不可见打开word + word.setProperty("Visible", new Variant(false)); + // 禁用宏 + word.setProperty("AutomationSecurity", new Variant(3)); + } + + if (documents == null) + documents = word.getProperty("Documents").toDispatch(); + } + + /** + * 设置退出时参数 + * + * @Title: setSaveOnExit + * @author: xianjun + * @Description: TODO + * @param saveOnExit true-退出时保存文件,false-退出时不保存文件 + * @throws + */ + public void setSaveOnExit(boolean saveOnExit) { + this.saveOnExit = saveOnExit; + } + + /** + * 打开一个已存在的文档 + * + * @Title: openDocument + * @author: xianjun + * @Description: TODO + * @param docPath + * @throws + */ + public void openDocument(String docPath) { + closeDocument(); + doc = Dispatch.call(documents, "Open", docPath).toDispatch(); + selection = Dispatch.get(word, "Selection").toDispatch(); + } + + /** + * 文件保存或另存为路径 + * + * @Title: save + * @author: xianjun + * @Description: TODO + * @param savePath + * @throws + */ + public void save(String savePath) { + Dispatch.call(Dispatch.call(word, "WordBasic").getDispatch(), "FileSaveAs", savePath); + } + + /** + * 关闭文档 + * @Title: closeDocument + * @author: xianjun + * @Description: TODO + * @param val 0不保存修改 -1 保存修改 -2 提示是否保存修改 + * @throws + */ + public void closeDocument(int val) { + Dispatch.call(doc, "Close", new Variant(val)); + doc = null; + } + + /** + * 关闭当前word文档 + * + * @Title: cloaseDocument + * @author: xianjun + * @Description: TODO + * @throws + */ + public void cloaseDocument() { + if (doc != null) { + Dispatch.call(doc, "Save"); + Dispatch.call(doc, "Close", new Variant(saveOnExit)); + doc = null; + } + } + + /** + * 关闭全部应用 + * + * @Title: close + * @author: xianjun + * @Description: TODO + * @throws + */ + public void close() { + if (word != null) { + //Dispatch.call(word, "Quit"); + word.invoke("Quit",0); + word = null; + } + selection = null; + documents = null; + ComThread.Release(); + } + + /** + * word转pdf + * + * @author xianjun + * @version 2017年4月20日 上午9:56:09 + * @describe + * @param fromAddress + * 待转地址 + * toAddress 新文件地址 + */ +// public static String wordToPdf(String fromAddress) { +// //ActiveXComponent ax = null; +// String toAddress = splitSuffx(fromAddress); +// try { +// PoiWordUtil.wordToPdf(fromAddress,toAddress); +// return toAddress; +// } catch (Exception e) { +// e.printStackTrace(); +// return null; +// } +// +// /*try { +// long startTime = System.currentTimeMillis(); +// +// *//** +// * 创建不同的控件调用不同的值 Word-Word.Application Excel-Excel.Application +// * Powerpoint-Powerpoint.Application Outlook-Outlook.Application +// *//* +// ax = new ActiveXComponent("Word.Application"); +// *//** +// * 设置打开文件不可见 +// *//* +// ax.setProperty("Visible", false); +// *//** +// * 获取word文档中所有内容 +// *//* +// Dispatch docs = ax.getProperty("Documents").toDispatch(); +// *//** +// * 打开word文档,并设置word为不可编辑和不需确认 +// *//* +// Dispatch doc = Dispatch.call(docs, "Open", fromAddress, false, true).toDispatch(); +// File tofile = new File(toAddress); +// if (tofile.exists()) { +// tofile.delete(); +// } +// // word文件另存为pdf文件 +// Dispatch.call(doc, "SaveAs", toAddress, wdFormatPDF); +// // 关闭word文档 +// Dispatch.call(doc, "Close", false); +// long endTime = System.currentTimeMillis(); +// System.out.println("转换完成,总共耗时" + (endTime - startTime)); +// return toAddress; +// } catch (Exception e) { +// System.out.println("=============Error:文档转换失败:" + e.getMessage()); +// return null; +// } */ +// } + +// public static String wordToPdf(String fromAddress, String fileName) throws Exception{ +// String fileRootPath = UploadFileUtils.getPath("write"); +// String dateUrl = CastUtil.castStringByDate(new Date(), "yyyy-MM-dd"); +// String htmlFileDirPath = File.separator + dateUrl; +// File htmlDirFile = new File(fileRootPath + htmlFileDirPath); +// if (!htmlDirFile.exists()) { +// htmlDirFile.mkdirs(); +// } +// //html文件路径 +// String pdfFilePath = htmlDirFile.getPath() + File.separator + fileName + "." + "pdf"; +// long startTime = System.currentTimeMillis(); +// File tofile = new File(pdfFilePath); +// if (tofile.exists()) { +// tofile.delete(); +// } +// // word文件另存为pdf文件 +// PoiWordUtil.wordToPdf(fromAddress,pdfFilePath); +// // 关闭word文档 +// long endTime = System.currentTimeMillis(); +// System.out.println("转换完成,总共耗时" + (endTime - startTime)); +// return "/"+dateUrl + "/" + fileName + "." + "pdf"; +// } + + public static String excel2PDF(String fromAddress){ + String toAddress = splitSuffx(fromAddress); + try{ + long startTime = System.currentTimeMillis(); + ActiveXComponent app = new ActiveXComponent("Excel.Application"); + app.setProperty("Visible", false); + Dispatch excels = app.getProperty("Workbooks").toDispatch(); + Dispatch excel = Dispatch.call(excels,"Open",fromAddress,false,true).toDispatch(); + File tofile = new File(toAddress); + if (tofile.exists()) { + tofile.delete(); + } + Dispatch.call(excel,"ExportAsFixedFormat",xlTypePDF,toAddress); + Dispatch.call(excel, "Close",false); + long endTime = System.currentTimeMillis(); + System.out.println("转换完成,总共耗时" + (endTime - startTime)); + return toAddress; + } catch (Exception e) { + System.out.println("=============Error:文档转换失败:" + e.getMessage()); + return null; + } + + } + public static String ppt2PDF(String fromAddress){ + String toAddress = splitSuffx(fromAddress); + try{ + long startTime = System.currentTimeMillis(); + ActiveXComponent app = new ActiveXComponent("PowerPoint.Application"); + //app.setProperty("Visible", msofalse); + Dispatch ppts = app.getProperty("Presentations").toDispatch(); + + Dispatch ppt = Dispatch.call(ppts, + "Open", + fromAddress, + true,//ReadOnly + true,//Untitled指定文件是否有标题 + false//WithWindow指定文件是否可见 + ).toDispatch(); + + Dispatch.call(ppt, + "SaveAs", + toAddress, + ppSaveAsPDF + ); + + Dispatch.call(ppt, "Close"); + long endTime = System.currentTimeMillis(); + System.out.println("转换完成,总共耗时" + (endTime - startTime)); + return toAddress; + } catch (Exception e) { + System.out.println("=============Error:文档转换失败:" + e.getMessage()); + return null; + } + } + + /** + * 路径处理函数,提取路径后缀 + * + * @author xianjun + * @version 2017年4月20日 上午10:54:49 + * @describe + * @param fromAddress + * @return + */ + public static String splitSuffx(String fromAddress) { + String toAddress = fromAddress.substring(0, fromAddress.lastIndexOf(".") + 1); + toAddress = toAddress + "pdf"; + return toAddress; + } + + /** + * 从选定内容或插入点开始查找文本 + * + * @param toFindText + * 要查找的文本 + * @return boolean true-查找到并选中该文本,false-未查找到文本 + */ + public static boolean find(ActiveXComponent ax,String toFindText) { + if (toFindText == null || toFindText.equals("")) + return false; + Dispatch selection = Dispatch.get(ax, "Selection").toDispatch(); // 输入内容需要的对象 + // 从selection所在位置开始查询 + Dispatch find1 = Dispatch.call(selection, "Find").toDispatch(); + // 设置要查找的内容 + Dispatch.put(find1, "Text", toFindText); + // 向前查找 + Dispatch.put(find1, "Forward", "True"); + // 设置格式 + Dispatch.put(find1, "Format", "True"); + // 大小写匹配 + Dispatch.put(find1, "MatchCase", "True"); + // 全字匹配 + Dispatch.put(find1, "MatchWholeWord", "True"); + // 查找并选中 + return Dispatch.call(find1, "Execute").getBoolean(); + } + + // 向文档中添加 一个图片, + public static void insertJpeg(ActiveXComponent ax,String toFindText,String jpegFilePath,int width,int height) { + Dispatch selection = Dispatch.get(ax, "Selection").toDispatch(); +// Dispatch image = Dispatch.get(selection, "InLineShapes").toDispatch(); +// Dispatch.call(image, "AddPicture", jpegFilePath); + + + if (find(ax,toFindText)) { + Dispatch picture = Dispatch.call(Dispatch.get(selection, "InLineShapes").toDispatch(), "AddPicture", jpegFilePath).toDispatch(); // 添加图片 + Dispatch.call(picture, "Select"); // 选中图片 + Dispatch.put(picture, "Width", new Variant(width)); // 图片的宽度 + Dispatch.put(picture, "Height", new Variant(height)); // 图片的高度 + Dispatch ShapeRange = Dispatch.call(picture, "ConvertToShape").toDispatch(); // 取得图片区域 + Dispatch WrapFormat = Dispatch.get(ShapeRange, "WrapFormat").toDispatch(); // 取得图片的格式对象 + Dispatch.put(WrapFormat, "Type", 7); // 设置环绕格式(0 - 7)下面是参数说明 + + // wdWrapInline 7 将形状嵌入到文字中。 + // wdWrapNone 3 将形状放在文字前面。请参阅 wdWrapFront 。 + // wdWrapSquare 0 使文字环绕形状。行在形状的另一侧延续。 + // wdWrapThrough 2 使文字环绕形状。 + // wdWrapTight 1 使文字紧密地环绕形状。 + // wdWrapTopBottom 4 将文字放在形状的上方和下方。 + // wdWrapBehind 5 将形状放在文字后面。 + // wdWrapFront 6 将形状放在文字前面。 + } + } + // 保存文档的更改 + public static void save() { + Dispatch.call(document, "Save"); + } + public static void closeDocument() { + // Close the document without saving changes + // 0 = wdDoNotSaveChanges + // -1 = wdSaveChanges + // -2 = wdPromptToSaveChanges + if(document != null) { + Dispatch.call(document, "Close", new Variant(false)); + document = null; + } + } + public static void closeWord() { + Dispatch.call(MsWordApp, "Quit"); + MsWordApp = null; + document = null; + } + + public static void openAnExistsFileTest(String wordFilePath,String toFindText,String imagePath,int width,int height) { + ActiveXComponent ax = null; + ax = new ActiveXComponent("Word.Application"); + ax.setProperty("Visible", new Variant(false));// 是否前台打开word 程序,或者后台运行 + Dispatch documents = Dispatch.get(ax, "Documents").toDispatch(); + documents = Dispatch.call(documents, "Open", wordFilePath, + new Variant(true)/* 是否进行转换ConfirmConversions */, + new Variant(false)/* 是否只读 */).toDispatch(); + //Dispatch selection = Dispatch.get(MsWordApp, "Selection").toDispatch(); + insertJpeg(ax,toFindText,imagePath,width,height); // 插入图片(注意刚打开的word,光标处于开头,故,图片在最前方插入) + // word文件另存为pdf文件 + String toAddress = splitSuffx(wordFilePath); + Dispatch.call(documents, "Save"); + //Dispatch.call(documents, "Quit"); + Dispatch.call(documents, "SaveAs", toAddress, wdFormatPDF); + Dispatch.call(documents, "Close", new Variant(false)); + //save(); + //closeDocument(); + //closeWord(); + // 关闭word文档 + //Dispatch.call(documents, "Close", false); + } + + public boolean findText(Dispatch selection, String toFindText,String matchWholeWord) { + return findText(selection, toFindText, matchWholeWord, false); + } + + /** + * 查找文本 + * @param selection + * @param toFindText + * @param matchWholeWord 匹配全词 + * @param matchWildcards 使用通配符 + * @return + */ + public boolean findText(Dispatch selection, String toFindText,String matchWholeWord, boolean matchWildcards) { + if (toFindText == null || "".equals(toFindText)) + return false; + // 从selection所在位置开始查询 + Dispatch find1 = Dispatch.call(selection, "Find").toDispatch(); + // 设置要查找的内容 + Dispatch.put(find1, "Text", toFindText); + // 向前查找 + Dispatch.put(find1, "Forward", "True"); + // 通配符 + Dispatch.put(find1, "MatchWildcards", matchWildcards); + // 设置格式 + Dispatch.put(find1, "Format", "True"); + // 大小写匹配 + Dispatch.put(find1, "MatchCase", "False"); + // 全字匹配 + Dispatch.put(find1, "MatchWholeWord", matchWholeWord); + // 查找并选中 + return Dispatch.call(find1, "Execute").getBoolean(); + } + + /** + * 插入图片 + * + * @Title: insertJpeg + * @author: xianjun + * @Description: TODO + * @param source + * @param toFindText + * @param imgSource + * @param width + * @param height + * @throws + */ + public static byte[] insertJpeg(byte[] source, String toFindText, byte[] imgSource, int width, int height) { + long t1 = System.currentTimeMillis(); + DocToPdfUtil docToPdfUtil = null; + Path tempFilePath = null; + Path imgTempFilePath = null; + byte[] result = null; + try { + docToPdfUtil = new DocToPdfUtil(); + tempFilePath = Files.createTempFile(UUID.randomUUID().toString(), null); + Files.write(tempFilePath, source); + docToPdfUtil.openDocument(tempFilePath.toString()); + if (docToPdfUtil.findText(docToPdfUtil.selection, toFindText,"True")) { + imgTempFilePath = Files.createTempFile(UUID.randomUUID().toString(), null); + Files.write(imgTempFilePath, imgSource); + Dispatch picture = Dispatch.call(Dispatch.get(docToPdfUtil.selection, "InLineShapes").toDispatch(), "AddPicture", imgTempFilePath.toString()).toDispatch(); // 添加图片 + Dispatch.call(picture, "Select"); // 选中图片 + if (width!=0) { + Dispatch.put(picture, "Width", new Variant(width)); // 图片的宽度 + Dispatch.put(picture, "Height", new Variant(height)); // 图片的高度 + } + +// Dispatch ShapeRange = Dispatch.call(picture, "ConvertToShape").toDispatch(); // 取得图片区域 +// Dispatch WrapFormat = Dispatch.get(ShapeRange, "WrapFormat").toDispatch(); // 取得图片的格式对象 +// Dispatch.put(WrapFormat, "Type", 0); // 设置环绕格式(0 - 7)下面是参数说明 + } + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } finally { + if(docToPdfUtil != null) { + docToPdfUtil.cloaseDocument(); + docToPdfUtil.close(); + } + if (tempFilePath != null) { + try { + result = Files.readAllBytes(tempFilePath); + Files.delete(tempFilePath); + } catch (IOException e) { + e.printStackTrace(); + } + } + if (imgTempFilePath != null) { + try { + Files.delete(imgTempFilePath); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + log.info("insert image=======consuming:{} milliseconds", System.currentTimeMillis() - t1); + return result; + } + + /** + * 插入图片 + * + * @Title: insertJpeg + * @author: xianjun + * @Description: TODO + * @param source + * @param toFindText + * @param imgSources + * @param width + * @param height + * @throws + */ + public static byte[] insertJpeg(byte[] source, String toFindText, List imgSources, int width, int height) { + DocToPdfUtil docToPdfUtil = null; + Path tempFilePath = null; + Path imgTempFilePath = null; + byte[] result = null; + try { + docToPdfUtil = new DocToPdfUtil(); + tempFilePath = Files.createTempFile(UUID.randomUUID().toString(), null); + Files.write(tempFilePath, source); + docToPdfUtil.openDocument(tempFilePath.toString()); + String replaceText = ""; + for (int i = 0; i < imgSources.size(); i++) { + replaceText += "${" + toFindText + "_" + i + "}"; + } + String searchText = "${" + toFindText + "}"; + boolean flag = docToPdfUtil.findText(docToPdfUtil.selection, searchText, "True"); + if (flag) { + setKeyAndValue(docToPdfUtil, replaceText, searchText); + } + for (int i = 0; i < imgSources.size(); i++) { + Dispatch.call(docToPdfUtil.selection,"HomeKey",new Variant(6)); + if (docToPdfUtil.findText(docToPdfUtil.selection, "${" + toFindText + "_" + i + "}" ,"True")) { + imgTempFilePath = Files.createTempFile(UUID.randomUUID().toString(), null); + Files.write(imgTempFilePath, imgSources.get(i)); + Dispatch picture = Dispatch.call(Dispatch.get(docToPdfUtil.selection, "InLineShapes").toDispatch(), "AddPicture", imgTempFilePath.toString()).toDispatch(); // 添加图片 + Dispatch.call(picture, "Select"); // 选中图片 + if (width != 0) { + Dispatch.put(picture, "Width", new Variant(width)); // 图片的宽度 + Dispatch.put(picture, "Height", new Variant(height)); // 图片的高度 + } +// Dispatch ShapeRange = Dispatch.call(picture, "ConvertToShape").toDispatch(); // 取得图片区域 +// Dispatch WrapFormat = Dispatch.get(ShapeRange, "WrapFormat").toDispatch(); // 取得图片的格式对象 +// Dispatch.put(WrapFormat, "Type", 0); // 设置环绕格式(0 - 7)下面是参数说明 + } + } + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } finally { + if(docToPdfUtil != null) { + docToPdfUtil.cloaseDocument(); + docToPdfUtil.close(); + } + if (tempFilePath != null) { + try { + result = Files.readAllBytes(tempFilePath); + Files.delete(tempFilePath); + } catch (IOException e) { + e.printStackTrace(); + } + } + if (imgTempFilePath != null) { + try { + Files.delete(imgTempFilePath); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + return result; + } + + public static void insertJpeg(String wordFilePath, List findTexts, List imagePaths, List> imageSelections) { + DocToPdfUtil docToPdfUtil = null; + try { + docToPdfUtil = new DocToPdfUtil(); + docToPdfUtil.openDocument(wordFilePath); + if (findTexts != null && findTexts.size() > 0) { + for (int i=0; i< findTexts.size(); i++) { + String toFindText = findTexts.get(i); + String imagePath = imagePaths.get(i); + Map imageSelection = imageSelections.get(i); + int height = CastUtil.castInt(imageSelection.get("Height")); + int width = CastUtil.castInt(imageSelection.get("Width")); + if (docToPdfUtil.findText(docToPdfUtil.selection, toFindText,"True")) { + Dispatch picture = Dispatch.call(Dispatch.get(docToPdfUtil.selection, "InLineShapes").toDispatch(), "AddPicture", imagePath).toDispatch(); // 添加图片 + Dispatch.call(picture, "Select"); // 选中图片 + if (width!=0) { + Dispatch.put(picture, "Width", new Variant(width)); // 图片的宽度 + Dispatch.put(picture, "Height", new Variant(height)); // 图片的高度 + } + + Dispatch ShapeRange = Dispatch.call(picture, "ConvertToShape").toDispatch(); // 取得图片区域 + Dispatch WrapFormat = Dispatch.get(ShapeRange, "WrapFormat").toDispatch(); // 取得图片的格式对象 + Dispatch.put(WrapFormat, "Type", 0); // 设置环绕格式(0 - 7)下面是参数说明 + } + } + } + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } finally { + if(docToPdfUtil != null) { + docToPdfUtil.cloaseDocument(); + docToPdfUtil.close(); + } + } + } + + /** + * 替换字段 + * + * @Title: wordFindReplace + * @author: xianjun + * @Description: TODO + * @param wordpath + * @param oldtext + * @param newtext + * @return + * @throws + */ + public static String wordFindReplace(String wordpath,String oldtext,String newtext) { + DocToPdfUtil docToPdfUtil = null; + try { + docToPdfUtil = new DocToPdfUtil(); + docToPdfUtil.openDocument(wordpath); + boolean flag = docToPdfUtil.findText(docToPdfUtil.selection, oldtext,"True"); + if (flag) { + Dispatch.put(docToPdfUtil.selection,"Text",newtext); + Dispatch.call(docToPdfUtil.selection, "MoveRight"); + } + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } finally { + if(docToPdfUtil != null) { + docToPdfUtil.cloaseDocument(); + docToPdfUtil.close(); + } + } + return null; + } + + /** + * 向word填充属性字段 + * + * @Title: fieldToWord + * @author: xianjun + * @Description: TODO + * @param source 源 + * @param infoMap 属性字段 + * @return + * @throws + */ + public static byte[] fieldToWord(byte[] source, Map infoMap) { + long t1 = System.currentTimeMillis(); + DocToPdfUtil docToPdfUtil = null; + Path tempFilePath = null; + byte[] result = null; + try { + docToPdfUtil = new DocToPdfUtil(); + tempFilePath = Files.createTempFile(UUID.randomUUID().toString(), null); + Files.write(tempFilePath, source); + docToPdfUtil.openDocument(tempFilePath.toString()); + for (String key : infoMap.keySet()) { + String replaceText = "${" + key.trim() + "}"; + boolean flag = docToPdfUtil.findText(docToPdfUtil.selection, replaceText,"True"); + if (flag) { + String value = CastUtil.castString(infoMap.get(key)); + setKeyAndValue(docToPdfUtil, value, replaceText); + } + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (docToPdfUtil != null) { + docToPdfUtil.cloaseDocument(); + docToPdfUtil.close(); + } + if (tempFilePath != null) { + try { + result = Files.readAllBytes(tempFilePath); + Files.delete(tempFilePath); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + log.info("insert text=======consuming:{} milliseconds", System.currentTimeMillis() - t1); + return result; + } + + /** + * 清除占位符 + * @param source + * @return + */ + public static byte[] clearPlaceholder(byte[] source) { + long t1 = System.currentTimeMillis(); + DocToPdfUtil docToPdfUtil = null; + Path tempFilePath = null; + byte[] result = null; + try { + docToPdfUtil = new DocToPdfUtil(); + tempFilePath = Files.createTempFile(UUID.randomUUID().toString(), null); + Files.write(tempFilePath, source); + docToPdfUtil.openDocument(tempFilePath.toString()); + String replaceText = "$\\{*\\}"; + boolean flag = docToPdfUtil.findText(docToPdfUtil.selection, replaceText, "True", true); + if (flag) { + setKeyAndValue(docToPdfUtil, "", replaceText, true); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (docToPdfUtil != null) { + docToPdfUtil.cloaseDocument(); + docToPdfUtil.close(); + } + if (tempFilePath != null) { + try { + result = Files.readAllBytes(tempFilePath); + Files.delete(tempFilePath); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + log.info("clear placeholder=======consuming:{} milliseconds", System.currentTimeMillis() - t1); + return result; + } + + public static void setKeyAndValue(DocToPdfUtil docToPdfUtil, String value, String replaceText) { + setKeyAndValue(docToPdfUtil, value, replaceText, false); + } + + public static void setKeyAndValue(DocToPdfUtil docToPdfUtil, String value,String replaceText, boolean matchWildcards) { + if (value == null) { + value = ""; + } + value = CastUtil.delHTMLTag(value); + if (value.indexOf("\n\n") != -1) { + value = value.replace("\n\n", CastUtil.castString((char) 11)); + } + if (value.indexOf("\n") != -1) { + value = value.replace("\n", CastUtil.castString((char) 11)); + } + + Dispatch.put(docToPdfUtil.selection, "Text", value); + + //Dispatch.call(docToPdfUtil.selection, "MoveStart"); + Dispatch.call(docToPdfUtil.selection,"HomeKey",new Variant(6)); + boolean flag = docToPdfUtil.findText(docToPdfUtil.selection, replaceText,"True", matchWildcards); + if (flag) { + setKeyAndValue(docToPdfUtil, value, replaceText, matchWildcards); + } + } + + /** + * word转pdf + * @Title: wordCnoverPdf + * @author: xianjun + * @Description: TODO + * @param fromAddress + * @return + * @throws + */ + public static String wordCnoverPdf(String fromAddress) { + String toAddress = splitSuffx(fromAddress); + DocToPdfUtil docToPdfUtil = null; + try { + docToPdfUtil = new DocToPdfUtil(); + long startTime = System.currentTimeMillis(); + docToPdfUtil = new DocToPdfUtil(); + docToPdfUtil.openDocument(fromAddress); + File tofile = new File(toAddress); + if (tofile.exists()) { + tofile.delete(); + } + // word文件另存为pdf文件 + Dispatch.call(docToPdfUtil.doc, "SaveAs", toAddress, wdFormatPDF); + // 关闭word文档 + long endTime = System.currentTimeMillis(); + System.out.println("转换完成,总共耗时" + (endTime - startTime)); + if(docToPdfUtil != null) { + docToPdfUtil.cloaseDocument(); + docToPdfUtil.close(); + } + return toAddress; + } catch (Exception e) { + if(docToPdfUtil != null) { + docToPdfUtil.cloaseDocument(); + docToPdfUtil.close(); + } + System.out.println("=============Error:文档转换失败:" + e.getMessage()); + return null; + } + + } + +} \ No newline at end of file diff --git a/server/src/main/java/com/optima/document/server/utils/PoiWordUtil.java b/server/src/main/java/com/optima/document/server/utils/PoiWordUtil.java new file mode 100644 index 0000000..070dffb --- /dev/null +++ b/server/src/main/java/com/optima/document/server/utils/PoiWordUtil.java @@ -0,0 +1,486 @@ +package com.optima.document.server.utils;/** + * Created by xianjun on 2019/3/22.16:30 + */ + +import org.apache.commons.lang3.StringUtils; +import org.apache.poi.hwpf.HWPFDocument; +import org.apache.poi.hwpf.model.PicturesTable; +import org.apache.poi.hwpf.usermodel.Bookmarks; +import org.apache.poi.hwpf.usermodel.Range; +import org.apache.poi.xwpf.usermodel.*; +import org.apache.xmlbeans.XmlToken; +import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps; +import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D; +import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTInline; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTFonts; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTHpsMeasure; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRPr; + +import java.io.*; +import java.math.BigInteger; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 文书替换工具类 + * @author xianjun + * @version 2019/3/22 16:30 + * + */ +public class PoiWordUtil { + /** + * word为doc后缀 + */ + private static final String DOC = "doc"; + /** + * word为docx后缀 + */ + private static final String DOCX = "docx"; + /** + * 服务器为windows + */ + private static final String SYSTEM_TYPE_W = "Windows"; + /** + * 服务器为Linux + */ + private static final String SYSTEM_TYPE_L = "Linux"; + + /** + * 错误消息 + */ + private static String errorMsg = ""; + /** + * 模板路径 + */ + private static String templatePath = ""; + /** + * 新文件的路径 + */ + private static String newFilePath = ""; + + /** + * 文书临时存放的目录 + */ + private static String tempFilePath = ""; + + /** + * 替换模板方法 + * @param path 文书路径 + * @param tempPath 临时路径 + * @param paramMap 替换参数 + * @return + * @throws Exception + */ +// public static Map executeWord(String path, String tempPath, Map paramMap) throws Exception { +// //获取模板路径 +// getTemplate(path); +// //获取新文件生成路径 +// getFileStore(path); +// newFilePath = tempPath; +// //判断模板类型 +// if(templatePath.endsWith(DOC)){ +// //处理03版word +// //replaceWordDoc(paramMap); +// }else if(templatePath.endsWith(DOCX)){ +// //处理07版word +// replaceWordDocx(paramMap); +// } +// +// //返回处理结果(新生成文件路径、错误信息) +// Map map = new HashMap(); +// map.put("newFilePath", newFilePath); +// map.put("errorMsg", errorMsg); +// return map; +// } + + /** + * 替换模板文本内容(2007) + * @param paramMap 替换参数 + * @throws Exception + */ + private static void replaceWordDocx(Map paramMap) throws Exception{ + InputStream is = new FileInputStream(tempFilePath); + XWPFDocument doc = new XWPFDocument(is); + Iterator iterator = doc.getParagraphsIterator(); + Iterator it = doc.getTablesIterator(); + XWPFParagraph para; + XWPFTable patab; + //替换文本 + while (iterator.hasNext()) { + para = iterator.next(); + replaceInParaDocx(doc, para, paramMap); + } + while (it.hasNext()) { + patab = it.next(); + List rows = patab.getRows(); + for (int i = 0; i < rows.size(); i++) { + XWPFTableRow row = rows.get(i);//读取每一列数据 + List cells = row.getTableCells(); + for (int j = 0; j < cells.size(); j++) { + XWPFTableCell cell = cells.get(j); + //输出当前的单元格的数据 + //System.out.print(cell.getText() + "\t"); + List paragraphs = cell.getParagraphs(); + for (XWPFParagraph xwpfParagraph : paragraphs) { + replaceInParaDocx(doc, xwpfParagraph, paramMap); + } + } + System.out.println(); + } + } + //写出文件 + OutputStream out = new FileOutputStream(newFilePath); + doc.write(out); + //关闭输出流 + close(out); + } + + /** + * 替换方法(2007) + * @param para + * @param paramMap + * @throws Exception + */ + @SuppressWarnings("unchecked") + private static void replaceInParaDocx(XWPFDocument doc, XWPFParagraph para, Map paramMap) throws Exception { + List runs; + Matcher matcher; + String imgPath = ""; + int width = 100,height = 100; + String txt = para.getParagraphText(); + if (matcher(txt.trim()).find()) { + runs = para.getRuns(); + for (int i=0; i infoMap = new HashMap(); + if ((matcher = matcher(runText)).find()) { + System.err.println("查找替换标签:" + matcher.group()); + String tx = matcher.group().substring(2,matcher.group().length()-1); + if (paramMap.get(tx) instanceof Map) { + infoMap = (Map)paramMap.get(tx); + if (runText.contains("_img") || runText.contains("_sign") || runText.contains("_map")) { + imgPath = CastUtil.castString(infoMap.get("Text")); + width = CastUtil.castInt(infoMap.get("Width")); + height = CastUtil.castInt(infoMap.get("Height")); + if (imgPath !=null && !"".equalsIgnoreCase(imgPath)) { + runText = ""; + } + } else { + String tempText = CastUtil.castString(infoMap.get("Text")); + if (tempText != null && !"".equalsIgnoreCase(tempText)) { + runText = CastUtil.castString(infoMap.get("Text")); + } + } + } else { + if (runText.contains("_img") || runText.contains("_sign") || runText.contains("_map")) { + imgPath = CastUtil.castString(paramMap.get(tx)); + if (imgPath !=null && !"".equalsIgnoreCase(imgPath)) { + runText = ""; + } + } else { + String tempText = CastUtil.castString(paramMap.get(tx)); + if (tempText != null && !"".equalsIgnoreCase(tempText)) { + runText = tempText; + } + } + } + } + //直接调用XWPFRun的setText()方法设置文本时,在底层会重新创建一个XWPFRun,把文本附加在当前文本后面, + //所以我们不能直接设值,需要先删除当前run,然后再自己手动插入一个新的run。 + para.removeRun(i); + XWPFRun newRun = para.insertNewRun(i); + newRun.setText(runText); + if(!infoMap.isEmpty()){ + //设置字体样式 + setFont(newRun, infoMap); + } + if(StringUtils.isNoneEmpty(imgPath)){ + //获得当前CTInline + CTInline inline = newRun.getCTR().addNewDrawing().addNewInline(); + doc.addPictureData(new FileInputStream(imgPath), 5); + insertPicture(doc, inline, width, height); + } + imgPath = ""; + //infoMap.clear(); + System.err.println("替换"); + } + } + } + } + + /** + * 2007字体样式设置 + * @param run + * @param param + */ + private static void setFont(XWPFRun run, Map param) { + CTRPr pRpr = null; + if (run.getCTR() != null) { + pRpr = run.getCTR().getRPr(); + if (pRpr == null) { + pRpr = run.getCTR().addNewRPr(); + } + } + + // 设置字体 + if(param.get("Name") != null){ + CTFonts fonts = pRpr.isSetRFonts()?pRpr.getRFonts():pRpr.addNewRFonts(); + fonts.setAscii(param.get("Name").toString()); + fonts.setEastAsia(param.get("Name").toString()); + fonts.setHAnsi(param.get("Name").toString()); + } + //粗体 + run.setBold(param.get("Bold")!=null?true:false); + //斜体 + run.setItalic(param.get("Italic")!=null?true:false); + //下划线 + if (param.get("Underline") != null) { + run.setUnderline(UnderlinePatterns.SINGLE); + } + //字体大小 + if(param.get("Size") != null){ + CTHpsMeasure sz = pRpr.isSetSz() ? pRpr.getSz() : pRpr.addNewSz(); + sz.setVal(new BigInteger(param.get("Size").toString())); + CTHpsMeasure szCs = pRpr.isSetSzCs() ? pRpr.getSzCs() : pRpr.addNewSzCs(); + szCs.setVal(new BigInteger(param.get("Size").toString())); + } + //字体颜色 + if (param.get("Color") != null) { + run.setColor(param.get("Color").toString()); + } + } + + /** + * 插入图片 + * + * @param document + * @param inline + * @param width + * @param height + * @throws Exception + */ + public static void insertPicture(XWPFDocument document, CTInline inline, int width, int height) throws Exception { + int id = document.getAllPictures().size()-1; + final int EMU = 9525; + width *= EMU; + height *= EMU; + String blipId = document.getAllPictures().get(id).getPackageRelationship().getId(); + + String picXml = "" + + "" + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + ""; + + inline.addNewGraphic().addNewGraphicData(); + XmlToken xmlToken = null; + xmlToken = XmlToken.Factory.parse(picXml); + inline.set(xmlToken); + inline.setDistT(0); + inline.setDistB(0); + inline.setDistL(0); + inline.setDistR(0); + CTPositiveSize2D extent = inline.addNewExtent(); + extent.setCx(width); + extent.setCy(height); + CTNonVisualDrawingProps docPr = inline.addNewDocPr(); + docPr.setId(id); + docPr.setName("IMG_" + id); + docPr.setDescr("IMG_" + id); + } + + /** + * 替换模板文本内容(2003) + * @param paramMap 替换参数 + * @throws Exception + */ + private static void replaceWordDoc(Map paramMap) throws Exception{ + InputStream is = new FileInputStream(templatePath); + HWPFDocument doc = new HWPFDocument(is); + Range bodyRange = doc.getRange(); + //替换文本 + replaceInParaDoc(doc, bodyRange, paramMap); + //输出新文件 + FileOutputStream out = new FileOutputStream(newFilePath); + doc.write(out); + out.flush(); + //关闭输出流 + out.close(); + } + + /** + * 替换方法(2003) + * @param range + * @param paramMap + */ + @SuppressWarnings("unchecked") + private static void replaceInParaDoc(HWPFDocument doc, Range range, Map paramMap) { + Matcher matcher; + String imgPath = ""; + int width = 0,height = 0; + while ((matcher = matcher(range.text())).find()) { + String key = matcher.group(); + Map infoMap = (Map) paramMap.get(key); + if (infoMap.get("Type") !=null && "img".equalsIgnoreCase(CastUtil.castString(infoMap.get("Type")))) { + //处理图片 + imgPath = infoMap.get("Path").toString(); + width = Integer.parseInt(infoMap.get("Width").toString()); + height = Integer.parseInt(infoMap.get("Height").toString()); + } else { + range.replaceText(matcher.group(), String.valueOf(infoMap.get("Text"))); + } + if(StringUtils.isNoneEmpty(imgPath)){ + Bookmarks bookmarks = doc.getBookmarks(); + PicturesTable picturesTable = doc.getPicturesTable(); + } + imgPath = ""; + /*int index = Integer.valueOf(matcher.group(1)) - 1; + if(index >= params.size()) break; + if(params.get(index) instanceof String){ + range.replaceText(matcher.group(), String.valueOf(params.get(index))); + }else if(params.get(index) instanceof Map){ + Map infoMap = (Map)params.get(index); + range.replaceText(matcher.group(), String.valueOf(infoMap.get("Text"))); + }*/ + } + } + + /** + * 正则匹配字符串 + * @param str 带替换字符串 + * @return + */ + public static Matcher matcher(String str) { + String remax = "\\$.+?}"; + //String remax = "\\$\\{(.+?)\\}"Pattern.CASE_INSENSITIVE; + Pattern pattern = Pattern.compile(remax); + Matcher matcher = pattern.matcher(str); + return matcher; + } + + /** + * 获取文件模板路径 + * @param path 模板文件路径 + * @return + * @throws Exception + */ + private static void getTemplate(String path) throws Exception { + //String path = UploadFileUtils.getPath("write"); + // String docPath = path + templateName + ".doc"; + //String docxPath = path + templateName + ".docx"; + templatePath = path; + } + +// /** +// * 获取文件存储路径 +// * @param templatePath 模板文件名称 +// * @return +// * @throws IOException +// */ +// private static void getFileStore(String templatePath) throws IOException { +// //操作系统类型(Windows、Linux) +// String sysType = UploadFileUtils.getPath("fileserver.system.type"); +// //文件存储跟目录 +// String fileRoot = UploadFileUtils.getPath("fileserver.local.root"); +// //文件生产目录 +// String fileStore = UploadFileUtils.getPath("fileserver.store"); +// //生成文件名称 +// String name = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()); +// +// if(SYSTEM_TYPE_W.equals(sysType)){ +// //Windows +// tempFilePath = fileRoot + fileStore + "/" ; +// }else if(SYSTEM_TYPE_L.equals(sysType)){ +// //Linux +// tempFilePath = File.separator + "usr/fileserver/"; +// }else{ +// //其他服务器系统处理 +// } +// +// //生成目录不存在创建目录 +// File file = new File(tempFilePath); +// if(!file.exists()){ +// file.mkdirs(); +// } +// tempFilePath = tempFilePath + name + ".docx"; +// FileUtils.copyFile(templatePath, tempFilePath); +// } + + + /** + * 关闭输出流 + * @param os + */ + public static void close(OutputStream os) { + if (os != null) { + try { + os.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + /** + * 关闭输入流 + * @param in + */ + public static void close(InputStream in) { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + /** + * word转pdf + * @throws Exception + * @return + */ +// public static byte[] wordToPdf(byte[] source) throws Exception{ +// // 临时文件 +// Path tempFilePath = Files.createTempFile(UUID.randomUUID().toString(), null);; +// Files.write(tempFilePath, source); +// String command = "D:/OpenOffice4/program/soffice.exe -headless -accept=\"socket,host=127.0.0.1,port=8100;urp;\""; +// Process p = Runtime.getRuntime().exec(command); +// OpenOfficeConnection connection = new SocketOpenOfficeConnection(); +// connection.connect(); +// // 转换 +// DocumentConverter converter = new OpenOfficeDocumentConverter(connection); +// converter.convert(new ByteArrayInputStream(source), tempFilePath.toString()); +// // 关闭连接 +// connection.disconnect(); +// // 关闭进程 +// p.destroy(); +// return null; +// } + +} diff --git a/server/src/main/resources/application.yml b/server/src/main/resources/application.yml new file mode 100644 index 0000000..02a87d7 --- /dev/null +++ b/server/src/main/resources/application.yml @@ -0,0 +1,5 @@ +server: + port: 8090 +document: + doc-to-program: D:\\optima\\docto.exe + diff --git a/server/src/main/resources/banner.txt b/server/src/main/resources/banner.txt new file mode 100644 index 0000000..4b44157 --- /dev/null +++ b/server/src/main/resources/banner.txt @@ -0,0 +1,6 @@ +██████╗ ██████╗ ██████╗██╗ ██╗███╗ ███╗███████╗███╗ ██╗████████╗ +██╔══██╗██╔═══██╗██╔════╝██║ ██║████╗ ████║██╔════╝████╗ ██║╚══██╔══╝ +██║ ██║██║ ██║██║ ██║ ██║██╔████╔██║█████╗ ██╔██╗ ██║ ██║ +██║ ██║██║ ██║██║ ██║ ██║██║╚██╔╝██║██╔══╝ ██║╚██╗██║ ██║ +██████╔╝╚██████╔╝╚██████╗╚██████╔╝██║ ╚═╝ ██║███████╗██║ ╚████║ ██║ +╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═══╝ ╚═╝ \ No newline at end of file diff --git a/tl-server/pom.xml b/tl-server/pom.xml new file mode 100644 index 0000000..31b532c --- /dev/null +++ b/tl-server/pom.xml @@ -0,0 +1,102 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.0.3.RELEASE + + + com.optima + document-tl-server + 1.0.0 + document tl server + 文档模版引擎服务 + + 1.8 + + + + org.springframework.boot + spring-boot-starter-web + + + + com.optima + document-api + 1.0.0 + + + + org.projectlombok + lombok + 1.16.20 + provided + + + org.springframework.boot + spring-boot-starter-test + test + + + com.artofsolving + jodconverter + 2.2.2 + system + ${basedir}/src/main/resources/libs/jodconverter-2.2.2.jar + + + org.jodconverter + jodconverter-core + 4.0.0-RELEASE + + + commons-io + commons-io + + + commons-lang3 + org.apache.commons + + + json + org.json + + + + + org.apache.pdfbox + pdfbox-tools + 2.0.25 + + + com.deepoove + poi-tl + 1.12.0 + + + org.apache.logging.log4j + log4j-to-slf4j + 2.17.2 + + + org.apache.logging.log4j + log4j-api + 2.17.2 + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + true + + + + + + diff --git a/tl-server/src/main/java/com/optima/document/tl/server/DocumentServer.java b/tl-server/src/main/java/com/optima/document/tl/server/DocumentServer.java new file mode 100644 index 0000000..30f27a1 --- /dev/null +++ b/tl-server/src/main/java/com/optima/document/tl/server/DocumentServer.java @@ -0,0 +1,13 @@ +package com.optima.document.tl.server; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class DocumentServer { + + public static void main(String[] args) { + SpringApplication.run(DocumentServer.class, args); + } + +} diff --git a/tl-server/src/main/java/com/optima/document/tl/server/api/DocumentServiceImpl.java b/tl-server/src/main/java/com/optima/document/tl/server/api/DocumentServiceImpl.java new file mode 100644 index 0000000..e2315a2 --- /dev/null +++ b/tl-server/src/main/java/com/optima/document/tl/server/api/DocumentServiceImpl.java @@ -0,0 +1,170 @@ +package com.optima.document.tl.server.api; + +import com.deepoove.poi.XWPFTemplate; +import com.deepoove.poi.config.Configure; +import com.deepoove.poi.data.PictureRenderData; +import com.deepoove.poi.data.TextRenderData; +import com.deepoove.poi.policy.ListRenderPolicy; +import com.deepoove.poi.policy.PictureRenderPolicy; +import com.deepoove.poi.render.RenderContext; +import com.deepoove.poi.template.run.RunTemplate; +import com.deepoove.poi.xwpf.BodyContainer; +import com.deepoove.poi.xwpf.BodyContainerFactory; +import com.optima.document.api.DocumentService; +import com.optima.document.tl.server.config.DocumentConfig; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.IOUtils; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.rendering.ImageType; +import org.apache.pdfbox.rendering.PDFRenderer; +import org.apache.pdfbox.tools.imageio.ImageIOUtil; +import org.apache.poi.xwpf.usermodel.XWPFRun; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; + +/** + * 服务接口实现 + * @author Elias + * @date 2021-09-28 16:18 + */ +@Slf4j +@Component +public class DocumentServiceImpl implements DocumentService { + + /** + * word模版引擎配置 + */ + private static final Configure wtlConfig = Configure.builder().buildGramer("${", "}") + .setValidErrorHandler(new Configure.DiscardHandler()) + .addPlugin('%', new ListRenderPolicy() { + @Override + public void doRender(RenderContext> context) throws Exception { + XWPFRun run = context.getRun(); + List dataList = context.getData(); + Iterator var5 = dataList.iterator(); + while (var5.hasNext()) { + Object data = var5.next(); + if (data instanceof TextRenderData) { + run.setText(((TextRenderData) data).getText()); + if (var5.hasNext()) { + run.setText(","); + } + } else if (data instanceof PictureRenderData) { + PictureRenderPolicy.Helper.renderPicture(run, (PictureRenderData) data); + } + + } + } + + }) + .setRenderDataComputeFactory(envModel -> + el -> { + Object data = envModel.getRoot(); + if ("#this".equals(el)) { + return data; + } else if (data instanceof Map) { + Map dataMap = ((Map) data); + if (dataMap.containsKey(el)) { + return dataMap.get(el); + } + } + return null; + }) + .build(); + + @Resource + private DocumentConfig documentConfig; + + @Override + public byte[] generateWord(byte[] templateData, Map dataModel) { + long start = System.currentTimeMillis(); + XWPFTemplate template = XWPFTemplate.compile(new ByteArrayInputStream(templateData), wtlConfig).render(dataModel); + try (ByteArrayOutputStream bos = new ByteArrayOutputStream()){ + template.write(bos); + log.info("word generate========consuming:{} milliseconds", System.currentTimeMillis() - start); + return bos.toByteArray(); + } catch (Exception e) { + log.error("word generate error", e); + return null; + } + } + + @Override + public byte[] wordToPdf(byte[] source, boolean clear) { + try { + long start = System.currentTimeMillis(); + if (clear) { + try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) { + XWPFTemplate template = XWPFTemplate.compile(new ByteArrayInputStream(source), wtlConfig); + BodyContainer bodyContainer = BodyContainerFactory.getBodyContainer(template.getXWPFDocument()); + template.getElementTemplates().forEach(it -> { + if (it instanceof RunTemplate) { + RunTemplate rt = (RunTemplate) it; + bodyContainer.clearPlaceholder(rt.getRun()); + } + }); + template.writeAndClose(bos); + source = bos.toByteArray(); + log.info("清空占位符=======耗时:{} 毫秒", System.currentTimeMillis() - start); + } catch (Exception e) { + log.error("清空标签失败:", e); + return null; + } + } + Path tempFilePath = Files.createTempFile(UUID.randomUUID().toString(), ".docx"); + Files.write(tempFilePath, source); + + Path pdfPath = Files.createTempFile(UUID.randomUUID().toString(), ".pdf"); + + try { + long begin = System.currentTimeMillis(); + String command = "%s -f %s -O %s -T wdFormatPDF"; + command = String.format(command, documentConfig.getDocToProgram(), tempFilePath, pdfPath); + Process p = Runtime.getRuntime().exec(command); + p.waitFor(); + long end = System.currentTimeMillis(); + log.info("docto take time in millis:" + (end - begin)); + return Files.readAllBytes(pdfPath); + } catch (Exception e) { + e.printStackTrace(); + return null; + } finally { + Files.delete(tempFilePath); + Files.delete(pdfPath); + } + } catch (Exception e) { + log.error("word to pdf error", e); + return null; + } + } + + @Override + public byte[] wordToImage(byte[] source, String targetFormat) { + try { + byte[] pdfBytes = wordToPdf(source, true); + long start = System.currentTimeMillis(); + PDDocument document = PDDocument.load(new ByteArrayInputStream(pdfBytes)); + PDFRenderer pdfRenderer = new PDFRenderer(document); + BufferedImage bim = pdfRenderer.renderImageWithDPI( + 0, 300, ImageType.RGB); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ImageIOUtil.writeImage(bim, targetFormat, bos, 300); + document.close(); + log.info("word to image=======consuming:{} milliseconds", System.currentTimeMillis() - start); + + return bos.toByteArray(); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + +} diff --git a/tl-server/src/main/java/com/optima/document/tl/server/config/DocumentConfig.java b/tl-server/src/main/java/com/optima/document/tl/server/config/DocumentConfig.java new file mode 100644 index 0000000..36a7709 --- /dev/null +++ b/tl-server/src/main/java/com/optima/document/tl/server/config/DocumentConfig.java @@ -0,0 +1,41 @@ +package com.optima.document.tl.server.config; + +import com.optima.document.api.DocumentService; +import com.optima.document.tl.server.api.DocumentServiceImpl; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.net.Socket; + +/** + * 服务端配置 + * @author Elias + * @date 2021-09-28 16:12 + */ +@Configuration +@ConfigurationProperties(prefix = "document") +@Data +public class DocumentConfig { + + private String docToProgram; + + /** + * 文档接口 + * @return httpinvoker + */ + @Bean(name = "/document-service") + HttpInvokerServiceExporter wordService(DocumentService documentService) { + HttpInvokerServiceExporter exporter = new HttpInvokerServiceExporter(); + exporter.setService( documentService ); + exporter.setServiceInterface(DocumentService.class); + return exporter; + } + +} diff --git a/tl-server/src/main/resources/application.yml b/tl-server/src/main/resources/application.yml new file mode 100644 index 0000000..04ffcae --- /dev/null +++ b/tl-server/src/main/resources/application.yml @@ -0,0 +1,4 @@ +server: + port: 9004 +document: + doc-to-program: C:\\DocumentServer\\docto.exe diff --git a/tl-server/src/main/resources/banner.txt b/tl-server/src/main/resources/banner.txt new file mode 100644 index 0000000..4b44157 --- /dev/null +++ b/tl-server/src/main/resources/banner.txt @@ -0,0 +1,6 @@ +██████╗ ██████╗ ██████╗██╗ ██╗███╗ ███╗███████╗███╗ ██╗████████╗ +██╔══██╗██╔═══██╗██╔════╝██║ ██║████╗ ████║██╔════╝████╗ ██║╚══██╔══╝ +██║ ██║██║ ██║██║ ██║ ██║██╔████╔██║█████╗ ██╔██╗ ██║ ██║ +██║ ██║██║ ██║██║ ██║ ██║██║╚██╔╝██║██╔══╝ ██║╚██╗██║ ██║ +██████╔╝╚██████╔╝╚██████╗╚██████╔╝██║ ╚═╝ ██║███████╗██║ ╚████║ ██║ +╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═══╝ ╚═╝ \ No newline at end of file