首页 > 系统相关 >Linux上使用python处理docx转pdf教程

Linux上使用python处理docx转pdf教程

时间:2024-04-02 18:55:31浏览次数:21  
标签:docx python Linux hr paragraph pdf path os

  今天在使用flask将生成好的docx文档转化为pdf的过程中,遇到了一些问题,本来在windows上转化的好好的,但是到了Linux上却是直接报错显示ModuleNotFoundError: No module named 'win32com'

  很明显他说的是在Linux系统下并没有win32com这个模块,所以通过百度发现python使用pdf2docx这个包将docx转化为pdf的使用环境必须为windows,那么在Linux上我们应该使用什么来进行文档的转化呢?百度后发现了一个解决方法:传送门

  但是上述代码的时间应该是有段时间了,pywpsrpc的代码已经更新了,目前的最新使用代码可以去github上访问官方文档

  需要注意的是需要安装好qt5-default, 我使用的Linux版本是Ubuntu23.04,在安装的时候会报错,所以百度后提供了别人的解决方案粘在下面。

  qt5-default安装失败解决方案

  以下是代码解决方案,代码来自于官方,我仅仅调用了其中的函数:


#!/usr/bin/python3

#**
# * Copyright (c) 2020 Weitian Leung
# *
# * This file is part of pywpsrpc.
# *
# * This file is distributed under the MIT License.
# * See the LICENSE file for details.
# *
#*

import os
import sys
import argparse

from pywpsrpc.rpcwpsapi import (createWpsRpcInstance, wpsapi)
from pywpsrpc.common import (S_OK, QtApp)


formats = {
    "doc": wpsapi.wdFormatDocument,
    "docx": wpsapi.wdFormatXMLDocument,
    "rtf": wpsapi.wdFormatRTF,
    "html": wpsapi.wdFormatHTML,
    "pdf": wpsapi.wdFormatPDF,
    "xml": wpsapi.wdFormatXML,
}


class ConvertException(Exception):

    def __init__(self, text, hr):
        self.text = text
        self.hr = hr

    def __str__(self):
        return """Convert failed:
Details: {}
ErrCode: {}
""".format(self.text, hex(self.hr & 0xFFFFFFFF))


def convert_to(paths, format, abort_on_fails=False):
    hr, rpc = createWpsRpcInstance()
    if hr != S_OK:
        raise ConvertException("Can't create the rpc instance", hr)

    hr, app = rpc.getWpsApplication()
    if hr != S_OK:
        raise ConvertException("Can't get the application", hr)

    # we don't need the gui
    app.Visible = False

    docs = app.Documents

    def _handle_result(hr):
        if abort_on_fails and hr != S_OK:
            raise ConvertException("convert_file failed", hr)

    for path in paths:
        abs_path = os.path.realpath(path)
        if os.path.isdir(abs_path):
            files = [(os.path.join(abs_path, f)) for f in os.listdir(abs_path)]
            for file in files:
                hr = convert_file(file, docs, format)
                _handle_result(hr)
        else:
            hr = convert_file(abs_path, docs, format)
            _handle_result(hr)

    app.Quit()


def convert_file(file, docs, format):
    hr, doc = docs.Open(file, ReadOnly=True)
    if hr != S_OK:
        return hr

    out_dir = os.path.dirname(os.path.realpath(file)) + "/out"
    os.makedirs(out_dir, exist_ok=True)

    # you have to handle if the new_file already exists
    new_file = out_dir + "/" + os.path.splitext(os.path.basename(file))[0] + "." + format
    ret = doc.SaveAs2(new_file, FileFormat=formats[format])

    # always close the doc
    doc.Close(wpsapi.wdDoNotSaveChanges)

    return ret


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--format", "-f",
                        required=True,
                        metavar="<DOC_TYPE>",
                        choices=["doc", "docx", "rtf", "html", "pdf", "xml"],
                        help="convert to <DOC_TYPE>,")

    parser.add_argument("--abort", "-a",
                        action="store_true",
                        help="abort if one convert fails")

    parser.add_argument("path",
                        metavar="<path>",
                        nargs='+',
                        help="the <path> can be one or more file or folder")

    args = parser.parse_args()

    qApp = QtApp(sys.argv)

    try:
        convert_to(args.path, args.format, args.abort)
    except ConvertException as e:
        print(e)


if __name__ == "__main__":
    main()

  上面是官方代码,下面是我的flask调用函数:

from .convertto import convert_to

