跟踪文档中的更改

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

启用"跟踪更改"功能后,文档的所有插入、删除和修改元素都将以可视化方式突出显示,其中包含有关更改对象、更改时间和更改内容的信息。 携带有关更改内容的信息的对象称为"跟踪更改"。 例如,假设您要查看文档并进行重要更改–这可能意味着您需要进行修订。 此外,您可能需要插入注释来讨论一些更改。 这就是跟踪文档更改的作用所在。

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

什么是修订

在深入研究修订之前,让我们解释修订的含义。 revision是在文档的一个节点中发生的更改,而由RevisionGroup类表示的修订组是在文档的许多节点中发生的一组顺序修订。 基本上,修订是跟踪更改的工具。

修订在跟踪更改功能和比较文档功能中使用,其中修订显示为比较的结果。 因此,跟踪更改功能中的修订会显示更改的对象和内容。

Aspose.Words支持不同的修订类型,以及在Microsoft Word中,如插入,删除,FormatChange,StyleDefinitionChange和移动。 所有修订类型都用RevisionType枚举表示。

启动和停止跟踪更改

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

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

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

下面的代码示例演示如何处理跟踪更改:

For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-C
auto doc = System::MakeObject<Document>();
System::SharedPtr<Body> body = doc->get_FirstSection()->get_Body();
System::SharedPtr<Paragraph> para = body->get_FirstParagraph();
// Add text to the first paragraph, then add two more paragraphs.
para->AppendChild(System::MakeObject<Run>(doc, u"Paragraph 1. "));
body->AppendParagraph(u"Paragraph 2. ");
body->AppendParagraph(u"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->StartTrackRevisions(u"John Doe", System::DateTime::get_Now());
// This paragraph is a revision and will have the according "IsInsertRevision" flag set.
para = body->AppendParagraph(u"Paragraph 4. ");
ASSERT_TRUE(para->get_IsInsertRevision());
// Get the document's paragraph collection and remove a paragraph.
System::SharedPtr<ParagraphCollection> paragraphs = body->get_Paragraphs();
ASSERT_EQ(4, paragraphs->get_Count());
para = paragraphs->idx_get(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.
ASSERT_EQ(4, paragraphs->get_Count());
ASSERT_TRUE(para->get_IsDeleteRevision());
// The delete revision paragraph is removed once we accept changes.
doc->AcceptAllRevisions();
ASSERT_EQ(3, paragraphs->get_Count());
ASSERT_EQ(0, para->get_Count());
// Stopping the tracking of revisions makes this text appear as normal text.
// Revisions are not counted when the document is changed.
doc->StopTrackRevisions();
System::String outputPath = outputDataDir + u"WorkingWithRevisions.AcceptRevisions.doc";
doc->Save(outputPath);

下面的代码示例演示在跟踪文档中移动节点时如何生成修订:

For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-C
auto doc = System::MakeObject<Document>();
auto builder = System::MakeObject<DocumentBuilder>(doc);
builder->Writeln(u"Paragraph 1");
builder->Writeln(u"Paragraph 2");
builder->Writeln(u"Paragraph 3");
builder->Writeln(u"Paragraph 4");
builder->Writeln(u"Paragraph 5");
builder->Writeln(u"Paragraph 6");
System::SharedPtr<Body> body = doc->get_FirstSection()->get_Body();
std::cout << "Paragraph count: " << body->get_Paragraphs()->get_Count() << std::endl;
// Start tracking revisions.
doc->StartTrackRevisions(u"Author", System::DateTime(2020, 12, 23, 14, 0, 0));
// Generate revisions when moving a node from one location to another.
System::SharedPtr<Node> node = body->get_Paragraphs()->idx_get(3);
System::SharedPtr<Node> endNode = body->get_Paragraphs()->idx_get(5)->get_NextSibling();
System::SharedPtr<Node> referenceNode = body->get_Paragraphs()->idx_get(0);
while (node != endNode)
{
System::SharedPtr<Node> nextNode = node->get_NextSibling();
body->InsertBefore(node, referenceNode);
node = nextNode;
}
// Stop the process of tracking revisions.
doc->StopTrackRevisions();
// There are 3 additional paragraphs in the move-from range.
std::cout << "Paragraph count: " << body->get_Paragraphs()->get_Count() << std::endl;
System::String outputPath = outputDataDir + u"WorkingWithRevisions.MoveNodeInTrackedDocument.pdf";
doc->Save(outputPath);

将更改管理并存储为修订

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

Aspose.Words允许您使用HasRevision属性检查文档是否有修订。 如果您不需要通过StartTrackRevisions和StopTrackRevisions方法自动跟踪文档中的更改,那么您可以使用TrackRevisions属性检查在Microsoft Word中编辑文档并存储为修订版本时是否跟踪了更改。

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

此外,Aspose.Words允许您使用IsDeleteRevision, IsFormatRevision, IsInsertRevision, IsMoveFromRevision, 和IsMoveToRevision属性。

下面的代码示例演示如何使用修订应用不同的属性:

For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-C
auto doc = System::MakeObject<Document>();
// Insert an inline shape without tracking revisions.
ASSERT_FALSE(doc->get_TrackRevisions());
auto shape = System::MakeObject<Shape>(doc, ShapeType::Cube);
shape->set_WrapType(WrapType::Inline);
shape->set_Width(100.0);
shape->set_Height(100.0);
doc->get_FirstSection()->get_Body()->get_FirstParagraph()->AppendChild(shape);
// Start tracking revisions and then insert another shape.
doc->StartTrackRevisions(u"John Doe");
shape = System::MakeObject<Shape>(doc, ShapeType::Sun);
shape->set_WrapType(WrapType::Inline);
shape->set_Width(100.0);
shape->set_Height(100.0);
doc->get_FirstSection()->get_Body()->get_FirstParagraph()->AppendChild(shape);
// Get the document's shape collection which includes just the two shapes we added.
auto shapes = doc->GetChildNodes(NodeType::Shape, true)->LINQ_Cast<System::SharedPtr<Shape>>()->LINQ_ToList();
ASSERT_EQ(2, shapes->get_Count());
// Remove the first shape.
shapes->idx_get(0)->Remove();
// Because we removed that shape while changes were being tracked, the shape counts as a delete revision.
ASSERT_EQ(ShapeType::Cube, shapes->idx_get(0)->get_ShapeType());
ASSERT_TRUE(shapes->idx_get(0)->get_IsDeleteRevision());
// And we inserted another shape while tracking changes, so that shape will count as an insert revision.
ASSERT_EQ(ShapeType::Sun, shapes->idx_get(1)->get_ShapeType());
ASSERT_TRUE(shapes->idx_get(1)->get_IsInsertRevision());
// 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 = System::MakeObject<Document>(inputDataDir + u"Revision shape.docx");
shapes = doc->GetChildNodes(NodeType::Shape, true)->LINQ_Cast<System::SharedPtr<Shape>>()->LINQ_ToList();
ASSERT_EQ(4, shapes->get_Count());
// This is the move to revision, also the shape at its arrival destination.
ASSERT_FALSE(shapes->idx_get(0)->get_IsMoveFromRevision());
ASSERT_TRUE(shapes->idx_get(0)->get_IsMoveToRevision());
// This is the move from revision, which is the shape at its original location.
ASSERT_TRUE(shapes->idx_get(1)->get_IsMoveFromRevision());
ASSERT_FALSE(shapes->idx_get(1)->get_IsMoveToRevision());