58 lines
2.9 KiB
Python
58 lines
2.9 KiB
Python
#!/usr/bin/env python3
|
||
"""Office to PDF using the preinstalled LibreOffice, isolated per invocation."""
|
||
from pathlib import Path
|
||
import shutil
|
||
import subprocess
|
||
import sys
|
||
import tempfile
|
||
|
||
from _pdf_common import SkillArgumentParser, output_pdf, new_temp_pdf, publish_temp_file, run_cli
|
||
|
||
FORMATS={'.docx','.doc','.odt','.rtf','.pptx','.ppt','.odp','.xlsx','.xls','.ods'}
|
||
|
||
|
||
def convert(args):
|
||
from pypdf import PdfReader
|
||
source=Path(args.input).expanduser().resolve()
|
||
if not source.is_file() or source.suffix.lower() not in FORMATS or not 0 < source.stat().st_size <= 25*1024*1024:
|
||
raise ValueError('输入需为不超过 25 MiB 的本地 Office 文档;不支持把 PDF 直接反向转成可编辑 Office')
|
||
if not 1 <= args.timeout <= 600:
|
||
raise ValueError('timeout 必须在 1–600 秒之间')
|
||
executable=shutil.which('soffice') or shutil.which('libreoffice')
|
||
if not executable:
|
||
raise RuntimeError('基础镜像缺少 LibreOffice,需要更新镜像')
|
||
target=output_pdf(args.output,args.overwrite)
|
||
temporary=new_temp_pdf(target)
|
||
try:
|
||
with tempfile.TemporaryDirectory(prefix='pdf-office-') as folder:
|
||
root=Path(folder)
|
||
incoming=root/'input'; outgoing=root/'output'; profile=root/'profile'
|
||
incoming.mkdir(); outgoing.mkdir(); profile.mkdir()
|
||
local=incoming/('source'+source.suffix.lower()); shutil.copyfile(source,local)
|
||
command=[executable,'-env:UserInstallation='+profile.as_uri(),'--headless','--nologo','--nodefault',
|
||
'--nofirststartwizard','--convert-to','pdf','--outdir',str(outgoing),str(local)]
|
||
completed=subprocess.run(command,capture_output=True,text=True,timeout=args.timeout,check=False)
|
||
result=outgoing/'source.pdf'
|
||
if completed.returncode or not result.is_file():
|
||
raise RuntimeError('Office 转 PDF 失败:'+(completed.stderr or completed.stdout)[-1500:])
|
||
count=len(PdfReader(result).pages)
|
||
if not count:
|
||
raise ValueError('转换结果没有页面')
|
||
shutil.copyfile(result,temporary)
|
||
publish_temp_file(temporary,target,args.overwrite)
|
||
finally:
|
||
temporary.unlink(missing_ok=True)
|
||
return {'source':str(source),'path':str(target),'page_count':count,'engine':'libreoffice','requires_visual_review':True,
|
||
'note':'转换前需在对应 Office skill 中重算公式并核对字体、图表和打印范围。'}
|
||
|
||
|
||
def main(argv=None):
|
||
parser=SkillArgumentParser(description='Office 文档导出 PDF,源文件保持不变')
|
||
parser.add_argument('--input',required=True); parser.add_argument('--output',required=True)
|
||
parser.add_argument('--timeout',type=int,default=180); parser.add_argument('--overwrite',action='store_true')
|
||
return run_cli(lambda:convert(parser.parse_args(sys.argv[1:] if argv is None else argv)))
|
||
|
||
|
||
if __name__=='__main__':
|
||
raise SystemExit(main())
|