python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > Python pypandoc格式转换

Python使用pypandoc将markdown文件和LaTex公式转为word

作者:风暴之零

pypandoc 是一个用于 pandoc 的轻量级 Python 包装器,支持多种格式的文档转换,下面我们来看看如何使用pypandoc将markdown文件和LaTex公式转为word吧

通义千问等大模型生成的回答多数是markdown类型的,需要将他们转为Word文件

一 pypandoc 介绍

1. 项目介绍

pypandoc 是一个用于 pandoc 的轻量级 Python 包装器。pandoc 是一个通用的文档转换工具,支持多种格式的文档转换,如 Markdown、HTML、LaTeX、DocBook 等。pypandoc 通过提供一个简单的 Python 接口,使得在 Python 脚本中调用 pandoc 变得更加方便。

2. 安装

使用pip安装

pip install pypandoc_binary

自动下载 Pandoc并安装

注意:pypandoc 提供了两个包:

pypandoc:需要用户自行安装 pandoc软件才能使用。

pypandoc_binary:包含了预编译的 pandoc 二进制文件,方便用户快速上手。

手动安装

可以手动安装pandoc再安装pypandoc库

pip install pypandoc

也可以先安装pypandoc然后再在pyhon中运行 pypandoc.download_pandoc()函数自动下载并安装 Pandoc,将其存放在 pypandoc 可以访问的目录中。

二、使用Python 将markdown转Word

本脚本实现了三类功能

1、将markdown文件转为word文件

2、将 markdown中段落开头的“-“转为回车,避免渲染成黑点或者空心圆等Word中不常见的符号

3、自定义了模板,格式化输出。

import pypandoc
import time
import re

# 定义路径
path1 = r"md.md"
path2 = r".docx"
template_path = r"D:\aTools\ytemplates\templates_s.docx"

# 读取原始Markdown文件内容
with open(path1, 'r', encoding='utf-8') as file:
    content = file.read()

# 使用正则表达式将以'- '开头的部分替换为换行符
processed_content = re.sub(r'- ', '\n', content)

# 记录开始时间
t1 = time.time()

# 将处理后的内容转换为Word文档
pypandoc.convert_text(
    processed_content,
    'docx',
    format='md',
    outputfile=path2,
    extra_args=['--reference-doc', template_path]
)

# 打印耗时
print(time.time() - t1)
print("转换完成!")

三、直接指定Word格式

直接读取文件(可以为txt或者md)转为指定格式的word。

这里格式是:

1、将 markdown中段落开头的“-“转为回车,避免渲染成黑点或者空心圆等Word中不常见的符号

2、将原来加粗部分继续加粗和左对齐

3、字体为黑色GB2312

注意:代码用正则替换####这些时需要先从4级标题开始替换否则会有逻辑错误,导致奇数个#无法替换。

设置中文字体不能用run.font.name = '仿宋_GB2312’而是用style._element.rPr.rFonts.set(qn(‘w:eastAsia’), ‘仿宋_GB2312’) 设置中文字体。

import re
from docx import Document
from docx.shared import Pt, RGBColor
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.oxml.ns import qn

def set_font_color(run):
    run.font.name = 'Times New Roman'
    run._element.rPr.rFonts.set(qn('w:eastAsia'), '仿宋_GB2312')
    run.font.size = Pt(12)
    run.font.color.rgb = RGBColor(0, 0, 0)
    run.italic = False

def process_content(line, paragraph):
    """通用内容处理函数"""
    bold_pattern = re.compile(r'\*\*(.*?)\*\*')
    matches = list(bold_pattern.finditer(line))
    
    if not matches:
        run = paragraph.add_run(line)
        set_font_color(run)
    else:
        start = 0
        for match in matches:
            if match.start() > start:
                run = paragraph.add_run(line[start:match.start()])
                set_font_color(run)
            run = paragraph.add_run(match.group(1))
            run.bold = True
            set_font_color(run)
            start = match.end()
        if start < len(line):
            run = paragraph.add_run(line[start:])
            set_font_color(run)

def mdtxt2word(txt_path, docx_path):
    with open(txt_path, 'r', encoding='utf-8') as file:
        content = re.sub(r'- ', '\n', file.read())

    doc = Document()
    style = doc.styles['Normal']
    style.font.name = 'Times New Roman'
    style._element.rPr.rFonts.set(qn('w:eastAsia'), '仿宋_GB2312')
    style.font.size = Pt(12)
    style.font.color.rgb = RGBColor(0, 0, 0)

    # 合并标题正则表达式
    heading_pattern = re.compile(
        r'^\s*(#{1,4})\s*(.*?)\s*$'  # 匹配1-4个#开头的标题
    )

    for line in content.split('\n'):
        # 处理所有标题类型
        heading_match = heading_pattern.match(line)
        if heading_match:
            level = len(heading_match.group(1))  # 根据#数量确定级别
            title_text = heading_match.group(2).strip()
            
            if not title_text:
                continue  # 跳过空标题

            # 创建对应级别的标题
            heading = doc.add_heading(level=min(level, 4))  # 限制最大4级
            heading.alignment = WD_ALIGN_PARAGRAPH.LEFT
            
            # 处理标题内容中的加粗标记
            process_content(title_text, heading)
            continue

        # 处理普通段落
        paragraph = doc.add_paragraph()
        paragraph.alignment = WD_ALIGN_PARAGRAPH.LEFT
        process_content(line, paragraph)

    doc.save(docx_path)
    print("转换完成!")

