65.9K
CodeProject 正在变化。 阅读更多。
Home

如何免费以编程方式创建 HTML、ODT、DOCX 和 PDF 文档

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.40/5 (3投票s)

2023 年 4 月 3 日

CPOL

10分钟阅读

viewsIcon

8075

学习使用完全免费的开源软件创建常用格式的文档

引言

政府、企业和其他组织会创建大量的文档。当他们自动化手动或纸质操作时,会购买每年或每台计算机花费数百甚至数千美元的专有文档创建软件组件。这似乎是巨大的浪费,尤其是当有免费开源软件可以免费完成同样的事情时。

背景

我自费出版了很多书,这些书都是只使用免费开源软件 (FOSS) 创建的。我唯一的开销是互联网接入和电费。当我收听播客或观看其他自出版作者的在线视频时,我 ужаснулся (horrified) 地得知他们花了数千美元才出版一本书。这还不包括营销费用。

在本文中,我将描述任何人都可以免费创建 ODT、DOCX 和 PDF 文件。整个工具链都是 FOSS。此过程可以轻松自动化并集成到应用程序服务器中。

使用 CommonMark (Markdown) 作为源文档

您可以使用纯文本、标记语言、markdown 或直接使用富文本来编写文档。如果您使用 markdown 作为源,那么您可以轻松地将其导出到所有其他形式。HTML (超文本标记语言)标记语言。它显示为富文本。富文本是您在浏览器或 Microsoft Word 或 LibreOffice Writer 等文档编辑器中看到的内容。标记语言是您在网页上执行查看 » 源代码时看到的内容。您在 Word/Writer 中创建的 DOCX/ODT 文件实际上是一个重命名的 zip 文件,其中包含大部分 XML 文件 (eXtended Markup Language)。

Markdown 是标记语言的对立面。它使用普通的纯文本增强功能来不显眼地标记文本。在万维网出现之前,互联网主要是电子邮件。电子邮件、BBS 和 Usenet (新闻组) 用户为他们的消息开发了一种纯文本格式。例如,粗体文本用**星号**括起来。斜体文本用_下划线_括起来。当 John Gruber 和 Aaron Swartz 于 2004 年创建Markdown时,他们进一步扩展了这种风格。它现在是最流行的 markdown 形式。Markdown 以 perl 脚本的形式发布。Markdown 文档通常保存为带有扩展名.md。我用 markdown 写了第一本书,然后这样转换了

perl markdown.pl jokebook.md > jokebook.html

Markdown 的其他实现基于 perl 脚本 (markdown.pl),但存在一些差异。最后,在 2019 年,Jeff Atwood 和 John MacFarlane 发布了一个标准化的 Markdown 实现,称为CommonMark。我在 2020 年的某个时候了解到它,并获得了第一本关于 CommonMark 的书的吹嘘资本。这本书名为CommonMark Ready Reference。它在许多电子书商店都可以免费获取。但是,我将为您提供一个快速介绍,以便您了解它的样子。

Markdown primer

与 MarkDown 不同,CommonMark 有一个正确的规范。它还有一个用 C 语言编写的实现,速度极快。(我在我的网站上提供了 Linux 和 Windows 可执行文件。) 使用基于 C 的可执行文件,您可以像这样转换您的 Markdown/CommonMark 源文档

commonmark  --unsafe --validate-utf8 jokebook.md > jokebook.html

CommonMark 可以生成标题、段落、块引用、图像、链接、列表、代码跨度和代码块、水平分隔线和换行符。但是,也就仅此而已。它无法生成表格和其他花哨的东西。如果您想要这些,您可以编写原始 HTML 并使用-unsafe选项。默认情况下,CommonMark 会省略原始 HTML,以保护软件系统免受代码注入。

CommonMark 可执行文件或 Markdown perl 脚本生成的 HTML 是验证安全的、结构良好的 HTML。但是,它不会包含HTMLHEADTITLEBODY标签。该可执行文件的唯一目的是创建可以直接用于预先存在的页面或 HTML 模板的 HTML 标记。

想象一下,这是 markdown 源文档。

Science Jokes
-------------

* **How many astronauts would it take to a screw a lightbulb?**  
    One to turn the bulb and several to prevent the spacecraft 
    from spinning in the same direction.
* **What did one radio wave say to another?**  
  "You are interfering with my work."
* **What's a radio engineer's favourite food?**  
  A can of tuna.

CommonMark 可以像这样用于转换它

echo '<!DOCTYPE html><html><title>2020 Jokebook</title></head><body>' > jokebook.html
commonmark  --unsafe --validate-utf8 jokebook.md >> jokebook.html
echo '</body></html>' >> jokebook.html

输出的 HTML 将如下所示

