如何输出包含所有已接受/拒绝的跟踪更改的 Word 文档/纯文本?

问题描述 投票:0回答:3

我有一组 .docx 文档,其中使用“跟踪更改”功能执行了文本修改。

对于我集中的每个

source.docx
文件,我想以编程方式运行两个操作:

  1. 生成两份文档,一份拒绝所有更改,另一份接受所有更改(棘手的步骤)
  2. 转换为明文。

换句话说,我想运行以下管道:

source.docx
->
sources-all-changes-rejected.docx
->
source-all-rejected-plaintext.txt

source.docx
->
sources-all-changes-accepted.docx
->
source-all-accepted-plaintext.txt

有没有办法做到这一点,例如使用

soffice --headless

我尝试了受 Python - 使用 win32com.client 接受 Word 文档中的所有更改启发的解决方案。这种方法很有效,注意使用绝对路径并保存为 txt 文档https://learn.microsoft.com/en-us/office/vba/api/word.wdsaveformat。所以我有一个函数,它采用

pathlib
路径
file_path
并根据需要写入纯文本文档:

def output_track_changed_version(file_path, action):
    
    doc.TrackRevisions = False

    # Delete all comments
    if doc.Comments.Count >= 1:
        doc.DeleteAllComments()

    # Accept/reject all revisions
    doc.Revisions.AcceptAll()
    changed_text = doc.Content.Text
    doc.Undo()
    doc.Revisions.RejectAll()
    original_text = doc.Content.Text

    # [CUT: code to dump changed/original strings to file and then...]

    doc.Close(False, False, False)
    word.Application.Quit()

但是我不想坚持

win32com.client
,更喜欢基于 LibreOffice 的解决方案 + Python,可以轻松在 Linux VM 上设置。

python ms-word docx libreoffice
3个回答
0
投票

我尝试了受 Python - 使用 win32com.client 接受 Word 文档中的所有更改启发的解决方案。这种方法很有效,注意使用绝对路径并保存为 txt 文档https://learn.microsoft.com/en-us/office/vba/api/word.wdsaveformat。所以我有一个函数,它采用

pathlib
路径
file_path
并根据需要写入纯文本文档:

def output_track_changed_version(file_path, action):
    
    word = win32.gencache.EnsureDispatch("Word.Application")
    word.Visible = False

    print(file_path.absolute())
    doc = word.Documents.Open(str(file_path.absolute()))
    
    doc.Activate()
    word.ActiveDocument.TrackRevisions = False

    # Delete all comments
    if word.ActiveDocument.Comments.Count >= 1:
        word.ActiveDocument.DeleteAllComments()

    # Accept all revisions
    if action == "accept":
        word.ActiveDocument.Revisions.AcceptAll()
        word.ActiveDocument.SaveAs2(str((Path("accept-all") / file_path.name).absolute())  + ".txt", FileFormat=7)
    elif action == "reject":
        word.ActiveDocument.Revisions.RejectAll()
        word.ActiveDocument.SaveAs2(str((Path("reject-all") / file_path.name).absolute()) + ".txt", FileFormat=7)

    doc.Close(False)
    word.Application.Quit()

但是我仍然更喜欢基于

soffice
的解决方案。


0
投票

至于生成接受所有更改的文档,您可能知道没有直接命令来执行此操作。您所要做的就是创建一个宏来执行此过程。对我来说,我希望它在 docker 容器中运行,作为一个无头环境,您需要在没有 GUI 的情况下启动 libreoffice。 为此,我创建了一个supervisord.conf 文件以使用虚拟屏幕启动 libreoffice,这是我的文件 (supervisord.conf):

[supervisord]
nodaemon=true
logfile=/var/log/supervisord.log
pidfile=/var/run/supervisord.pid

[program:Xvfb]
command=/usr/bin/Xvfb :1 -screen 0 1024x768x16
autostart=true
autorestart=true
stdout_logfile=/var/log/xvfb.log
stderr_logfile=/var/log/xvfb_err.log

[program:libreoffice]
command=/usr/bin/libreoffice --headless --accept="socket,host=0.0.0.0,port=2002;urp;StarOffice.ServiceManager"
autostart=true
autorestart=true
stdout_logfile=/var/log/libreoffice.log
stderr_logfile=/var/log/libreoffice_err.log

那么你可以使用以下命令运行它:

supervisord -c /etc/supervisor/conf.d/supervisord.conf

这将以无头模式启动 libreoffice 应用程序,并允许您的宏连接到该应用程序。

我用来打开文档并接受所有跟踪更改的宏是:

import sys
sys.path.append('/usr/lib/python3/dist-packages')
import uno
import os

def load_document(desktop, file_path):
    file_url = uno.systemPathToFileUrl(os.path.abspath(file_path))
    return desktop.loadComponentFromURL(file_url, "_blank", 0, ())

def accept_all_changes(file_path):
    # Get the UNO component context from the running LibreOffice instance
    local_context = uno.getComponentContext()

    # Create the UnoUrlResolver
    resolver = local_context.ServiceManager.createInstanceWithContext("com.sun.star.bridge.UnoUrlResolver", local_context)

    # Connect to the running LibreOffice instance
    ctx = resolver.resolve("uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext")

    # Get the ServiceManager from the context
    smgr = ctx.ServiceManager

    # Get the desktop (main entry point for most actions)
    desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx)

    # Load the specified document
    model = load_document(desktop, file_path)

    if model is None:
        raise Exception(f"Failed to load document: {file_path}")
    
    dispatcher = smgr.createInstanceWithContext("com.sun.star.frame.DispatchHelper", ctx)
    controller = model.getCurrentController()
    args = []
    dispatcher.executeDispatch(controller.Frame, 
    ".uno:ShowTrackedChanges", "", 0, tuple(args))
    dispatcher.executeDispatch(controller.Frame, 
    ".uno:AcceptAllTrackedChanges", "", 0, tuple(args))
    model.store()
    model.close(True)

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Usage: python accept_changes_2.py <file_path>")
        sys.exit(1)

    file_path = sys.argv[1]
    accept_all_changes(file_path)

然后你可以使用以下命令运行这个宏:

python path/to/macro.py tmp/filename.docx

文件保存在您传递到宏中的同一目录/文件名中。 在拒绝所有更改的情况下,请使用名为“.uno:RejectAllTrackedChanges”的调度命令

所有命令的列表可以在这里找到: https://wiki.documentfoundation.org/Development/DispatchCommands#Script_example


-2
投票

我不知道它是否解决了您的问题,但是您可以使用 docx 库(使用

pip install python-docx
命令安装)来解决此任务。

示例:

import docx

#rejected changes
doc = docx.Document('source.docx')
doc.save('sources-all-changes-rejected.docx')

#txt
r_text = []
for par in doc.paragraphs:
    r_text.append(par.text)
r_text = '\n'.join(r_text)

filename = 'source-all-rejected-plaintext.txt'
with open(filename, 'w') as r_txt:
    r_txt.write(r_text)

#accepted changes
for par in doc.paragraphs:
    #do changes
    pass

#txt
a_text = []
for par in doc.paragraphs:
    a_text.append(par.text)
a_text = '\n'.join(a_text)

filename = 'source-all-accepted-plaintext.txt'
with open(filename, 'w') as a_txt:
    a_txt.write(a_text)

#docx
doc.save('sources-all-changes-accepted.docx')

然后您可以循环遍历集合中的所有文件。

© www.soinside.com 2019 - 2024. All rights reserved.