@medical.route('/insertMoadlDocx', methods=['POST', 'GET'])
@login_required
def insertModalDocx():
    try:
        image_id = request.form.get('image_id')
        # 查询相关信息
        medical_picture_info = MedicalPicture.query.filter_by(id=image_id).first()
        user_info = User.query.filter_by(id=medical_picture_info.user_id).first()
        user_message_info = UserMessage.query.filter_by(user_id=user_info.id).first()
        modal_list_info = ModalList.query.filter_by(image_id=image_id).all()

        # 读取docx模板
        template_path = os.path.join(current_app.root_path, 'static', 'word', 'template.docx')
        doc = Document(template_path)

        # 替换表格占位符
        placeholders = {
            '{{username}}': user_info.username,
            '{{name}}': user_message_info.name,
            '{{sex}}': '男' if user_message_info.sex == 1 else '女',
            '{{age}}': str(user_message_info.age),
            '{{imageType}}': medical_picture_info.imageType,
            '{{uploadTime}}': str(medical_picture_info.uploadTime),
            '{{phone}}': user_message_info.phone,
            '{{idCard}}': str(user_message_info.idCard),
            '{{asset}}': user_message_info.asset
        }

        for table in doc.tables:
            for row in table.rows:
                for cell in row.cells:
                    for key, value in placeholders.items():
                        if key in cell.text:
                            # 保留原始字体格式
                            for paragraph in cell.paragraphs:
                                for run in paragraph.runs:
                                    if key in run.text:
                                        run.text = run.text.replace(key, value)

        # 循环插入ModalList信息
        for index, item in enumerate(modal_list_info):
            if index == 0:
                # 如果是第一条记录,直接替换原有的占位符
                for paragraph in doc.paragraphs:
                    if '{{description}}' in paragraph.text:
                        paragraph.text = paragraph.text.replace('{{description}}','\t' + item.description)
                    if '{{image}}' in paragraph.text:
                        # 删除原有的占位符
                        paragraph.text = paragraph.text.replace('{{image}}', '')
                        # 添加图片
                        run = paragraph.add_run()
                        image_path = os.path.join(current_app.root_path, item.image.lstrip('/'))
                        run.add_picture(image_path, width=docx.shared.Cm(14.5), height=docx.shared.Cm(5.2))
            else:
                # 如果不是第一条记录,在报告医师信息的上一行插入新段落并插入数据
                paragraphs_copy, paragraphs_iter = tee(doc.paragraphs)
                for i, paragraph in enumerate(paragraphs_iter):
                    if '报告医师:' in paragraph.text:
                        # 在报告医师信息的上一行插入空白行
                        doc.paragraphs[i - 1].insert_paragraph_before()

                        # 在空白行之后插入新段落
                        new_paragraph = doc.paragraphs[i].insert_paragraph_before()

                        # 插入诊断描述和图片信息
                        new_run1 = new_paragraph.add_run(f"诊断描述:\n")
                        new_run1.font.name = '宋体'  # 设置字体为宋体
                        new_run1.font.size = Pt(12)  # 设置字号为12磅
                        new_paragraph.add_run('\t')  # 添加制表符实现缩进
                        new_paragraph.add_run(item.description)
                        new_run2 =  new_paragraph.add_run(f"\n诊断图片:\n")  # 设置字体为宋体
                        new_run2.font.name = '宋体'  # 设置字体为宋体
                        new_run2.font.size = Pt(12)  # 设置字号为12磅
                        image_path = os.path.join(current_app.root_path, item.image.lstrip('/'))
                        new_paragraph.add_run().add_picture(image_path, width=docx.shared.Cm(14.5),
                                                            height=docx.shared.Cm(5.2))
                        break

                # 添加一个空行,用于分隔不同的记录
                doc.add_paragraph()

        docx_filename = f"{image_id}_{user_message_info.name}_{medical_picture_info.imageType}.docx"
        folder_name = os.path.splitext(docx_filename)[0]  # 去掉文件尾缀
        docx_folder = os.path.join(current_app.root_path, 'static', 'word', folder_name)  # 使用去掉尾缀后的文件名作为文件夹名

        # 确保文件夹存在,如果不存在则创建
        if not os.path.exists(docx_folder):
            os.makedirs(docx_folder)

        # 保存 DOCX 文件
        docx_path = os.path.join(docx_folder, docx_filename)
        doc.save(docx_path)

        # from win32com.client import pythoncom  # 导入 pythoncom
        # pythoncom.CoInitialize()  # 初始化 COM 线程
        # # 构建 PDF 文件路径
        # pdf_filename = docx_filename.replace('.docx', '.pdf')
        # pdf_folder = docx_folder  # 与 DOCX 文件相同的目录
        # pdf_path = os.path.join(pdf_folder, pdf_filename)
        #
        # # 将 DOCX 文件转换为 PDF
        # convert(docx_path, pdf_path)

        # 创建 PDF 文件
        pdf_filename = docx_filename.replace('.docx', '.pdf')
        convert_to([docx_path], "pdf")

        # 构建目标文件的路径
        docx_save_path = os.path.join('/static', 'word', folder_name, docx_filename)
        pdf_save_path = os.path.join('/static', 'word', folder_name, 'out', pdf_filename)

        # 替换所有路径中的反斜杠为正斜杠
        docx_save_path = docx_save_path.replace('\\', '/')
        pdf_save_path = pdf_save_path.replace('\\', '/')

        # 将路径保存到数据库中
        medical_picture_info.pdf_path = pdf_save_path
        medical_picture_info.docx_path = docx_save_path

        db.session.commit()

        # 返回 JSON 响应
        return jsonify({'message': '报告生成成功!'}), 200
    except Exception as e:
        # 返回 JSON 响应,表示修改失败
        return jsonify({'error': str(e)}), 500

