中国领先的IT技术网站
|
|

Java中常用的几种DOCX转PDF方法

将DOCX文档转化为PDF是项目中常见的需求之一,目前主流的方法可以分为两大类,一类是利用各种Office应用进行转换,譬如Microsoft Office、WPS以及LiberOffice,另一种是利用各种语言提供的对于Office文档读取的接口(譬如Apache POI)然后使用专门的PDFGenerator库,譬如IText进行PDF构建。

作者:王下邀月熊_Chevalier来源:segmentfault|2016-09-06 22:16

沙龙活动 | 去哪儿、陌陌、ThoughtWorks在自动化运维中的实践!10.28不见不散!


DOCX2PDF

将DOCX文档转化为PDF是项目中常见的需求之一,目前主流的方法可以分为两大类,一类是利用各种Office应用进行转换,譬如Microsoft Office、WPS以及LiberOffice,另一种是利用各种语言提供的对于Office文档读取的接口(譬如Apache POI)然后使用专门的PDFGenerator库,譬如IText进行PDF构建。总的来说,从样式上利用Office应用可以保证较好的样式,不过相对而言效率会比较低。其中Microsoft Office涉及版权,不可轻易使用(笔者所在公司就被抓包了),WPS目前使用比较广泛,不过存在超链接截断问题,即超过256个字符的超链接会被截断,LiberOffice的样式排版相对比较随意。而利用POI接口进行读取与生成的方式性能较好,适用于对于格式要求不是很高的情况。另外还有一些封装好的在线工具或者命令行工具,譬如docx2pdf与OfficeToPDF。

MicroSoft Office

