ドキュメント内の変更を追跡する

変更の追跡機能 (レビューとも呼ばれます) を使用すると、自分または他のユーザーが行ったコンテンツおよび書式設定の変更を追跡できます。 Aspose.Words によるこのトラック変更機能は、Microsoft Word でのトラック変更をサポートします。この機能を使用すると、ドキュメント内の個々のリビジョンにアクセスし、それらにさまざまなプロパティを適用できます。

変更の追跡機能を有効にすると、文書の挿入、削除、および変更されたすべての要素が、誰が、いつ、何を変更したかに関する情報とともに視覚的に強調表示されます。何が変更されたかに関する情報を保持するオブジェクトは、「変更の追跡」と呼ばれます。たとえば、ドキュメントをレビューして重要な変更を加えたいとします。これは、改訂が必要になることを意味する場合があります。また、一部の変更について説明するためにコメントを挿入する必要がある場合もあります。そこで重要になるのが、ドキュメントの変更の追跡です。

この記事では、同じ文書に対して多数のレビュー担当者によって作成された変更を管理および追跡する方法と、変更を追跡するためのプロパティについて説明します。

リビジョンとは

リビジョンに入る前に、リビジョンの意味を説明しましょう。 Revision はドキュメントの 1 つのノードで発生する変更ですが、RevisionGroup クラスで表されるリビジョン グループはドキュメントの多くのノードで発生する連続したリビジョンのグループです。基本的に、リビジョンは変更を追跡するためのツールです。

リビジョンは、変更の追跡機能およびドキュメントの比較機能内で使用され、比較の結果としてリビジョンが表示されます。したがって、変更追跡機能内のリビジョンには、誰が何を変更したかが表示されます。

Aspose.Words は、Microsoft Word だけでなく、挿入、削除、FormatChange、StyleDefinitionChange、Moving などのさまざまなリビジョン タイプをサポートしています。すべてのリビジョン タイプは RevisionType 列挙で表されます。

変更の追跡を開始および停止する

通常、ドキュメントの編集は、追跡を開始するまでリビジョンとしてカウントされません。 Aspose.Words を使用すると、簡単な手順でドキュメント内のすべての変更を自動的に追跡できます。 start_track_revisions メソッドを使用すると、変更を追跡するプロセスを簡単に開始できます。今後の編集がリビジョンとみなされないように変更の追跡プロセスを停止する必要がある場合は、stop_track_revisions メソッドを使用する必要があります。

文書の変更追跡プロセスの最後には、すべての改訂を受け入れるか、拒否して文書を元の形式に戻すこともできます。これは、accept_all_revisions または reject_all 方式のいずれかを使用して実現できます。さらに、accept または reject 方式を使用して、各リビジョンを個別に承認または拒否できます。

すべての変更は、プロセスを開始した瞬間からプロセスを停止する瞬間まで、1 回の反復で追跡されます。異なる反復間の関係は、次のシナリオで表されます。追跡プロセスを完了し、いくつかの変更を加え、再び変更の追跡を開始します。このシナリオでは、承認または拒否しなかったすべての変更が再度表示されます。

次のコード例は、変更の追跡を操作する方法を示しています。

# 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_revision、および is_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)