标签:docx,python,Linux,hr,paragraph,pdf,path,os
From: https://www.cnblogs.com/lht020321/p/18111285

相关文章

  • Python中is和==的区别有多大,你知道吗?
    Python中有很多运算符,今天我们就来讲讲is和==两种运算符在应用上的本质区别是什么。在讲is和==这两种运算符区别之前,首先要知道Python中对象包含的三个基本要素,分别是:id(身份标识)、type(数据类型)和value(值)。is和==都是对对象进行比较判断作用的,但对对象比较判断的内容并不......
  • 8、linux安装orace11g报[FATAL][INS32035]无法在指定位置创建新产品清单
    目录linux安装orace11g报·[FATAL]·[INS32035]无法在指定位置创建新产品清单1、执行静默安装命令后报错:提示如下2、解决方案:3、重新执行静默安装命令linux安装orace11g报·[FATAL]·[INS32035]无法在指定位置创建新产品清单1、执行静默安装命令后报错:提示如下正在启动Oracle......
  • 10分钟带你了解 Linux 系统中的 Top 命令
    前言作为一个测试工程师,可能我们并不需要像运维人员那样时时刻刻去关注系统的运行情况,但是对于一些查看系统运行情况,以及性能情况的命令,我们还是需要了解并掌握的,本文就来给大家介绍一下Linux系统重非常重要的top命令。什么是top命令在Linux系统中,top命令是一个非常实用的......
  • java,postgresql,python中各种数据类型的占位长度,取值范围
    Java数据类型Java中的数据类型分为两类:基本数据类型和引用数据类型。基本数据类型数据类型占位长度取值范围byte1字节-128127short2字节-3276832767int4字节-21474836482147483647long8字节-92233720368547758089223372036854775807float4字节1.4E-453.4028235E38double8字节4.......
  • python改变字幕时间线
    #处理小时,和分钟字母串defmyHandle(she):kaishi_shi=sheiflen(str(kaishi_shi))<2:kaishi_shi_str="0%s"%str(kaishi_shi)else:kaishi_shi_str=str(kaishi_shi)print(kaishi_shi_str)returnkaishi_shi_str#处理......
  • 【Linux】服务器时间ntp同步(chrony)
    一、简介Chrony是一个开源自由的网络时间协议NTP的客户端和服务器软软件。它能让计算机保持系统时钟与时钟服务器(NTP)同步,因此让你的计算机保持精确的时间,Chrony也可以作为服务端软件为其他计算机提供时间同步服务。Chrony由两个程序组成,分别是chronyd和chronycchronyd是一个......
  • Lock Linux是一个基于Linux的操作系统,它主要关注于系统安全和隐私保护
    LockLinux是一个基于Linux的操作系统,它主要关注于系统安全和隐私保护。LockLinux采用了SELinux(Security-EnhancedLinux)和AppArmor等安全机制,以确保系统的安全性。此外,它还提供了一些额外的功能,如加密的文件系统、安全的网络连接和强制访问控制等。以下是关于LockLinux......
  • python改变文件修改时间
    代码:importosimporttime,datetimedefchange_file_time(file_path,new_timestamp):#使用stat函数获取文件的状态file_stat=os.stat(file_path)#更新文件的访问时间和修改时间os.utime(file_path,(file_stat.st_atime,new_timestamp))......
  • Python加载C语言动态库
    ★背景说明1.python是一门胶水语言,可以通过加载动态库的方式在一个项目中运行不同语言的程序2.通过动态库加载其他语言的方式可以解决多线程GIL使用C解释器无法并发运行的问题★在Linux中运行C代码:编辑C语言代码//hello.c//c代码作为启动文件必须加include<stdio......
  • python selenium 速查笔记
    1.安装与配置pipinstallselenium基本使用selenium都是为了动态加载网页内容用于爬虫,所以一般也会用到phantomjsmac下如果要配置phantomjs环境的话echo$PATHln-s<phantomjs地址><PATH中任一路径>至于chromeDriver,配置方法类似,下载地址:https://sites.google.com/a/chro......