本部分的核心代码如下,全部代码参考这里:

  1. private ActiveXComponent oleComponent = null
  2. private Dispatch activeDoc = null
  3. private final static String APP_ID = "Word.Application"
  4.  
  5. // Constants that map onto Word's WdSaveOptions enumeration and that 
  6. // may be passed to the close(int) method 
  7. public static final int DO_NOT_SAVE_CHANGES = 0; 
  8. public static final int PROMPT_TO_SAVE_CHANGES = -2; 
  9. public static final int SAVE_CHANGES = -1; 
  10.  
  11. // These constant values determine whether or not tha application 
  12. // instance will be displyed on the users screen or not
  13. public static final boolean VISIBLE = true
  14. public static final boolean HIDDEN = false
  15.  
  16. /** 
  17.  * Create a new instance of the JacobWordSearch class using the following 
  18.  * parameters. 
  19.  * 
  20.  * @param visibility A primitive boolean whose value will determine whether 
  21.  *                   or not the Word application will be visible to the user. Pass true 
  22.  *                   to display Word, false otherwise. 
  23.  */ 
  24. public OfficeConverter(boolean visibility) { 
  25.     this.oleComponent = new ActiveXComponent(OfficeConverter.APP_ID); 
  26.     this.oleComponent.setProperty("Visible", new Variant(visibility)); 
  27.  
  28. /** 
  29.  * Open ana existing Word document. 
  30.  * 
  31.  * @param docName An instance of the String class that encapsulates the 
  32.  *                path to and name of a valid Word file. Note that there are a few 
  33.  *                limitations applying to the format of this String; it must specify 
  34.  *                the absolute path to the file and it must not use the single forward 
  35.  *                slash to specify the path separator. 
  36.  */ 
  37. public void openDoc(String docName) { 
  38.     Dispatch disp = null
  39.     Variant var = null
  40.     // First get a Dispatch object referencing the Documents collection - for 
  41.     // collections, think of ArrayLists of objects. 
  42.     var = Dispatch.get(this.oleComponent, "Documents"); 
  43.     disp = var.getDispatch(); 
  44.     // Now call the Open method on the Documents collection Dispatch object 
  45.     // to both open the file and add it to the collection. It would be possible 
  46.     // to open a series of files and access each from the Documents collection 
  47.     // but for this example, it is simpler to store a reference to the 
  48.     // active document in a private instance variable. 
  49.     var = Dispatch.call(disp, "Open", docName); 
  50.     this.activeDoc = var.getDispatch(); 
  51.  
  52. /** 
  53.  * There is more than one way to convert the document into PDF format, you 
  54.  * can either explicitly use a FileConvertor object or call the 
  55.  * ExportAsFixedFormat method on the active document. This method opts for 
  56.  * the latter and calls the ExportAsFixedFormat method passing the name 
  57.  * of the file along with the integer value of 17. This value maps onto one 
  58.  * of Word's constants called wdExportFormatPDF and causes the application 
  59.  * to convert the file into PDF format. If you wanted to do so, for testing 
  60.  * purposes, you could add another value to the args array, a Boolean value 
  61.  * of true. This would open the newly converted document automatically. 
  62.  * 
  63.  * @param filename 
  64.  */ 
  65. public void publishAsPDF(String filename) { 
  66.     // The code to expoort as a PDF is 17 
  67.     //Object args = new Object{filename, new Integer(17), new Boolean(true)}; 
  68.     Object args = new Object { 
  69.         filename, new Integer(17) 
  70.     } ; 
  71.     Dispatch.call(this.activeDoc, "ExportAsFixedFormat", args); 
  72.  
  73. /** 
  74.  * Called to close the active document. Note that this method simply 
  75.  * calls the overloaded closeDoc(int) method passing the value 0 which 
  76.  * instructs Word to close the document and discard any changes that may 
  77.  * have been made since the document was opened or edited. 
  78.  */ 
  79. public void closeDoc() { 
  80.     this.closeDoc(JacobWordSearch.DO_NOT_SAVE_CHANGES); 
  81.  
  82. /** 
  83.  * Called to close the active document. It is possible with this overloaded 
  84.  * version of the close() method to specify what should happen if the user 
  85.  * has made changes to the document that have not been saved. There are three 
  86.  * possible value defined by the following manifest constants; 
  87.  * DO_NOT_SAVE_CHANGES - Close the document and discard any changes 
  88.  * the user may have made. 
  89.  * PROMPT_TO_SAVE_CHANGES - Display a prompt to the user asking them 
  90.  * how to proceed. 
  91.  * SAVE_CHANGES - Save the changes the user has made to the document. 
  92.  * 
  93.  * @param saveOption A primitive integer whose value indicates how the close 
  94.  *                   operation should proceed if the user has made changes to the active 
  95.  *                   document. Note that no checks are made on the value passed to 
  96.  *                   this argument. 
  97.  */ 
  98. public void closeDoc(int saveOption) { 
  99.     Object args = {new Integer(saveOption)}; 
  100.     Dispatch.call(this.activeDoc, "Close", args); 
  101.  
  102. /** 
  103.  * Called once processing has completed in order to close down the instance 
  104.  * of Word. 
  105.  */ 
  106. public void quit() { 
  107.     Dispatch.call(this.oleComponent, "Quit"); 
  108.  

WPS

Java调用WPS或pdfcreator的com接口实现doc转pdf

本文的核心代码如下,完整代码查看这里:

  1.  @Override 
  2.         public boolean convert(String word, String pdf) { 
  3.             File pdfFile = new File(pdf); 
  4.             File wordFile = new File(word); 
  5.             boolean convertSuccessfully = false
  6.  
  7.             ActiveXComponent wps = null
  8.             ActiveXComponent doc = null
  9.  
  10.  
  11.             try { 
  12.                 wps = new ActiveXComponent("KWPS.Application"); 
  13.  
  14. //                Dispatch docs = wps.getProperty("Documents").toDispatch(); 
  15. //                Dispatch d = Dispatch.call(docs, "Open", wordFile.getAbsolutePath(), falsetrue).toDispatch(); 
  16. //                Dispatch.call(d, "SaveAs", pdfFile.getAbsolutePath(), 17); 
  17. //                Dispatch.call(d, "Close"false); 
  18.  
  19.                 doc = wps.invokeGetComponent("Documents"
  20.                         .invokeGetComponent("Open", new Variant(wordFile.getAbsolutePath())); 
  21.  
  22.                 try { 
  23.                     doc.invoke("SaveAs"
  24.                             new Variant(new File("C:\\Users\\lotuc\\Documents\\mmm.pdf").getAbsolutePath()), 
  25.                             new Variant(17)); 
  26.                     convertSuccessfully = true
  27.                 } catch (Exception e) { 
  28.                     logger.warning("生成PDF失败"); 
  29.                     e.printStackTrace(); 
  30.                 } 
  31.  
  32.                 File saveAsFile = new File("C:\\Users\\lotuc\\Documents\\saveasfile.doc"); 
  33.                 try { 
  34.                     doc.invoke("SaveAs", saveAsFile.getAbsolutePath()); 
  35.                     logger.info("成功另存为" + saveAsFile.getAbsolutePath()); 
  36.                 } catch (Exception e) { 
  37.                     logger.info("另存为" + saveAsFile.getAbsolutePath() + "失败"); 
  38.                     e.printStackTrace(); 
  39.                 } 
  40.             } finally { 
  41.                 if (doc == null) { 
  42.                     logger.info("打开文件 " + wordFile.getAbsolutePath() + " 失败"); 
  43.                 } else { 
  44.                     try { 
  45.                         logger.info("释放文件 " + wordFile.getAbsolutePath()); 
  46.                         doc.invoke("Close"); 
  47.                         doc.safeRelease(); 
  48.                     } catch (Exception e1) { 
  49.                         logger.info("释放文件 " + wordFile.getAbsolutePath() + " 失败"); 
  50.                     } 
  51.                 } 
  52.  
  53.                 if (wps == null) { 
  54.                     logger.info("加载 WPS 控件失败"); 
  55.                 } else { 
  56.                     try { 
  57.                         logger.info("释放 WPS 控件"); 
  58.                         wps.invoke("Quit"); 
  59.                         wps.safeRelease(); 
  60.                     } catch (Exception e1) { 
  61.                         logger.info("释放 WPS 控件失败"); 
  62.                     } 
  63.                 } 
  64.             } 
  65.  
  66.             return convertSuccessfully; 
  67.         }  

LiberOffice

Convert Microsoft Word to PDF - using Java and LibreOffice (UNO API)

LiberOffice本身提供了一个命令行工具进行转换,在你安装好了LiberOffice之后

  1. /usr/local/bin/soffice --convert-to pdf:writer_pdf_Export /Users/lotuc/Downloads/test.doc 

如果有打开的libreoffice实例, 要穿入env选项指定一个工作目录

  1. /usr/local/bin/soffice "-env:UserInstallation=file:///tmp/LibreOffice_Conversion_abc" --convert-to pdf:writer_pdf_Export /Users/lotuc/Downloads/test.doc 

首先我们需要安装好LiberOffice,然后将依赖的Jar包添加到classpath中:I

  1. Install Libre Office 
  2.  
  3. Create a Java project in your favorite editor and add these to your class path: 
  4.   [Libre Office Dir]/URE/java/juh.jar 
  5.   [Libre Office Dir]/URE/java/jurt.jar 
  6.   [Libre Office Dir]/URE/java/ridl.jar 
  7.   [Libre Office Dir]/program/classes/unoil.jar  

然后我们需要启动一个LiberOffice进程:

  1. import java.util.Date
  2. import java.io.File; 
  3. import com.sun.star.beans.PropertyValue; 
  4. import com.sun.star.comp.helper.Bootstrap; 
  5. import com.sun.star.frame.XComponentLoader; 
  6. import com.sun.star.frame.XDesktop; 
  7. import com.sun.star.frame.XStorable; 
  8. import com.sun.star.lang.XComponent; 
  9. import com.sun.star.lang.XMultiComponentFactory; 
  10. import com.sun.star.text.XTextDocument; 
  11. import com.sun.star.uno.UnoRuntime; 
  12. import com.sun.star.uno.XComponentContext; 
  13. import com.sun.star.util.XReplaceDescriptor; 
  14. import com.sun.star.util.XReplaceable; 
  15.  
  16. public class MailMergeExample { 
  17.  
  18. public static void main(String[] args) throws Exception { 
  19.  
  20.  // Initialise 
  21.  XComponentContext xContext = Bootstrap.bootstrap(); 
  22.  
  23.  XMultiComponentFactory xMCF = xContext.getServiceManager(); 
  24.   
  25.  Object oDesktop = xMCF.createInstanceWithContext( 
  26.       "com.sun.star.frame.Desktop", xContext); 
  27.   
  28.  XDesktop xDesktop = (XDesktop) UnoRuntime.queryInterface( 
  29.       XDesktop.class, oDesktop);  

接下来我们需要加载目标Doc文档:

  1. // Load the Document 
  2. String workingDir = "C:/projects/"
  3. String myTemplate = "letterTemplate.doc"
  4.  
  5. if (!new File(workingDir + myTemplate).canRead()) { 
  6.  throw new RuntimeException("Cannot load template:" + new File(workingDir + myTemplate)); 
  7.  
  8. XComponentLoader xCompLoader = (XComponentLoader) UnoRuntime 
  9.  .queryInterface(com.sun.star.frame.XComponentLoader.class, xDesktop); 
  10.  
  11. String sUrl = "file:///" + workingDir + myTemplate; 
  12.  
  13. PropertyValue[] propertyValues = new PropertyValue[0]; 
  14.  
  15. propertyValues = new PropertyValue[1]; 
  16. propertyValues[0] = new PropertyValue(); 
  17. propertyValues[0].Name = "Hidden"
  18. propertyValues[0].Value = new Boolean(true); 
  19.  
  20. XComponent xComp = xCompLoader.loadComponentFromURL( 
  21.  sUrl, "_blank", 0, propertyValues); 

然后我们可以使用如下方式对内容进行替换:

  1. // Search and replace 
  2. XReplaceDescriptor xReplaceDescr = null
  3. XReplaceable xReplaceable = null
  4.  
  5. XTextDocument xTextDocument = (XTextDocument) UnoRuntime 
  6.   .queryInterface(XTextDocument.class, xComp); 
  7.  
  8. xReplaceable = (XReplaceable) UnoRuntime 
  9.   .queryInterface(XReplaceable.class, xTextDocument); 
  10.  
  11. xReplaceDescr = (XReplaceDescriptor) xReplaceable 
  12.   .createReplaceDescriptor(); 
  13.  
  14. // mail merge the date 
  15. xReplaceDescr.setSearchString("<date>"); 
  16. xReplaceDescr.setReplaceString(new Date().toString()); 
  17. xReplaceable.replaceAll(xReplaceDescr); 
  18.  
  19. // mail merge the addressee 
  20. xReplaceDescr.setSearchString("<addressee>"); 
  21. xReplaceDescr.setReplaceString("Best Friend"); 
  22. xReplaceable.replaceAll(xReplaceDescr); 
  23.  
  24. // mail merge the signatory 
  25. xReplaceDescr.setSearchString("<signatory>"); 
  26. xReplaceDescr.setReplaceString("Your New Boss"); 
  27. xReplaceable.replaceAll(xReplaceDescr);  

然后可以输出到PDF中:

  1. // save as a PDF 
  2. XStorable xStorable = (XStorable) UnoRuntime 
  3.   .queryInterface(XStorable.class, xComp); 
  4.  
  5. propertyValues = new PropertyValue[2]; 
  6. propertyValues[0] = new PropertyValue(); 
  7. propertyValues[0].Name = "Overwrite"
  8. propertyValues[0].Value = new Boolean(true); 
  9. propertyValues[1] = new PropertyValue(); 
  10. propertyValues[1].Name = "FilterName"
  11. propertyValues[1].Value = "writer_pdf_Export"
  12.  
  13. // Appending the favoured extension to the origin document name 
  14. String myResult = workingDir + "letterOutput.pdf"
  15. xStorable.storeToURL("file:///" + myResult, propertyValues); 
  16.  
  17. System.out.println("Saved " + myResult); 

xdocreport

本文的核心代码如下,完整代码查看这里:

  1. /** 
  2.  * @param inpuFile 输入的文件流 
  3.  * @param outFile  输出的文件对象 
  4.  * @return 
  5.  * @function 利用Apache POI从输入的文件中生成PDF文件 
  6.  */ 
  7. @SneakyThrows 
  8. public static void convertWithPOI(InputStream inpuFile, File outFile) { 
  9.  
  10.     //从输入的文件流创建对象 
  11.     XWPFDocument document = new XWPFDocument(inpuFile); 
  12.  
  13.     //创建PDF选项 
  14.     PdfOptions pdfOptions = PdfOptions.create();//.fontEncoding("windows-1250"
  15.  
  16.     //为输出文件创建目录 
  17.     outFile.getParentFile().mkdirs(); 
  18.  
  19.     //执行PDF转化 
  20.     PdfConverter.getInstance().convert(document, new FileOutputStream(outFile), pdfOptions); 
  21.  
  22.  
  23. /** 
  24.  * @param inpuFile 
  25.  * @param outFile 
  26.  * @param renderParams 
  27.  * @function 先将渲染参数填入模板DOCX文件然后生成PDF 
  28.  */ 
  29. @SneakyThrows 
  30. public static void convertFromTemplateWithFreemarker(InputStream inpuFile, File outFile, Map<String, Object> renderParams) { 
  31.  
  32.     //创建Report实例 
  33.     IXDocReport report = XDocReportRegistry.getRegistry().loadReport( 
  34.             inpuFile, TemplateEngineKind.Freemarker); 
  35.  
  36.     //创建上下文 
  37.     IContext context = report.createContext(); 
  38.  
  39.     //填入渲染参数 
  40.     renderParams.forEach((s, o) -> { 
  41.         context.put(s, o); 
  42.     }); 
  43.  
  44.     //创建输出流 
  45.     outFile.getParentFile().mkdirs(); 
  46.  
  47.     //创建转化参数 
  48.     Options options = Options.getTo(ConverterTypeTo.PDF).via( 
  49.             ConverterTypeVia.XWPF); 
  50.  
  51.     //执行转化过程 
  52.     report.convert(context, options, new FileOutputStream(outFile)); 
  53.  

【编辑推荐】

  1. 十分钟理解 Java 中的动态代理
  2. 为何Java序列化漏洞并未被修复?
  3. 立足GitHub学编程:13个不容错过的Java项目
  4. Oracle宣布Java EE 8改进计划,旨在提高云竞争力
  5. 14位IT高管及技术大牛谈Java的生态系统
【责任编辑:枯木 TEL:(010)68476606】

点赞 0
分享:
大家都在看
猜你喜欢

读 书 +更多

Fedora Core 5 Linux架站与网管

Linux出色的网络功能堪称当今操作系统中的佼佼者,无论在功能还是在支持能力上都有令人满意的表现。Linux内置的多个服务器,几乎包含了目前...

订阅51CTO邮刊

点击这里查看样刊

订阅51CTO邮刊
× Python最火的编程语言