Loading

用纯命令行生成真实 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 的方案分两步:

  1. 用 Python 生成合法的 ODF 文件(ZIP + XML)
  2. 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
posted @ 2026-03-25 15:31  饭勺oO  阅读(1404)  评论(0)    收藏  举报