if __name__ == "__main__":
    txt_path = r"C:\Users\xueshifeng\Desktop\11.txt"
    docx_path = r"C:\Users\xueshifeng\Desktop\11.docx"
    mdtxt2word(txt_path, docx_path)

四、将LaTex公式转为Word

将 latex_content字符串$ $ 中间的位置替换为公式,或者直接复制代码到GPT,让GPT修改代码

import pypandoc

# 定义包含特定公式的LaTeX字符串
#$ $ 中间的位置替换为公式,或者直接复制代码到GPT,让GPT生成最终代码
latex_content = r"""
\documentclass{article}
\usepackage{amsmath} % 确保包含用于数学排版的包
\begin{document}

$ L(y_i, f(x_i)) = \max(0, 1 - y_if(x_i)) $


\end{document}
"""

# 将LaTeX内容转换为Word文档
output_file = r"xx14.docx"

output = pypandoc.convert_text(
    latex_content,  # 输入的字符串
    'docx',         # 输出格式
    format='latex', # 输入格式(LaTeX)
    outputfile=output_file,  # 输出文件路径
    extra_args=['--mathml']  # 额外参数,确保公式渲染为MathML格式
)

# 检查转换是否成功
if output != '':
    print(f"转换过程中出现错误: {output}")
else:
    print(f"Word 文档已生成: {output_file}")

四、将LaTex公式转为Word,追加写入Word

难点在于如何管理文件句柄,没有找到好方法,采用对已打开的文档,先关闭再打开的方法。

import os
import pypandoc
from docx import Document
import tempfile
import time
import pythoncom
from win32com.client import Dispatch  # 需要安装pywin32库

def is_file_locked(filepath):
    try:
        with open(filepath, 'a'):
            return False
    except PermissionError:
        return True
    except FileNotFoundError:
        return False

def close_word_document(filepath):
    try:
        word = Dispatch("Word.Application")
        for doc in word.Documents:
            if doc.FullName.lower() == os.path.abspath(filepath).lower():
                doc.Save()
                doc.Close()
                print("已保存并关闭Word文档")
                return True
        word.Quit()
    except Exception as e:
        print(f"关闭Word文档失败:{str(e)}")
    return False

def generate_latex_content(formula):
    """生成完整的LaTeX文档内容"""
    return fr"""
    \documentclass{{article}}
    \usepackage{{amsmath}}
    \begin{{document}}

    开始:

    ${formula}$

    结束。
    \end{{document}}
    
    """
    
def doc_creat(user_formula, output_file):


    # 检查文件是否存在
    if not os.path.exists(output_file):
        # 创建新文档对象
        doc = Document()
        # 保存文档
        doc.save(output_file)
        print(f"文件已创建:{output_file}")
        document = Document(output_file)
    else:
        print("文件已打开")
        
    
    
    retry_count = 3
    for _ in range(retry_count):
        if is_file_locked(output_file):
            print("检测到文件被占用,尝试关闭Word文档...")
            if close_word_document(output_file):
                time.sleep(0.5)  # 等待系统释放文件
                continue
            else:
                print("错误:文件被其他程序占用,请手动关闭后重试!")
                break

        try:
            with tempfile.NamedTemporaryFile(delete=False, suffix=".tex") as temp_tex_file:
                latex_content = generate_latex_content(user_formula)
                temp_tex_file.write(latex_content.encode('utf-8'))
                temp_tex_file_name = temp_tex_file.name

            with tempfile.NamedTemporaryFile(delete=False, suffix=".docx") as temp_docx_file:
                temp_docx_file_name = temp_docx_file.name

            # 转换LaTeX到Word
            pypandoc.convert_file(
                temp_tex_file_name, 'docx', 
                outputfile=temp_docx_file_name, extra_args=['--mathjax']
            )

            # 创建或打开目标文档
            target_doc = Document(output_file) if os.path.exists(output_file) else Document()
            temp_doc = Document(temp_docx_file_name)
            
            # 复制所有元素
            for element in temp_doc.element.body:
                target_doc.element.body.append(element)
            
            # 保存目标文档
            target_doc.save(output_file)
            print(f"内容已成功追加至:{output_file}")
            
            # 自动用Word打开文档
            os.startfile(output_file)
            break

        except PermissionError:
            print("文件权限错误,请检查文件是否被其他程序占用")
            break
        except Exception as e:
            print(f"操作失败:{str(e)}")
            break
        finally:
            if 'temp_tex_file_name' in locals() and os.path.exists(temp_tex_file_name):
                os.unlink(temp_tex_file_name)
            if 'temp_docx_file_name' in locals() and os.path.exists(temp_docx_file_name):
                os.unlink(temp_docx_file_name)
    else:
        print("重试次数已达上限,请检查文件状态")

if __name__ == '__main__':
    # 用户输入公式(示例)
    user_formula = r"\frac{\sqrt{x^2 + y^2}}{z}"   
    # 输出文件路径
    output_file = r"C:\Users\xueshifeng\Desktop\18.docx"
    
    doc_creat(user_formula, output_file)

以上就是Python使用pypandoc将markdown文件和LaTex公式转为word的详细内容,更多关于Python pypandoc格式转换的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文