<!DOCTYPE html><html><title>2020 Jokebook</title></head><body>
<h2>Science Jokes</h2>
<ul>
<li><strong>How many astronauts would it take to a screw a lightbulb?</strong><br />
One to turn the bulb and several to prevent the spacecraft from 
spinning in the same direction.</li>
<li><strong>What did one radio wave say to another?</strong><br />
&quot;You are interfering with my work.&quot;</li>
<li><strong>What's a radio engineer's favourite food?</strong><br />
A can of tuna.</li>
</ul>
</body></html>

CommonMark 生成的标记从<h2>开始,以</ul>结束。其余的是 HTML 模板。此 HTML 在浏览器中看起来是这样的。

Screenshot of HTML document

使用 LibreOffice 创建 ODT、DOCX 和 PDF

您已经知道 LibreOffice 是 Microsoft Office 的 FOSS 替代品。它有一个文字处理器 Writer (Word 的替代品),电子表格应用程序 Calc (Excel 的替代品),演示文稿幻灯片制作程序 Impress (PowerPoint 的替代品) 以及其他一些应用程序。虽然 LibreOffice 像普通的 GUI 应用程序一样发出声音,但它还有一个温和的命令行界面。

将上述 HTML 文档转换为 ODT 格式

libreoffice --convert-to "odt" jokebook.html

ODT document

您可以使用相同的 HTML 文档并将其转换为 DOCX,以便 Microsoft Office 用户感到满意。(Microsoft Word 可以很好地编辑 ODT 文件。)

libreoffice --convert-to "docx:MS Word 2007 XML" jokebook.html

您可以使用生成的 ODT 或 DOCX 文件并将其转换为 PDF 文件。

libreoffice --convert-to "pdf" jokebook.odt

PDF document

为什么不直接从 HTML 转换为 PDF?为什么要创建中间的 ODT 或 DOCX 文件?因为 HTML 文档没有任何页面大小、边距、页眉和页脚的概念。

创建包含图像的文档

当您转换包含图像的 HTML 文档时,生成的 ODT 或 DOCX 文档会正确显示图像。当您移动文档或将其发送给某人时,图像将消失。这是因为 ODT 或 DOCX 文档中的图像将继续从源图像文件中加载。要解决此问题,您需要将图像编码为文本。这类似于电子邮件消息中的图像和附件是如何编码的—使用base64编码。查看包含附件的电子邮件的邮件源,您会发现文件被编码为纯文本。

而不是使用像这样的图像文件

<img src="lion-and-deer.png" />

…您可以像这样将其编码为文本…

<img src="data:image/png;base64,iVBORw0KGgoAAAA…" />

不,这还不是全部。我已经截断了编码图像的实际文本。完整的文本接近 600 行。如果您好奇,可以尝试这样的命令

base64 lion-and-deer.png

Text-encoded image

您不必手动进行文本编码。LibreOffice 会将图像编码为文本。

echo '<!DOCTYPE html><html><title>2020 Jokebook</title></head><body>' > jokebook.htm
commonmark  --unsafe --validate-utf8 jokebook.md >> jokebook.htm
echo '</body></html>' >> jokebook.htm

libreoffice --convert-to "html:HTML:EmbedImages" jokebook.htm

在这里,.htm文件 (引用外部图像) 是由 CommonMark 创建的。LibreOffice 使用该.htm文件创建了一个包含文本编码图像的.html文件。这个自包含的 HTML 现在是可移植的,不依赖于任何外部文件。当您转换这样的 HTML 文档时,生成的 ODT 或 DOCX 文件也将是自包含的和可移植的。即使您删除源图像文件,您仍然可以在.html.odt.docx文件中看到它。

libreoffice --convert-to "html:HTML:EmbedImages" jokebook.htm
libreoffice --convert-to "odt" jokebook.html
libreoffice --convert-to "docx:MS Word 2007 XML" jokebook.odt
libreoffice --convert-to "pdf" jokebook.odt

Images in a document

增强的文档内容

如前所述,CommonMark 只输出有限的 HTML 标签集。要创建它不支持的内容,您必须在 markdown 中添加原始 HTML。

Animal Jokes
------------

* **Why did the lion cross the road?**  
  Because <span style="color: white; background-color: red; 
  border-radius: 0.5em; border: 2px dashed yellow; ">the buck stops here</span>.  
  ![Lion and deer](lion-and-deer.png)

不要过度使用这种原始 HTML内容。LibreOffice 有自己的有限 HTML 标签和 CSS 样式集,可以进行转换。

Output with raw HTML in Markdown

这张截图说了什么?LibreOffice 不能做圆角,等等。所以,请克制您的兴奋。

这种原始 HTML 的使用很粗糙。它违背了 markdown 的理念。CommonMark 的目的是创建一个结构良好的文档。可以通过在 HTML 模板中包含 CSS 样式来实现特殊的样式。我将此作为家庭作业留给您或您的开发人员。(只需复习一下 CSS 伪类、选择器和属性匹配。) 我给出了上面的例子,以便让概念易于理解。

