Docker安装LibreOffice

LibreOffice是一款开源的办公套件,提供了类似于Microsoft Office的功能,包括文档处理、电子表格、演示文稿、绘图、数据库等多个办公应用程序。LibreOffice 是由 The Document Foundation(文档基金会)开发和维护的。该软件套件的目标是提供一个免费、开放源代码的办公软件替代品,以促进用户的自由和数据的互操作性。

LibreOffice 包含一系列的应用程序,其中最常用的包括:

  1. Writer(文档处理器): 用于创建和编辑文档,类似于 Microsoft Word。
  2. Calc(电子表格程序): 用于创建和编辑电子表格,类似于 Microsoft Excel。
  3. Impress(演示文稿程序): 用于制作演示文稿,类似于 Microsoft PowerPoint。
  4. Draw(绘图程序): 用于创建图形和图表。
  5. Base(数据库程序): 用于创建和管理数据库,类似于 Microsoft Access。
  6. Math(公式编辑器): 用于编辑数学公式。

由于 LibreOffice 是开源的,在某些地方还是无法和商业软件MSOffice,WPS相比的,但是在服务器端转换文档格式是一个不错的选择。

Dockerfile构建

FROM lscr.io/linuxserver/libreoffice:latest

LABEL Author="windcf.com"

COPY ./Fonts.zip /tmp/Fonts.zip

COPY ./get-pip.py /tmp/get-pip.py

RUN unzip -n -d /usr/share/fonts /tmp/Fonts.zip


RUN python /tmp/get-pip.py && \
    pip config set global.index-url https://mirrors.aliyun.com/pypi/simple && \
    pip install fastapi uvicorn python-multipart

RUN rm -f /tmp/get-pip.py /tmp/Fonts.zip
  1. Fonts.zip是从拷贝windows系统的所有字体,以保证文件格式转化字体能正常显示,文字格式不发生变化。可以在容器中使用fc-list查看所有支持的字体
  2. 文件格式转化作为服务调用,需要写一个简单后端,本人决定使用python,基础镜像python版本是3.11,但是没有安装pip,此处下载get-pip.py,安装pip,python环境就配好了。

docker-compose

version: "2.1"
services:
  libreoffice:
    #    image: lscr.io/linuxserver/libreoffice:latest
    image: com.windcf/libreoffice
    container_name: libreoffice
    tty: true
    stdin_open: true
    #    security_opt:
    #      - seccomp:unconfined #optional
    environment:
      #      - PUID=1000
      #      - PGID=1000
      - TZ=Asia/Shanghai
    volumes:
      - ./volumes/libreoffice/docs:/docs
    #    ports:
    #      - "3000:3000"
    #      - "3001:3001"
    entrypoint: [ '/bin/bash' ]
    restart: unless-stopped

以上是在官方的docker-compose改的,注释掉了修改或不需要的部分。默认启动命令下,容器会启动web服务,浏览器访问可看到UI界面,作为接口调用不需要,注释掉相关内容。我们只需要容器保持运行,使用docker exec转化文件即可。

测试

Libreoffice命令行使用说明

  1. 支持的格式非常多,使用图形界面转化格式有可选输出参数,命令行模式下仅支持部分格式设置输出参数。
  2. filter options for Lotus, dBase and Diff files
  3. filter options for CSV files
  4. filter options for PDF files
docker exec libreoffice soffice --help
# 直接将文件内容打印到控制台
docker exec libreoffice soffice --cat /docs/test.docx
# 转化为pdf
docker exec libreoffice --convert-to pdf --outdir /docs /docs/test.docx
# odg转pdf,从第二页开始
soffice --headless --convert-to 'pdf:draw_pdf_Export:{"PageRange":{"type":"string","value":"2-"}}' test.odg
# 批量转化文件夹下doc
soffice --headless --convert-to "html:XHTML Writer File:UTF8" *.doc

接口调用

import os.path
import subprocess
from pathlib import Path
from typing import Tuple

import uvicorn
from fastapi import FastAPI, UploadFile, BackgroundTasks
from fastapi.responses import PlainTextResponse
from starlette.responses import FileResponse

MAX_FILE_SIZE = 10 * 1024 * 1024

TMP_PATH = "/tmp/libreoffice-upload"

if not os.path.exists(TMP_PATH):
    os.mkdir(TMP_PATH)


def clear(paths: Tuple[str, ...]):
    subprocess.run(f'rm -f {" ".join(paths)}', shell=True)


app = FastAPI()


async def save_file(src: str, file: UploadFile):
    assert file.filename.endswith(src)
    assert file.size <= MAX_FILE_SIZE
    filepath = os.path.join(TMP_PATH, file.filename)
    Path(filepath).write_bytes(await file.read())
    return filepath


@app.post("/transfer")
async def transfer(src: str, to: str, file: UploadFile, background_tasks: BackgroundTasks):
    filepath = await save_file(src, file)
    name = os.path.splitext(filepath)[0]
    target = name + '.' + to

    background_tasks.add_task(clear, (filepath, target))

    popen = subprocess.Popen(f"soffice --headless --convert-to {to} --outdir {TMP_PATH} {filepath}",
                             stderr=subprocess.PIPE, stdout=subprocess.PIPE,
                             shell=True)
    popen.wait(60)

    err = popen.stderr.read().decode()
    if err:
        return PlainTextResponse(content=err, status_code=500)

    return FileResponse(path=target, filename=os.path.basename(target))


@app.post("/cat")
async def cat(src: str, file: UploadFile) -> str:
    filepath = await save_file(src, file)
    popen = subprocess.Popen(f"soffice --headless --cat {filepath}", stderr=subprocess.PIPE, stdout=subprocess.PIPE,
                             shell=True)
    popen.wait(60)

    subprocess.run(f'rm -f {filepath}', shell=True)

    err = popen.stderr.read().decode()
    if err:
        return err

    return popen.stdout.read().decode()


if __name__ == '__main__':
    uvicorn.run(app, host='0.0.0.0')

image-20231119161418592

windows上批量转换pdf

实测libreoffice对于部分word 97 2003文档(不知道是因为宏还是因为特殊格式)转化pdf后字体间距,行间距变大,自己新建word2003文档没有这样的问题,试过多种办法无法解决,由于业务中可以离线处理文档,所以在windows上调用word批量转化pdf。

"""
@author: windcf.com
@time: 2023/11/21 9:28 
"""
import os

# pip install pywin32
from win32com.client import gencache, constants

path = r'D:\规章'

word = gencache.EnsureDispatch("Word.Application")

for root, directories, filenames in os.walk(path):
    for filename in filenames:
        if not filename.endswith('doc') and not filename.endswith('docx'):
            continue
        wordpath = os.path.join(root, filename)
        name, _ = os.path.splitext(filename)
        pdfpath = os.path.join(root, name + '.pdf')
        print(pdfpath)
        if os.path.exists(pdfpath):
            continue
        doc = word.Documents.Open(wordpath, ReadOnly=1)

        doc.ExportAsFixedFormat(pdfpath, constants.wdExportFormatPDF,
                                Item=constants.wdExportDocumentWithMarkup,
                                CreateBookmarks=constants.wdExportCreateHeadingBookmarks)
        doc.Close(False)

word.Quit(constants.wdDoNotSaveChanges)

Q.E.D.


一切很好,不缺烦恼。