- Русские Блоги
- Используйте poi + itextpdf для преобразования word в pdf
- задний план
- выбор плана
- # О зависимости
- О коде
- О яме
- 1. Не удается найти метод, сообщить об ошибке
- 2. Ярлык, сгенерированный jsoup, не закрывается, что приводит к сбою преобразования html в pdf и сообщении об ошибке
- 3. После конвертации китайцы заменяются на заглушки
- 4. Воспользуйтесь первым из описанных выше способов, чтобы решить проблему с китайским шрифтом и сделать ошибку.
Русские Блоги
Используйте poi + itextpdf для преобразования word в pdf
На самом деле существует множество решений для преобразования word в pdf!
задний план
Недавно нам просто нужно было сделать такую функцию.Требуется преобразовать шаблон word в pdf после подписания. С этой целью я потратил немного времени на поиск решений в Интернете. В этот период я встречал некоторые ямы, вот рекорд.
выбор плана
Прежде всего, поскольку код работает на сервере Linux, общее решение, основанное на функции Windows Office, не будет работать. Это исключало некоторые программы, которые работали хорошо, как Джейкоб.
Во-вторых, программное обеспечение, такое как office, не может быть установлено на нашем сервере, а пакет maven jar, импортированный извне, очень строг, и время относительно невелико. Принимая во внимание эти факторы, невозможно детально разобраться в каждой из этих программ. В конце концов, я выбрал poi + itextpdf, более традиционный метод.
Программа poi + itextpdf использует для передачи html. Другими словами, сначала конвертируйте word в формат html, а затем конвертируйте html в pdf. HTML имеет формат String и может быть легко изменен с помощью jsoup или строковых манипуляций. Это также одна из причин, почему я выбрал эту схему.
# О зависимости
Первая встречающаяся проблема — это зависимость от maven.Примеры в Интернете либо неполные, либо отсутствуют, либо имеют проблемы с версией. После окончательной проверки получаются следующие зависимости maven.
Обратите внимание на версию пакета jar! Я успешно протестировал его только на версии, указанной ниже.
dependency> groupId>org.apache.poi/groupId> artifactId>poi-ooxml/artifactId> version>3.14/version> /dependency> dependency> groupId>org.apache.poi/groupId> artifactId>poi-scratchpad/artifactId> version>3.14/version> /dependency> dependency> groupId>fr.opensagres.xdocreport/groupId> artifactId>xdocreport/artifactId> version>1.0.6/version> /dependency> dependency> groupId>org.apache.poi/groupId> artifactId>poi-ooxml-schemas/artifactId> version>3.14/version> /dependency> dependency> groupId>org.apache.poi/groupId> artifactId>ooxml-schemas/artifactId> version>1.3/version> /dependency> dependency> groupId>com.itextpdf.tool/groupId> artifactId>xmlworker/artifactId> version>5.5.11/version> /dependency> dependency> groupId>com.itextpdf/groupId> artifactId>itext-asian/artifactId> version>5.2.0/version> /dependency>
О коде
package cn.hewie.pdf; import com.itextpdf.text.BaseColor; import com.itextpdf.text.Font; import com.itextpdf.text.FontProvider; import com.itextpdf.text.PageSize; import com.itextpdf.text.pdf.BaseFont; import com.itextpdf.text.pdf.PdfWriter; import com.itextpdf.tool.xml.XMLWorkerHelper; import org.apache.commons.lang3.StringUtils; import org.apache.poi.hwpf.HWPFDocument; import org.apache.poi.hwpf.converter.PicturesManager; import org.apache.poi.hwpf.converter.WordToHtmlConverter; import org.apache.poi.hwpf.usermodel.PictureType; import org.apache.poi.xwpf.converter.core.BasicURIResolver; import org.apache.poi.xwpf.converter.core.FileImageExtractor; import org.apache.poi.xwpf.converter.xhtml.XHTMLConverter; import org.apache.poi.xwpf.converter.xhtml.XHTMLOptions; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.jsoup.Jsoup; import org.jsoup.nodes.Element; import org.jsoup.nodes.Entities; import org.jsoup.select.Elements; import org.w3c.dom.Document; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import java.io.*; import java.nio.charset.Charset; /** * Используйте poi + itextpdf для преобразования word в pdf * Сначала конвертируйте слово в HTML, затем конвертируйте HTML в PDF * * @author :hewie * @date :Created in 2020/2/27 22:41 */ public class TestPoi /** * Преобразование файлов формата doc в html * * @param docPath путь к файлу документа * @param imageDir каталог хранения изображений файла doc * @return html */ public static String doc2Html(String docPath, String imageDir) String content = null; ByteArrayOutputStream baos = null; try HWPFDocument wordDocument = new HWPFDocument(new FileInputStream(docPath)); WordToHtmlConverter wordToHtmlConverter = new WordToHtmlConverter(DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument()); wordToHtmlConverter.setPicturesManager(new PicturesManager() @Override public String savePicture(byte[] content, PictureType pictureType, String suggestedName, float widthInches, float heightInches) File file = new File(imageDir + suggestedName); FileOutputStream fos = null; try fos = new FileOutputStream(file); fos.write(content); > catch (IOException e) e.printStackTrace(); > finally try if (fos != null) fos.close(); > > catch (Exception e) e.printStackTrace(); > > return imageDir + suggestedName; > >); wordToHtmlConverter.processDocument(wordDocument); Document htmlDocument = wordToHtmlConverter.getDocument(); DOMSource domSource = new DOMSource(htmlDocument); baos = new ByteArrayOutputStream(); StreamResult streamResult = new StreamResult(baos); TransformerFactory tf = TransformerFactory.newInstance(); Transformer serializer = tf.newTransformer(); serializer.setOutputProperty(OutputKeys.ENCODING, "utf-8"); serializer.setOutputProperty(OutputKeys.INDENT, "yes"); serializer.setOutputProperty(OutputKeys.METHOD, "html"); serializer.transform(domSource, streamResult); > catch (Exception e) e.printStackTrace(); > finally try if (baos != null) content = new String(baos.toByteArray(), "utf-8"); baos.close(); > > catch (Exception e) e.printStackTrace(); > > return content; > /** * Преобразование файлов формата docx в html * * @param docxPath путь к файлу docx * @param imageDir каталог для хранения изображений в формате docx * @return html */ public static String docx2Html(String docxPath, String imageDir) String content = null; FileInputStream in = null; ByteArrayOutputStream baos = null; try // 1> Загрузить документ в XWPFDocument in = new FileInputStream(new File(docxPath)); XWPFDocument document = new XWPFDocument(in); // 2> Анализировать конфигурацию XHTML (здесь IURIResolver задает каталог, в котором хранятся изображения) XHTMLOptions options = XHTMLOptions.create(); // Каталог для хранения картинок в word options.setExtractor(new FileImageExtractor(new File(imageDir))); options.URIResolver(new BasicURIResolver(imageDir)); options.setIgnoreStylesIfUnused(false); options.setFragment(true); // 3> Преобразовать XWPFDocument в XHTML baos = new ByteArrayOutputStream(); XHTMLConverter.getInstance().convert(document, baos, options); > catch (Exception e) e.printStackTrace(); > finally try if (in != null) in.close(); > if (baos != null) content = new String(baos.toByteArray(), "utf-8"); baos.close(); > > catch (Exception e) e.printStackTrace(); > > return content; > /** * Используйте jsoup для нормализации html * * @param html html-контент * @return нормализованный HTML */ private static String formatHtml(String html) org.jsoup.nodes.Document doc = Jsoup.parse(html); // Удаляем слишком большую ширину String style = doc.attr("style"); if (StringUtils.isNotEmpty(style) && style.contains("width")) doc.attr("style", ""); > Elements divs = doc.select("div"); for (Element div : divs) String divStyle = div.attr("style"); if (StringUtils.isNotEmpty(divStyle) && divStyle.contains("width")) div.attr("style", ""); > > // jsoup генерирует закрытую метку doc.outputSettings().syntax(org.jsoup.nodes.Document.OutputSettings.Syntax.xml); doc.outputSettings().escapeMode(Entities.EscapeMode.xhtml); return doc.html(); > /** * Конвертировать html в pdf * * @param html html * @param outputPdfPath путь к выходному PDF-файлу */ private static void htmlToPdf(String html, String outputPdfPath) com.itextpdf.text.Document document = null; try // бумага document = new com.itextpdf.text.Document(PageSize.A4); // ручка PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(outputPdfPath)); document.open(); // html в pdf XMLWorkerHelper.getInstance().parseXHtml(writer, document, new ByteArrayInputStream(html.getBytes()), Charset.forName("UTF-8"), new FontProvider() @Override public boolean isRegistered(String s) return false; > @Override public Font getFont(String s, String s1, boolean embedded, float size, int style, BaseColor baseColor) // Настраиваем шрифт Font font = null; try // Вариант 1: использовать локальные шрифты (требуются локальные шрифты) // BaseFont bf = BaseFont.createFont("c:/Windows/Fonts/simsun.ttc,0", BaseFont.IDENTITY_H, BaseFont.EMBEDDED); // Вариант 2: Используйте пакет jar: iTextAsian, поэтому достаточно одного пакета jar BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED); font = new Font(bf, size, style, baseColor); font.setColor(baseColor); > catch (Exception e) e.printStackTrace(); > return font; > >); > catch (Exception e) e.printStackTrace(); > finally if (document != null) document.close(); > > > public static void main(String[] args) throws Exception String basePath = "F:/test/pdf/"; String docPath = basePath + "index.doc"; String docxPath = basePath + "index.docx"; String pdfPath = basePath + "index.pdf"; String imageDir = "F:/test/pdf/image/"; // Тестируем документ в pdf // String docHtml = doc2Html(docPath, imageDir); // docHtml = formatHtml(docHtml); // htmlToPdf(docHtml, pdfPath); // Тестируем docx в pdf String docxHtml = docx2Html(docxPath, imageDir); docxHtml = formatHtml(docxHtml); docxHtml = docxHtml.replace("___", "Чжан Сан"); htmlToPdf(docxHtml, pdfPath); > >
Как мы все знаем, существует несколько версий слова. В этой демонстрации рассматривается только обработка форматов doc и docx.
Кроме того, следует отметить несколько моментов.
- Поскольку в файле Word могут быть изображения, необходим путь для хранения изображений для передачи. Если вы не установите его, вы не увидите изображение в PDF ~
- Поскольку существует проблема, заключающаяся в том, что ширина настройки стиля слишком велика в сгенерированном html (сцена из docx в html), это приведет к появлению большого количества пробелов в сгенерированном PDF-файле (если «бумага» мала, вполне вероятно, что весь PDF-файл будет пустым. из). Следовательно, ширину нужно удалить, а стиль здесь прямо убит, что немного жестоко ~
- В преобразованном шаблоне легко заменить текст, например: введите имя пользователя
Давайте посмотрим на эффект, чувствую себя неплохо.
Исходное слово выглядит следующим образом:
Конвертируется в pdf следующим образом:
О яме
Поговорим о некоторых возникших проблемах!
1. Не удается найти метод, сообщить об ошибке
Exception in thread “main” java.lang.NoSuchMethodError: org.apache.poi.POIXMLDocumentPart.getPackageRelationship()Lorg/apache/poi/openxml4j/opc/PackageRelationship;
После долгого исследования этой причины позже выяснилось, что это версия пакета. Исходные пакеты jar, связанные с poi, были 3.17, но позже выяснилось, что они были изменены на 3.14.
2. Ярлык, сгенерированный jsoup, не закрывается, что приводит к сбою преобразования html в pdf и сообщении об ошибке
com.itextpdf.tool.xml.exceptions.RuntimeWorkerException: Invalid nested tag p found, expected closing tag br.
На этот вопрос был дан ответ на github. Старые друзья joup думают, что некоторые теги, такие как
, В стандарте h5 его закрывать не нужно. Но представленный нами синтаксический анализатор XMLWorker так не считает, поэтому выдает такую ошибку.
К счастью, его можно установить в jsoup, код выглядит следующим образом:
doc.outputSettings().syntax(org.jsoup.nodes.Document.OutputSettings.Syntax.xml); doc.outputSettings().escapeMode(Entities.EscapeMode.xhtml);
3. После конвертации китайцы заменяются на заглушки
Это потому, что нет соответствующего файла шрифта. Есть несколько решений:
1) Используйте системные файлы, которые поставляются с системой. Недостаток в том, что нужно указывать путь. Если он развернут в среде Linux, требуется загрузка файла шрифта.
2) Используйте внешний пакет jar
dependency> groupId>com.itextpdf/groupId> artifactId>itext-asian/artifactId> version>5.2.0/version> /dependency>
Несомненно, это решение намного удобнее первого. В конце концов, есть много вопросов по эксплуатации и обслуживанию.
Оба этих сценария отмечены в коде. Реализация здесь больше не указывается.
4. Воспользуйтесь первым из описанных выше способов, чтобы решить проблему с китайским шрифтом и сделать ошибку.
\ simsun.ttc »с« Identity-H »не распознается или тип шрифта не распознается