用纯命令行生成真实 PDF:LibreOffice CLI 踩坑记录
LibreOffice 没有 CLI 界面
LibreOffice 是开源办公套件里的扛把子——Writer、Calc、Impress 全能用。但它的问题是:官方并没有提供一个友好的命令行工具。安装完之后,你能用的是 soffice 这个可执行文件,参数长这样:
soffice --headless --convert-to pdf --outdir ./output ./doc.odt
这个命令可以跑,但问题是:它需要一个已经存在的 .odt 文件才能工作。你怎么用纯命令生成一个 .odt 文件?
这就是 CLI-Anything 的 LibreOffice 模块要解决的问题。
解决思路:ODF 生成 + LibreOffice headless
LibreOffice 的文档格式叫 ODF(Open Document Format),本质上是一个 ZIP 包,里面装着 XML 文件和其他资源。Writer 文档的 .odt 文件结构如下:
mimetype # 纯文本,声明文件类型
META-INF/manifest.xml # ZIP 包清单
content.xml # 文档正文内容
styles.xml # 样式定义
也就是说:不需要 LibreOffice 也能生成合法的 ODF 文件。ODF 是一个开放格式,规格写在 OASIS 标准里,可以直接手工构造。
CLI-Anything 的方案分两步:
- 用 Python 生成合法的 ODF 文件(ZIP + XML)
- 用
soffice --headless把 ODF 转换成 PDF
Writer:生成文字文档
# 创建文档
$ cli-anything-libreoffice document new \
--type writer \
--name "Q1 Report" \
-o report.odt
✓ Created Writer document: report.odt
# 添加标题
$ cli-anything-libreoffice --project report.odt \
writer add-heading \
--text "Q1 产品报告" \
--level 1
✓ Added heading: Q1 产品报告 (level 1)
# 添加内容段落
$ cli-anything-libreoffice --project report.odt \
writer add-paragraph \
--text "本季度完成了三个主要版本迭代..."
✓ Added paragraph (86 characters)
# 添加表格
$ cli-anything-libreoffice --project report.odt \
writer add-table \
--rows 4 --cols 3 \
--headers "功能,计划,实际"
✓ Added 4×3 table with headers
# 导出 PDF
$ cli-anything-libreoffice --project report.odt \
export render \
--output q1_report.pdf
✓ Rendered via libreoffice-headless
✓ Output: q1_report.pdf (42,831 bytes)
验证 PDF 是否真的生成了:
$ file q1_report.pdf
q1_report.pdf: PDF document, version 1.4
$ head -c 8 q1_report.pdf
%PDF-1.4
这里用的是真实的 LibreOffice headless 渲染,不是假的。PDF 文件是真的——LibreOffice 帮你把 ODF 转成了 PDF。
Calc:生成电子表格
# 创建表格
$ cli-anything-libreoffice document new \
--type calc \
--name "销售数据" \
-o sales.ods
✓ Created Calc document: sales.ods
# 添加工作表
$ cli-anything-libreoffice --project sales.ods \
calc add-sheet \
--name "2024-Q1"
✓ Added sheet: 2024-Q1
# 设置单元格值
$ cli-anything-libreoffice --project sales.ods \
calc set-cell \
--sheet "2024-Q1" \
--cell A1 \
--value "产品名称"
$ cli-anything-libreoffice --project sales.ods \
calc set-cell \
--sheet "2024-Q1" \
--cell B1 \
--value "销量"
# 批量设置一行数据
$ cli-anything-libreoffice --project sales.ods \
calc set-row \
--sheet "2024-Q1" \
--row 2 \
--values "Widget A,1250,¥125,000"
✓ Set row 2: [Widget A, 1250, ¥125,000]
# 添加公式(统计总和)
$ cli-anything-libreoffice --project sales.ods \
calc set-formula \
--sheet "2024-Q1" \
--cell B6 \
--formula "=SUM(B2:B5)"
✓ Set formula: =SUM(B2:B5)
# 导出 PDF
$ cli-anything-libreoffice --project sales.ods \
export render \
--output sales_q1.pdf \
--pagesheet "2024-Q1"
✓ Rendered sheet "2024-Q1" to PDF
Impress:生成幻灯片
# 创建演示文稿
$ cli-anything-libreoffice document new \
--type impress \
--name "年度汇报" \
-o annual.odp
✓ Created Impress document: annual.odp
# 添加幻灯片
$ cli-anything-libreoffice --project annual.odp \
impress add-slide \
--title "年度业务汇报" \
--layout title
✓ Added slide 1: title layout
# 添加内容页
$ cli-anything-libreoffice --project annual.odp \
impress add-slide \
--title "Q1-Q4 业绩回顾" \
--layout bullets
✓ Added slide 2: bullets layout
# 添加图表(作为外部图片)
$ cli-anything-libreoffice --project annual.odp \
impress add-image \
--slide 2 \
--image ./chart.png \
--position "center" \
--width 10cm
✓ Added image to slide 2
# 导出为 PDF(演示模式,每张幻灯片一页)
$ cli-anything-libreoffice --project annual.odp \
export render \
--output annual_presentation.pdf
✓ Rendered 3 slides to PDF
ODF 生成的技术细节
LibreOffice CLI 的核心在后端。以 Writer 为例,ODF 的 content.xml 大概长这样:
<?xml version="1.0" encoding="UTF-8"?>
<office:document-content
xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0"
xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0"
xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0">
<office:body>
<office:text>
<text:h text:outline-level="1">Q1 产品报告</text:h>
<text:p>本季度完成了三个主要版本迭代...</text:p>
<table:table>
<table:table-header>
<table:table-cell>功能</table:table-cell>
<table:table-cell>计划</table:table-cell>
<table:table-cell>实际</table:table-cell>
</table:table-header>
<!-- 行数据 -->
</table:table>
</office:text>
</office:body>
</office:document-content>
CLI-Anything 的后端模块用 Python 生成这段 XML,zip 成 ODF 包,然后调 LibreOffice headless 转 PDF。整个流程里最难的不是 ODF 生成,而是处理 LibreOffice headless 的各种边界情况——字体缺失、路径编码、超时控制、输出目录创建……这些都是踩过的坑。
Headless LibreOffice 的常见问题
字体缺失:在无头模式下运行,LibreOffice 可能找不到系统中安装的中文字体。需要在 user份模板 里预设字体路径,或者在调用时指定 --infilter 参数。
路径编码:Windows 路径里有空格或中文时,需要用引号包裹,并用 cygpath 转换。CLI-Anything 在 Windows 上有专门的路径处理逻辑。
启动超时:LibreOffice headless 第一次启动比较慢。CLI-Anything 在测试里用 timeout=120 来兜底,如果 2 分钟还没启动成功就报错,而不是无限等待。
PDF 输出格式:LibreOffice 的 PDF 导出参数在不同版本之间有细微差异。测试里会检查多个版本的兼容性。
JSON 模式与 Agent 工作流
LibreOffice CLI 特别适合和文件处理 pipeline 结合。Agent 可以用 JSON 模式获取文档结构信息:
$ cli-anything-libreoffice --json document info --project report.odt
{
"name": "Q1 产品报告",
"type": "writer",
"pages": 4,
"elements": {
"headings": 2,
"paragraphs": 8,
"tables": 1,
"images": 0
},
"file_size": 18432,
"modified": true
}
一个典型的自动化报告生成 pipeline:
# 1. 从数据库导出数据
python export_sales.py --period Q1 --output sales_data.json
# 2. 用 Python 生成报告内容
python generate_report.py --data sales_data.json --template report.odt
# 3. LibreOffice 转 PDF
cli-anything-libreoffice --project report.odt \
export render --output Q1_report_final.pdf
# 4. 发邮件
python send_email.py --to team@company.com --attachment Q1_report_final.pdf

浙公网安备 33010602011771号