跟踪文档中的更改

跟踪更改功能(也称为审阅)允许您跟踪您或其他用户对内容和格式所做的更改。 Aspose.Words 的跟踪更改功能支持 Microsoft Word 中的跟踪更改。通过此功能,您可以访问文档中的各个修订版本并对它们应用不同的属性。

当您启用跟踪更改功能时,文档中所有插入、删除和修改的元素都将在视觉上突出显示,并包含有关更改者、更改时间和更改内容的信息。携带有关更改内容的信息的对象称为"跟踪更改"。例如,假设您想要审阅一份文档并进行重要更改 - 这可能意味着您需要进行修订。此外,您可能需要插入评论来讨论某些更改。这就是跟踪文档更改的用武之地。

本文介绍如何管理和跟踪多个审阅者对同一文档创建的更改,以及用于跟踪更改的属性。

什么是修订

在深入讨论修订之前,我们先解释一下修订的含义。 Revision 是在文档的一个节点中发生的更改,而由 RevisionGroup 类表示的修订组是在文档的许多节点中发生的一组连续修订。基本上,修订是跟踪更改的工具。

修订用于跟踪更改功能和比较文档功能,其中修订作为比较的结果出现。因此,跟踪更改功能中的修订会显示更改者和内容。

Aspose.Words 支持不同的修订类型以及 Microsoft Word,例如插入、删除、格式更改、样式定义更改和移动。所有修订类型均用 RevisionType 枚举表示。

开始和停止跟踪更改

在您开始跟踪文档之前,编辑文档通常不算作修订。 Aspose.Words 允许您通过简单的步骤自动跟踪文档中的所有更改。您可以使用 start_track_revisions 方法轻松开始跟踪更改的过程。如果您需要停止跟踪更改的过程,以便将来的任何编辑都不会被视为修订,则需要使用 stop_track_revisions 方法。

在文档中的跟踪更改过程结束时,您甚至可以接受所有修订或拒绝它们以将文档恢复为其原始形式。这可以通过使用 accept_all_revisionsreject_all 方法来实现。此外,您可以使用 acceptreject 方法分别接受或拒绝每个修订。

从您开始流程的那一刻到您停止流程的那一刻,所有更改都将被跟踪一次迭代。不同迭代之间的联系表示为以下场景:您完成跟踪过程,然后进行一些更改,然后再次开始跟踪更改。在这种情况下,您未接受或拒绝的所有更改都将再次显示。

以下代码示例展示了如何跟踪更改:

# For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-Python-via-.NET
doc = aw.Document()
body = doc.first_section.body
para = body.first_paragraph
# Add text to the first paragraph, then add two more paragraphs.
para.append_child(aw.Run(doc, "Paragraph 1. "))
body.append_paragraph("Paragraph 2. ")
body.append_paragraph("Paragraph 3. ")
# We have three paragraphs, none of which registered as any type of revision
# If we add/remove any content in the document while tracking revisions,
# they will be displayed as such in the document and can be accepted/rejected.
doc.start_track_revisions("John Doe", datetime.today())
# This paragraph is a revision and will have the according "IsInsertRevision" flag set.
para = body.append_paragraph("Paragraph 4. ")
self.assertTrue(para.is_insert_revision)
# Get the document's paragraph collection and remove a paragraph.
paragraphs = body.paragraphs
self.assertEqual(4, paragraphs.count)
para = paragraphs[2]
para.remove()
# Since we are tracking revisions, the paragraph still exists in the document, will have the "IsDeleteRevision" set
# and will be displayed as a revision in Microsoft Word, until we accept or reject all revisions.
self.assertEqual(4, paragraphs.count)
self.assertTrue(para.is_delete_revision)
# The delete revision paragraph is removed once we accept changes.
doc.accept_all_revisions()
self.assertEqual(3, paragraphs.count)
# Stopping the tracking of revisions makes this text appear as normal text.
# Revisions are not counted when the document is changed.
doc.stop_track_revisions()
# Save the document.
doc.save(docs_base.artifacts_dir + "WorkingWithRevisions.accept_revisions.docx")

以下代码示例显示了在跟踪文档中移动节点时如何生成修订:

