diff --git a/README.md b/README.md index 199776e..b2a8932 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,13 @@ 文书服务 -api下为调用的接口,在需要处理文书的项目中引用,以`fegin方式`配置Service后进行调用 ++ api下为调用的接口,在需要处理文书的项目中引用,以`Http Invoker`配置Service后进行调用 + + 参考[在Spring Boot中使用Http Invoker](https://codeleading.com/article/15413828287/) 的`Client`部分 -server下为使用jacob处理word文书,使用openoffice来进行pdf转换,`java -jar xxx.jar`启动 ++ [jod-document-server](document-server/jod-document-server)下使用poi-tl处理word文件,使用jacob处理调用[LibreOffice](https://zh-cn.libreoffice.org/)来进行格式转换,`java -jar xxx.jar`启动 + + 参考:[springboot整合libreoffice(两种方式,使用本地和远程的libreoffice);docker中同时部署应用和libreoffice](https://blog.csdn.net/qq_42882229/article/details/140917550) + + 在Linux下,需要注意word文件的字体,必须在Linux中存在 + + 若出现格式问题,需先使用LibreOffice打开修复后,在进行转换 -tl-server下仅进行word转pdf,使用的docto调用office来转换,`java -jar xxx.jar`启动 \ No newline at end of file ++ [docto-document-server](document-server/docto-document-server)下使用poi-tl处理word文件,使用docto调用`Microsoft Office`来转换格式,`java -jar xxx.jar`启动 + + 仅能运行在Windows,兼容性最好,比较慢 \ No newline at end of file diff --git a/api/pom.xml b/api/pom.xml deleted file mode 100644 index fdeb470..0000000 --- a/api/pom.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - 4.0.0 - com.optima - document-api - 2.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/LegacyDocumentService.java b/api/src/main/java/com/optima/document/api/LegacyDocumentService.java deleted file mode 100644 index f4c9610..0000000 --- a/api/src/main/java/com/optima/document/api/LegacyDocumentService.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.optima.document.api; - -import java.util.List; -import java.util.Map; - -/** - * 文档接口,此类中用的jacob生成word文档,插入图片等,使用openoffice进行格式转换 - * - * @author Elias - * @since 2021-09-28 16:00 - */ -public interface LegacyDocumentService extends BaseDocumentService { - /** - * 通过jacob向文档中插入图片,仅server模块下实现 - * - * @param source 文档流 - * @param toFindText 需要替换的文本 - * @param imgSource 图片流 - * @param width 宽度 - * @param height 高度 - * @return word文档流 - */ - default byte[] insertJpeg(byte[] source, String toFindText, byte[] imgSource, int width, int height) { - throw new UnsupportedOperationException(); - } - - /** - * 通过jacob向文档中插入多张图片,仅server模块下实现 - * - * @param source 文档流 - * @param toFindText 需要替换的文本 - * @param imgSource 图片流列表 - * @param width 宽度 - * @param height 高度 - * @return word文档流 - */ - default byte[] insertJpeg(byte[] source, String toFindText, List imgSource, int width, int height) { - throw new UnsupportedOperationException(); - } - - /** - * 通过jacob向word填充属性字段,仅server模块下实现 - * - * @param source 文档流 - * @param infoMap 字段集合,${key}为占位字符,value为对应替换内容 - * @return word文档流 - */ - default byte[] fieldToWord(byte[] source, Map infoMap) { - throw new UnsupportedOperationException(); - } -} diff --git a/document-api/pom.xml b/document-api/pom.xml new file mode 100644 index 0000000..6c71a4c --- /dev/null +++ b/document-api/pom.xml @@ -0,0 +1,58 @@ + + + 4.0.0 + + com.optima + document + 2.0.0 + + document-api + Document API + 文档操作api + + + + + org.projectlombok + lombok + 1.16.20 + provided + + + + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.3.0 + + + + + com.example.MyApp + + + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.2.1 + + + attach-sources + package + + jar-no-fork + + + + + + + diff --git a/api/src/main/java/com/optima/document/api/BaseDocumentService.java b/document-api/src/main/java/com/optima/document/api/BaseDocumentService.java similarity index 59% rename from api/src/main/java/com/optima/document/api/BaseDocumentService.java rename to document-api/src/main/java/com/optima/document/api/BaseDocumentService.java index 71be642..f7a4de2 100644 --- a/api/src/main/java/com/optima/document/api/BaseDocumentService.java +++ b/document-api/src/main/java/com/optima/document/api/BaseDocumentService.java @@ -1,17 +1,34 @@ package com.optima.document.api; +import java.util.Map; + /** * @author yanghuanglin * @since 2022/12/28 */ public interface BaseDocumentService { /** - * 格式转换,server和tl-server下均实现 + * 格式转换 * - * @param source 源文件流,tl-server下仅支持docx格式 + * @param source 源文件流,仅支持docx格式 * @param sourceExtension 源文件后缀名,不包含"." * @param targetExtension 目标文件后缀名,不包含"." - * @param targetFormat 目标文件格式,需与目标文件后缀名匹配,server下此参数无效。,tl-server下参考: word格式 + * @return 转换后的文件流 + */ + default byte[] convert(byte[] source, String sourceExtension, String targetExtension) { + return convert(source, sourceExtension, targetExtension, null); + } + + /** + * 格式转换 + * + * @param source 源文件流,仅支持docx格式 + * @param sourceExtension 源文件后缀名,不包含"." + * @param targetExtension 目标文件后缀名,不包含"." + * @param targetFormat 目标文件格式,需与目标文件后缀名匹配。 + * server下此参数无效。 + * tl-server下参考: + * word格式 * 或 excel格式 * 或 powerpoint格式 * @return 转换后的文件流 @@ -21,9 +38,20 @@ public interface BaseDocumentService { } /** - * word转为pdf,server和tl-server下均实现 + * 通过调用poi生成word * - * @param source word文件流,tl-server下仅支持docx格式 + * @param sourceTemplate word模版流,仅支持docx格式 + * @param dataModel 数据模型 + * @return word文档流 + */ + default byte[] generateWord(byte[] sourceTemplate, Map dataModel) { + throw new UnsupportedOperationException(); + } + + /** + * word转为pdf + * + * @param source word文件流,仅支持docx格式 * @param sourceFormat 源文件后缀名,不包含".",tl-server下此参数无效 * @param clear 是否清除占位符,如果为true,则在tl-server下源文件只支持docx格式 * @return pdf文档流 @@ -35,7 +63,7 @@ public interface BaseDocumentService { /** * 通过pdfbox将word转图片 * - * @param source word文件流,tl-server下仅支持docx格式 + * @param source word文件流,仅支持docx格式 * @param sourceExtension 源文件后缀名,不包含".",server下此参数无效 * @param targetExtension 目标格式 支持jpeg, jpg, gif, tiff or png * @return 图片流 @@ -45,7 +73,7 @@ public interface BaseDocumentService { } /** - * doc转为docx,server和tl-server下均实现 + * doc转为docx * * @param source doc文档流 * @return docx文档流 @@ -55,7 +83,7 @@ public interface BaseDocumentService { } /** - * xls转为xlsx,server和tl-server下均实现 + * xls转为xlsx * * @param source xls文档流 * @return xlsx文档流 diff --git a/api/src/main/java/com/optima/document/api/DocumentService.java b/document-api/src/main/java/com/optima/document/api/DocumentService.java similarity index 62% rename from api/src/main/java/com/optima/document/api/DocumentService.java rename to document-api/src/main/java/com/optima/document/api/DocumentService.java index 81b6d47..a86b261 100644 --- a/api/src/main/java/com/optima/document/api/DocumentService.java +++ b/document-api/src/main/java/com/optima/document/api/DocumentService.java @@ -1,7 +1,5 @@ package com.optima.document.api; -import java.util.Map; - /** * 文档接口,此类中用的poi2生成word文档,使用docto进行格式转换 * @@ -10,18 +8,7 @@ import java.util.Map; */ public interface DocumentService extends BaseDocumentService { /** - * 通过调用poi生成word,仅tl-server模块下实现,仅支持docx格式 - * - * @param sourceTemplate word模版流,仅支持docx格式 - * @param dataModel 数据模型 - * @return word文档流 - */ - default byte[] generateWord(byte[] sourceTemplate, Map dataModel) { - throw new UnsupportedOperationException(); - } - - /** - * 通过调用poi将word转pdf,仅tl-server模块下实现,如果clear为true,则仅支持docx格式 + * 通过调用poi将word转pdf,如果clear为true,则仅支持docx格式 * * @param source word模版流,仅支持docx格式 * @param clear 是否清除占位符 diff --git a/document-server/docto-document-server/pom.xml b/document-server/docto-document-server/pom.xml new file mode 100644 index 0000000..542217e --- /dev/null +++ b/document-server/docto-document-server/pom.xml @@ -0,0 +1,20 @@ + + + 4.0.0 + + com.optima + document-server + 2.0.0 + + docto-document-server + 2.0.0 + Docto Document server + Docto 文档操作服务 + + + + + + + diff --git a/tl-server/src/main/java/com/optima/document/tl/server/DocumentServer.java b/document-server/docto-document-server/src/main/java/com/optima/document/server/DoctoDocumentServer.java similarity index 60% rename from tl-server/src/main/java/com/optima/document/tl/server/DocumentServer.java rename to document-server/docto-document-server/src/main/java/com/optima/document/server/DoctoDocumentServer.java index 30f27a1..c627082 100644 --- a/tl-server/src/main/java/com/optima/document/tl/server/DocumentServer.java +++ b/document-server/docto-document-server/src/main/java/com/optima/document/server/DoctoDocumentServer.java @@ -1,13 +1,13 @@ -package com.optima.document.tl.server; +package com.optima.document.server; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication -public class DocumentServer { +public class DoctoDocumentServer { public static void main(String[] args) { - SpringApplication.run(DocumentServer.class, args); + SpringApplication.run(DoctoDocumentServer.class, args); } } diff --git a/tl-server/src/main/java/com/optima/document/tl/server/api/DocumentServiceImpl.java b/document-server/docto-document-server/src/main/java/com/optima/document/server/api/DocumentServiceImpl.java similarity index 92% rename from tl-server/src/main/java/com/optima/document/tl/server/api/DocumentServiceImpl.java rename to document-server/docto-document-server/src/main/java/com/optima/document/server/api/DocumentServiceImpl.java index bc068d0..c77d154 100644 --- a/tl-server/src/main/java/com/optima/document/tl/server/api/DocumentServiceImpl.java +++ b/document-server/docto-document-server/src/main/java/com/optima/document/server/api/DocumentServiceImpl.java @@ -1,4 +1,4 @@ -package com.optima.document.tl.server.api; +package com.optima.document.server.api; import com.deepoove.poi.XWPFTemplate; import com.deepoove.poi.config.Configure; @@ -11,7 +11,7 @@ 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 com.optima.document.server.config.DocumentConfig; import lombok.extern.slf4j.Slf4j; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.rendering.ImageType; @@ -42,11 +42,13 @@ import java.util.UUID; @Slf4j @Service public class DocumentServiceImpl implements DocumentService { + @Resource + private DocumentConfig documentConfig; /** * word模版引擎配置 */ - private static final Configure wtlConfig = Configure.builder().buildGramer("${", "}") + Configure wtlConfig = Configure.builder().buildGramer("${", "}") .setValidErrorHandler(new Configure.DiscardHandler()) .addPlugin('%', new ListRenderPolicy() { @Override @@ -84,9 +86,6 @@ public class DocumentServiceImpl implements DocumentService { }) .build(); - @Resource - private DocumentConfig documentConfig; - public byte[] convert(byte[] source, String sourceExtension, String targetExtension, String targetFormat) { try { sourceExtension = sourceExtension.replace(".", ""); @@ -108,17 +107,17 @@ public class DocumentServiceImpl implements DocumentService { Process p = Runtime.getRuntime().exec(command); p.waitFor(); long end = System.currentTimeMillis(); - log.info("docto convert {} to {} take time in millis:{}", sourceExtension, targetExtension, (end - begin)); + log.info("convert {} to {} take time in millis:{}", sourceExtension, targetExtension, (end - begin)); return Files.readAllBytes(targetPath); } catch (Exception e) { - e.printStackTrace(); + log.error("convert {} to {} error", sourceExtension, targetExtension, e); return null; } finally { Files.delete(sourcePath); Files.delete(targetPath); } } catch (IOException e) { - log.error("docto convert error", e); + log.error("convert error", e); } return null; } @@ -155,9 +154,9 @@ public class DocumentServiceImpl implements DocumentService { }); template.writeAndClose(bos); source = bos.toByteArray(); - log.info("清空占位符=======耗时:{} 毫秒", System.currentTimeMillis() - start); + log.info("clear placeholder take time in millis:{}", System.currentTimeMillis() - start); } catch (Exception e) { - log.error("清空标签失败:", e); + log.error("clear placeholder error", e); return null; } } diff --git a/tl-server/src/main/java/com/optima/document/tl/server/config/DocumentConfig.java b/document-server/docto-document-server/src/main/java/com/optima/document/server/config/DocumentConfig.java similarity index 91% rename from tl-server/src/main/java/com/optima/document/tl/server/config/DocumentConfig.java rename to document-server/docto-document-server/src/main/java/com/optima/document/server/config/DocumentConfig.java index a0ebf0a..3a9c27e 100644 --- a/tl-server/src/main/java/com/optima/document/tl/server/config/DocumentConfig.java +++ b/document-server/docto-document-server/src/main/java/com/optima/document/server/config/DocumentConfig.java @@ -1,4 +1,4 @@ -package com.optima.document.tl.server.config; +package com.optima.document.server.config; import com.optima.document.api.DocumentService; import lombok.Data; @@ -13,7 +13,7 @@ import org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter; * @author Elias * @since 2021-09-28 16:12 */ -@SuppressWarnings("VulnerableCodeUsages") +@SuppressWarnings("deprecation") @Configuration @ConfigurationProperties(prefix = "document") @Data diff --git a/document-server/docto-document-server/src/main/resources/application.yml b/document-server/docto-document-server/src/main/resources/application.yml new file mode 100644 index 0000000..7eddcf7 --- /dev/null +++ b/document-server/docto-document-server/src/main/resources/application.yml @@ -0,0 +1,5 @@ +server: + port: 9004 + +document: + doc-to-program: C:\\DocumentServer\\docto.exe \ No newline at end of file diff --git a/server/src/main/resources/banner.txt b/document-server/docto-document-server/src/main/resources/banner.txt similarity index 100% rename from server/src/main/resources/banner.txt rename to document-server/docto-document-server/src/main/resources/banner.txt diff --git a/document-server/jod-document-server/pom.xml b/document-server/jod-document-server/pom.xml new file mode 100644 index 0000000..5395f6e --- /dev/null +++ b/document-server/jod-document-server/pom.xml @@ -0,0 +1,32 @@ + + + 4.0.0 + + com.optima + document-server + 2.0.0 + + jod-document-server + 2.0.0 + Jod Document Server + Jod 文档操作服务 + + + 4.4.9 + + + + + org.jodconverter + jodconverter-local + ${jodconverter.version} + + + + org.jodconverter + jodconverter-spring-boot-starter + ${jodconverter.version} + + + diff --git a/server/src/main/java/com/optima/document/server/DocumentServer.java b/document-server/jod-document-server/src/main/java/com/optima/document/server/JodDocumentServer.java similarity index 72% rename from server/src/main/java/com/optima/document/server/DocumentServer.java rename to document-server/jod-document-server/src/main/java/com/optima/document/server/JodDocumentServer.java index 437ed56..581fc53 100644 --- a/server/src/main/java/com/optima/document/server/DocumentServer.java +++ b/document-server/jod-document-server/src/main/java/com/optima/document/server/JodDocumentServer.java @@ -4,10 +4,10 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication -public class DocumentServer { +public class JodDocumentServer { public static void main(String[] args) { - SpringApplication.run(DocumentServer.class, args); + SpringApplication.run(JodDocumentServer.class, args); } } diff --git a/document-server/jod-document-server/src/main/java/com/optima/document/server/api/DocumentServiceImpl.java b/document-server/jod-document-server/src/main/java/com/optima/document/server/api/DocumentServiceImpl.java new file mode 100644 index 0000000..b1e53c1 --- /dev/null +++ b/document-server/jod-document-server/src/main/java/com/optima/document/server/api/DocumentServiceImpl.java @@ -0,0 +1,196 @@ +package com.optima.document.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 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 org.apache.poi.xwpf.usermodel.XWPFRun; +import org.jodconverter.core.DocumentConverter; +import org.jodconverter.core.document.DefaultDocumentFormatRegistry; +import org.jodconverter.core.document.DocumentFormat; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * 服务接口实现 + * + * @author Elias + * @since 2021-09-28 16:18 + */ +@Slf4j +@Service +public class DocumentServiceImpl implements DocumentService { + @Resource + private DocumentConverter documentConverter; + + /** + * 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(); + + /** + * 文件格式转换 + * + * @param source 源文件流 + * @param sourceExtension 源文件后缀名,不包含"." + * @param targetExtension 目标文件后缀名 + * @return 转换后的文件流 + */ + public byte[] convert(byte[] source, String sourceExtension, String targetExtension) { + try { + sourceExtension = sourceExtension.replace(".", ""); + targetExtension = targetExtension.replace(".", ""); + long start = System.currentTimeMillis(); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + DocumentFormat sourceFormat = DefaultDocumentFormatRegistry.getFormatByExtension(sourceExtension); + DocumentFormat targetFormat = DefaultDocumentFormatRegistry.getFormatByExtension(targetExtension); + assert sourceFormat != null; + assert targetFormat != null; + documentConverter.convert(new ByteArrayInputStream(source)) + .as(sourceFormat) + .to(bos) + .as(targetFormat) + .execute(); + log.info("convert {} to {} take time in millis:{}", sourceExtension, targetExtension, System.currentTimeMillis() - start); + return bos.toByteArray(); + } catch (Exception e) { + log.error("convert {} to {} error", sourceExtension, targetExtension, e); + } + return null; + } + + public byte[] convert(byte[] source, String sourceExtension, String targetExtension, String targetFormat) { + return convert(source, sourceExtension, targetExtension); + } + + public byte[] generateWord(byte[] sourceTemplate, Map dataModel) { + long start = System.currentTimeMillis(); + XWPFTemplate template = XWPFTemplate.compile(new ByteArrayInputStream(sourceTemplate), wtlConfig).render(dataModel); + try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) { + template.write(bos); + log.info("word generate take time in millis:{}", System.currentTimeMillis() - start); + return bos.toByteArray(); + } catch (Exception e) { + log.error("word generate error", e); + return null; + } + } + + public byte[] wordToPdf(byte[] source, String sourceFormat, boolean clear) { + return wordToPdf(source, clear); + } + + 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("clear placeholder take time in millis:{}", System.currentTimeMillis() - start); + } catch (Exception e) { + log.error("clear placeholder error", e); + return null; + } + } + return convert(source, "docx", "pdf"); + } catch (Exception e) { + log.error("word to pdf error", e); + return null; + } + } + + public byte[] wordToImage(byte[] source, String targetExtension) { + 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, targetExtension, bos, 300); + document.close(); + log.info("word to image take time in millis:{}", System.currentTimeMillis() - start); + return bos.toByteArray(); + } catch (Exception e) { + log.error("word to image error", e); + } + return null; + } + + public byte[] wordToImage(byte[] source, String sourceExtension, String targetExtension) { + return wordToImage(source, targetExtension); + } + + public byte[] docToDocx(byte[] source) { + return convert(source, "doc", "docx"); + } + + public byte[] xlsToXlsx(byte[] source) { + return convert(source, "xls", "xlsx"); + } + +} diff --git a/server/src/main/java/com/optima/document/server/config/DocumentConfig.java b/document-server/jod-document-server/src/main/java/com/optima/document/server/config/DocumentConfig.java similarity index 66% rename from server/src/main/java/com/optima/document/server/config/DocumentConfig.java rename to document-server/jod-document-server/src/main/java/com/optima/document/server/config/DocumentConfig.java index 9e3b9d5..effd536 100644 --- a/server/src/main/java/com/optima/document/server/config/DocumentConfig.java +++ b/document-server/jod-document-server/src/main/java/com/optima/document/server/config/DocumentConfig.java @@ -1,6 +1,6 @@ package com.optima.document.server.config; -import com.optima.document.api.LegacyDocumentService; +import com.optima.document.api.DocumentService; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; @@ -13,26 +13,21 @@ import org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter; * @author Elias * @since 2021-09-28 16:12 */ -@SuppressWarnings("VulnerableCodeUsages") +@SuppressWarnings("deprecation") @Configuration @ConfigurationProperties(prefix = "document") @Data public class DocumentConfig { - - private String tempDir; - - private String openOfficeHome; - /** * 文档接口 * * @return httpinvoker */ @Bean(name = "/document-service") - HttpInvokerServiceExporter wordService(LegacyDocumentService legacyDocumentService) { + HttpInvokerServiceExporter wordService(DocumentService documentService) { HttpInvokerServiceExporter exporter = new HttpInvokerServiceExporter(); - exporter.setService(legacyDocumentService); - exporter.setServiceInterface(LegacyDocumentService.class); + exporter.setService(documentService); + exporter.setServiceInterface(DocumentService.class); return exporter; } diff --git a/document-server/jod-document-server/src/main/resources/application.yml b/document-server/jod-document-server/src/main/resources/application.yml new file mode 100644 index 0000000..a44f16b --- /dev/null +++ b/document-server/jod-document-server/src/main/resources/application.yml @@ -0,0 +1,16 @@ +server: + port: 9005 + +jodconverter: + local: + # 启动本地转换 + enabled: true + # macOS下:program/soffice 的 program 所在目录 或 MacOS/soffice 的 MacOS 所在目录 + # windows下:program/soffice.exe 的 program 所在目录 + # linux下:program/soffice.bin 的 program 所在目录 + # 如果不配置,则自动查找 + office-home: /Applications/LibreOffice.app/Contents + # 一个端口表示一个常驻进程,默认只有一个进程,端口为2002 + port-numbers: [ 2002 ] + # 每个进程最多处理多个任务,默认为200 + max-tasks-per-process: 200 diff --git a/tl-server/src/main/resources/banner.txt b/document-server/jod-document-server/src/main/resources/banner.txt similarity index 100% rename from tl-server/src/main/resources/banner.txt rename to document-server/jod-document-server/src/main/resources/banner.txt diff --git a/document-server/jod-document-server/src/test/java/com/optima/document/server/JodDocumentServerTest.java b/document-server/jod-document-server/src/test/java/com/optima/document/server/JodDocumentServerTest.java new file mode 100644 index 0000000..8088311 --- /dev/null +++ b/document-server/jod-document-server/src/test/java/com/optima/document/server/JodDocumentServerTest.java @@ -0,0 +1,42 @@ +package com.optima.document.server; + +import com.optima.document.api.DocumentService; +import org.apache.commons.io.FileUtils; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.HashMap; +import java.util.Map; + +@Disabled +public class JodDocumentServerTest { + + @Test + public void test() throws IOException { + DocumentService documentService = buildDocumentService(); + + File sourceDocx = new File("/Users/yanghuanglin/Downloads/test.docx"); + File targetPdf = new File("/Users/yanghuanglin/Downloads/test.pdf"); + + Map params = new HashMap<>(); + params.put("callerName", "张三"); + byte[] sourceBytes = documentService.generateWord(Files.readAllBytes(sourceDocx.toPath()), params); + + byte[] bytes = documentService.wordToPdf(sourceBytes, false); + FileUtils.writeByteArrayToFile(targetPdf, bytes); + } + + private static DocumentService buildDocumentService() { + // 创建客户端代理 + HttpInvokerProxyFactoryBean factoryBean = new HttpInvokerProxyFactoryBean(); + factoryBean.setServiceUrl("http://10.211.55.13:9005/document-service"); + factoryBean.setServiceInterface(DocumentService.class); + factoryBean.afterPropertiesSet(); + + return (DocumentService) factoryBean.getObject(); + } +} diff --git a/document-server/pom.xml b/document-server/pom.xml new file mode 100644 index 0000000..e664496 --- /dev/null +++ b/document-server/pom.xml @@ -0,0 +1,163 @@ + + + 4.0.0 + + com.optima + document + 2.0.0 + + document-server + pom + Document Server + 文档操作模块 + + docto-document-server + jod-document-server + + + + 2.0.25 + 2.17.2 + 1.16.20 + 1.12.1 + + + + + com.optima + document-api + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-aop + + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.apache.pdfbox + pdfbox-tools + + + + com.deepoove + poi-tl + + + + org.apache.logging.log4j + log4j-to-slf4j + + + + org.apache.logging.log4j + log4j-api + + + + org.projectlombok + lombok + provided + + + + + + + org.springframework.boot + spring-boot-starter-web + 2.7.18 + + + org.springframework.boot + spring-boot-starter-aop + 2.7.18 + + + org.springframework.boot + spring-boot-starter-test + 2.7.18 + test + + + + com.optima + document-api + 2.0.0 + + + + org.apache.pdfbox + pdfbox-tools + ${pdfbox-tools.version} + + + + com.deepoove + poi-tl + ${poi-tl.version} + + + + org.apache.logging.log4j + log4j-to-slf4j + ${log4j.version} + + + + org.apache.logging.log4j + log4j-api + ${log4j.version} + + + + org.projectlombok + lombok + ${lombok.version} + provided + + + + + + document-server-${version} + + + org.apache.maven.plugins + maven-resources-plugin + 3.3.1 + + + copy-resources + process-resources + + copy-resources + + + ${project.build.directory} + + + src/main/resources + + application.yml + + + + + + + + + + diff --git a/pom.xml b/pom.xml index 08296c9..f2a85ac 100644 --- a/pom.xml +++ b/pom.xml @@ -1,16 +1,40 @@ - 4.0.0 - com.optima - document - 2.0.0 - pom - document - 文档操作模块 - - api - server - tl-server - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + com.optima + document + 2.0.0 + pom + Document + 文档操作模块 + + document-api + document-server + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 8 + 8 + + + + org.springframework.boot + spring-boot-maven-plugin + 2.7.18 + + + + repackage + + + + + + diff --git a/server/pom.xml b/server/pom.xml deleted file mode 100644 index 6110b38..0000000 --- a/server/pom.xml +++ /dev/null @@ -1,136 +0,0 @@ - - - 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 2.0.3.RELEASE - - - com.optima - document-server - 2.0.0 - document server - 文档操作服务 - - 1.8 - 8.5.100 - - - - org.springframework.boot - spring-boot-starter-web - - - - com.optima - document-api - 2.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/api/LegacyDocumentServiceImpl.java b/server/src/main/java/com/optima/document/server/api/LegacyDocumentServiceImpl.java deleted file mode 100644 index f01cdd9..0000000 --- a/server/src/main/java/com/optima/document/server/api/LegacyDocumentServiceImpl.java +++ /dev/null @@ -1,120 +0,0 @@ -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.LegacyDocumentService; -import com.optima.document.server.config.DocumentConfig; -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 org.springframework.stereotype.Service; - -import javax.annotation.Resource; -import java.awt.image.BufferedImage; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.util.List; -import java.util.Map; - -/** - * 服务接口实现 - * - * @author Elias - * @since 2021-09-28 16:18 - */ -@Slf4j -@Service -public class LegacyDocumentServiceImpl implements LegacyDocumentService { - @Resource - private DocumentConfig documentConfig; - - /** - * 文件格式转换 - * - * @param source 源文件流 - * @param sourceExtension 源文件后缀名,不包含"." - * @param targetExtension 目标文件后缀名 - * @return 转换后的文件流 - */ - private byte[] convert(byte[] source, String sourceExtension, String targetExtension) { - try { - sourceExtension = sourceExtension.replace(".",""); - targetExtension = targetExtension.replace(".",""); - long start = System.currentTimeMillis(); - String command = "%s -headless -accept=\"socket,host=127.0.0.1,port=8100;urp;\" -nofirststartwizard"; - Process p = Runtime.getRuntime().exec(String.format(command, documentConfig.getOpenOfficeHome())); - 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(sourceExtension), bos, formatRegistry.getFormatByFileExtension(targetExtension)); - connection.disconnect(); - p.destroy(); - log.info("openoffice convert {} to {} take time in millis:{}", sourceExtension, targetExtension, System.currentTimeMillis() - start); - return bos.toByteArray(); - } catch (Exception e) { - e.printStackTrace(); - } - return null; - } - - 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 sourceExtension, boolean clear) { - try { - if (clear) { - source = DocToPdfUtil.clearPlaceholder(source); - } - return convert(source, sourceExtension, "pdf"); - } catch (Exception e) { - e.printStackTrace(); - } - return null; - } - - public byte[] convert(byte[] source, String sourceExtension, String targetExtension, String targetFormat) { - return convert(source, sourceExtension, targetExtension); - } - - public byte[] wordToImage(byte[] source, String sourceExtension, String targetExtension) { - try { - byte[] pdfBytes = wordToPdf(source, sourceExtension, 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, targetExtension, bos, 300); - document.close(); - return bos.toByteArray(); - } catch (Exception e) { - e.printStackTrace(); - } - return null; - } - - public byte[] docToDocx(byte[] source) { - return convert(source, "doc", "docx"); - } - - public byte[] xlsToXlsx(byte[] source) { - return convert(source, "xls", "xlsx"); - } -} 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 deleted file mode 100644 index 5192f39..0000000 --- a/server/src/main/java/com/optima/document/server/utils/CastUtil.java +++ /dev/null @@ -1,493 +0,0 @@ -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 deleted file mode 100644 index 04d3d62..0000000 --- a/server/src/main/java/com/optima/document/server/utils/DocToPdfUtil.java +++ /dev/null @@ -1,792 +0,0 @@ -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 deleted file mode 100644 index 070dffb..0000000 --- a/server/src/main/java/com/optima/document/server/utils/PoiWordUtil.java +++ /dev/null @@ -1,486 +0,0 @@ -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 deleted file mode 100644 index 77863fa..0000000 --- a/server/src/main/resources/application.yml +++ /dev/null @@ -1,5 +0,0 @@ -server: - port: 8090 -document: - open-office-home: D:\\OpenOffice4\\program\\soffice.exe - diff --git a/server/src/main/resources/libs/jacob.jar b/server/src/main/resources/libs/jacob.jar deleted file mode 100644 index c35ad12..0000000 Binary files a/server/src/main/resources/libs/jacob.jar and /dev/null differ diff --git a/server/src/main/resources/libs/jodconverter-2.2.2.jar b/server/src/main/resources/libs/jodconverter-2.2.2.jar deleted file mode 100644 index d033256..0000000 Binary files a/server/src/main/resources/libs/jodconverter-2.2.2.jar and /dev/null differ diff --git a/server/src/main/resources/libs/poi2-1.0.jar b/server/src/main/resources/libs/poi2-1.0.jar deleted file mode 100644 index 8ec5fc1..0000000 Binary files a/server/src/main/resources/libs/poi2-1.0.jar and /dev/null differ diff --git a/tl-server/pom.xml b/tl-server/pom.xml deleted file mode 100644 index 5b8e787..0000000 --- a/tl-server/pom.xml +++ /dev/null @@ -1,96 +0,0 @@ - - - 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 2.0.3.RELEASE - - - com.optima - document-tl-server - 2.0.0 - document tl server - 文档模版引擎服务 - - 1.8 - 8.5.100 - - - - org.springframework.boot - spring-boot-starter-web - - - - com.optima - document-api - 2.0.0 - - - - 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.pdfbox - pdfbox-tools - 2.0.25 - - - com.deepoove - poi-tl - 1.12.1 - - - 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/resources/application.yml b/tl-server/src/main/resources/application.yml deleted file mode 100644 index 04ffcae..0000000 --- a/tl-server/src/main/resources/application.yml +++ /dev/null @@ -1,4 +0,0 @@ -server: - port: 9004 -document: - doc-to-program: C:\\DocumentServer\\docto.exe