与样式不同,表格不是花哨的东西。它们是财务文档的基石。对于这些文档,您可以在行内使用原始 HTML。LibreOffice 会正确转换 HTML 表格。

页眉和页脚呢?CommonMark 支持它们吗?可以像 CSS 样式一样添加到 HTML 模板中吗?不幸的是,不能。这种工具链适用于单页文档。或者,不带页眉和页脚的多页文档。如果您需要这些,那么您必须使用一个名为wkhtmltopdf的工具。它本质上是一个无头 Firefox 浏览器,可以将格式丰富的 HTML 文档转换为 PDF。您可以使用单独的 HTML 文件指定页眉和页脚。我用这个工具制作了我所有的书。它支持 LibreOffice 不支持的许多 CSS 样式。如果我依赖 LibreOffice,我的书就不会看起来那么丰富。(wkhtmltopdf基于一个未更新的旧 Firefox 代码库。一个印度人正在维护它。它也有一些 bug。) 与 LibreOffice 不同,wkhtmltopdf可以执行 JavaScript。它有一个选项,可以添加几秒钟的延迟,以便 JavaScript 在 wkhtmltopdf 开始将文档打印为 PDF 之前完成其工作。所以,对于简单的文档,请使用 LibreOffice。对于重型文档,请使用wkhtmltopdf。这两个程序都会使用源文档中的标题来创建 PDF 文档中的书签树。LibreOffice 有一个wkhtmltopdf没有的优势——它可以创建 ePUB 电子书和其他几种类型的文档。

附加文档功能

如果您需要合并两个 ODT 或 DOCX 文档,我不知道也没有关心任何工具可以做到。在 markdown 源文档中进行合并,然后创建组合文档。

对于 PDF,有很多工具。对于我的书,我需要将一些页面大小的图像与内页 PDF 合并。为此,我使用ImageMagickpdftk

magick title-page.png -resize 100% front.pdf
pdftk front.pdf jokes.pdf output book.pdf

pdftk是一个非常强大的工具,它不仅仅是合并 PDF 页面或从不同文档中整理它们。它还可以为您的 PDF 添加水印和加密。

pdftk book.pdf output book-encrypted.pdf \
      encrypt_128bit \
      owner_pw RcHrDsTlMn^012 \
      user_pw FrSfTWrFnDtn^321

为了增加最后一点格调,为 PDF 添加元数据。

echo "InfoBegin" > meta.txt
echo "InfoKey: Title" >> meta.txt
echo "InfoValue: 2020 Jokebook by V. Subhash" >> meta.txt

echo "InfoBegin" >> meta.txt
echo "InfoKey: Subject" >> meta.txt
echo "InfoValue: Fresh Clean Jokes" >> meta.txt

echo "InfoBegin" >> meta.txt
echo "InfoKey: Author" >> meta.txt
echo "InfoValue: V. Subhash (&#169; 2022 V. Subhash. All rights reserved.)" >> meta.txt

pdftk "jokebook.pdf" update_info meta.txt output 2020-jokebook.pdf

Document properties

无论我用这些免费 PDF 工具做什么都无法完成的,我都会编写自己的自定义实用程序,使用iText Java。例如,当发现pdftk无法处理我的一些更大的书时,我就可以通过我用iText创建的 JAR 文件可执行文件来完成。

关注点

  • 在线应用中使用: 文档创建需要大量的计算工作,包括 CPU 使用率、磁盘访问和内存需求。不要将这些工具直接集成到 Web 应用程序中。使用操作系统在启动时启动的 dæmon (后台进程或服务) 来执行文档创建。您的 Web 应用程序只需将文档创建作业排队到此 dæmon。当文档准备好后,dæmon 应该调用 Web 应用程序中的 API 例程来通知作业已完成。否则,当用户数量增加时,您的在线用户将崩溃您的系统。
  • 捐赠: 如果本文中提到的免费软件帮助您降低了成本,那么请务必向他们的项目捐赠一些钱。如果您是独立软件供应商 (ISV),那么请告知您的客户您并非编写了所有代码,系统的一部分依赖于 FOSS 工具。告知客户根据该系统中免费软件的使用情况进行捐赠。iText 的创建者有一个令人难以置信的故事,讲述了 Google 如何拒绝为在 Google Analytics、Google Docs 和 Google Calendar 等产品中使用他的 PDF 库提供比 T 恤和马克杯更多的东西。不要像他们那样。在许多国家,企业有法定义务将一部分利润分配给慈善事业。他们中的大多数会很乐意将此类捐款转给开源项目。
  • 本文最初发表于Open Source For You杂志。

历史

  • 2023 年 4 月 2 日:初始版本
© . All rights reserved.