# For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-Python-via-.NET
doc = aw.Document()
builder = aw.DocumentBuilder(doc)
builder.writeln("Paragraph 1")
builder.writeln("Paragraph 2")
builder.writeln("Paragraph 3")
builder.writeln("Paragraph 4")
builder.writeln("Paragraph 5")
builder.writeln("Paragraph 6")
body = doc.first_section.body
print(f"Paragraph count: {body.paragraphs.count}")
# Start tracking revisions.
doc.start_track_revisions("Author", datetime(2020, 12, 23, 14, 0, 0))
# Generate revisions when moving a node from one location to another.
node = body.paragraphs[3]
endNode = body.paragraphs[5].next_sibling
referenceNode = body.paragraphs[0]
while (node != endNode) :
nextNode = node.next_sibling
body.insert_before(node, referenceNode)
node = nextNode
# Stop the process of tracking revisions.
doc.stop_track_revisions()
# There are 3 additional paragraphs in the move-from range.
print("Paragraph count: 0", body.paragraphs.count)
doc.save(docs_base.artifacts_dir + "WorkingWithRevisions.move_node_in_tracked_document.docx")

管理更改并将其存储为修订

通过以前的跟踪更改功能,您可以了解文档中进行了哪些更改以及谁进行了这些更改。使用 track_revisions 功能时,您可以强制将文档中的任何更改存储为修订版本。

Aspose.Words 允许您使用 has_revision 属性检查文档是否有修订。如果不需要通过 start_track_revisions 和 stop_track_revisions 方法自动跟踪文档中的更改,则可以使用 track_revisions 属性来检查在以 Microsoft Word 格式编辑文档时是否跟踪更改并将其存储为修订版本。

track_revisions 功能进行修订而不是真正的 DOM 更改。但修订本身是分开的。例如,如果删除任何段落,Aspose.Words 会将其作为修订,将其标记为删除,而不是删除它。

此外,Aspose.Words 允许您使用 is_delete_revisionis_format_revisionis_insert_revisionis_move_from_revisionis_move_to_revision 属性检查对象是否被插入、删除或更改格式。

以下代码示例展示了如何应用不同的属性和修订:

# For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-Python-via-.NET
doc = aw.Document()
# Insert an inline shape without tracking revisions.
self.assertFalse(doc.track_revisions)
shape = aw.drawing.Shape(doc, aw.drawing.ShapeType.CUBE)
shape.wrap_type = aw.drawing.WrapType.INLINE
shape.width = 100
shape.height = 100
doc.first_section.body.first_paragraph.append_child(shape)
# Start tracking revisions and then insert another shape.
doc.start_track_revisions("John Doe")
shape = aw.drawing.Shape(doc, aw.drawing.ShapeType.SUN)
shape.wrap_type = aw.drawing.WrapType.INLINE
shape.width = 100.0
shape.height = 100.0
doc.first_section.body.first_paragraph.append_child(shape)
# Get the document's shape collection which includes just the two shapes we added.
shapes = doc.get_child_nodes(aw.NodeType.SHAPE, True)
self.assertEqual(2, shapes.count)
# Remove the first shape.
shape0 = shapes[0].as_shape()
shape0.remove()
# Because we removed that shape while changes were being tracked, the shape counts as a delete revision.
self.assertEqual(aw.drawing.ShapeType.CUBE, shape0.shape_type)
self.assertTrue(shape0.is_delete_revision)
# And we inserted another shape while tracking changes, so that shape will count as an insert revision.
shape1 = shapes[1].as_shape()
self.assertEqual(aw.drawing.ShapeType.SUN, shape1.shape_type)
self.assertTrue(shape1.is_insert_revision)
# The document has one shape that was moved, but shape move revisions will have two instances of that shape.
# One will be the shape at its arrival destination and the other will be the shape at its original location.
doc = aw.Document(docs_base.my_dir + "Revision shape.docx")
shapes = doc.get_child_nodes(aw.NodeType.SHAPE, True)
self.assertEqual(2, shapes.count)
# This is the move to revision, also the shape at its arrival destination.
shape0 = shapes[0].as_shape()
self.assertFalse(shape0.is_move_from_revision)
self.assertTrue(shape0.is_move_to_revision)
# This is the move from revision, which is the shape at its original location.
shape1 = shapes[1].as_shape()
self.assertTrue(shape1.is_move_from_revision)
self.assertFalse(shape1.is_move_to_revision)