Revision Track Changes in Aspose

Are we correct that we can’t create tracked revisions using Aspose Words for Android?
If, when using Aspose Words for Android, the isInsertRevision() or isDeleteRevision() method of a Paragraph or Run indicates that the object is a revision, How do we query its author, timestamp, etc?

Hi Brian,

Thanks for your inquiry.

You can create tracked changes (revisions) by using Aspose.Words for Android via Java. If you want to automatically track changes as they are made programmatically by ‘Aspose.Words for Android via Java’ to this document then use the Document.StartTrackRevisions and Document.StopTrackRevisions methods.

The Document.TrackRevisions property returns True if changes are tracked when this document is edited in Microsoft Word. Setting this option only instructs Microsoft Word whether the track changes is turned on or off. This property has no effect on changes to the document that you make programmatically via Aspose.Words.

Brian:
If, when using Aspose Words for Android, the isInsertRevision() or isDeleteRevision() method of a Paragraph or Run indicates that the object is a revision, How do we query its author, timestamp, etc?

Yes, IsDeleteRevision, IsFormatRevision and IsInsertRevision properties indicate that the object has respective revisions. Also, please see the following code:

for (Revision rev : doc.getRevisions())
{
    System.out.println(rev.getRevisionType() +
    " by " + rev.getAuthor() + " : " +
    rev.getParentNode().toString(SaveFormat.TEXT));
}

Hope, this helps.

Best regards,

Thank you for the quick reply. We still have a couple more questions:

  1. For getting at a revision’s author and timestamp: How do we get from a Paragraph or Run that’s a revision to its Revision object?
  2. For creating revisions: We aren’t editing an Aspose-loaded document directly. We’re creating one from scratch using the DocumentBuilder object. How do we create revisions while creating a document via a DocumentBuilder?

Hi Brian,

Thanks for your inquiry.

Brian:
1. For getting at a revision’s author and timestamp: How do we get from a Paragraph or Run that’s a revision to its Revision object?

You can get reference to a Node (PARAGRAPH or RUN) that is associated with a Revision by using the following code:

for (Revision rev : doc.getRevisions())
{
    //if(rev.getParentNode().getNodeType() == NodeType.PARAGRAPH)
    if (rev.getParentNode().getNodeType() == NodeType.RUN)
    {
        Run run = (Run)rev.getParentNode();

        System.out.println(rev.getRevisionType() +
        " by " + rev.getAuthor() + " : " +
        rev.getParentNode().toString(SaveFormat.TEXT));
    }
}

Brian:
2. For creating revisions: We aren’t editing an Aspose-loaded document directly. We’re creating one from scratch using the DocumentBuilder object. How do we create revisions while creating a document via a DocumentBuilder?

Please try using the following code:

Document doc = new Document();
DocumentBuilder builder = new DocumentBuilder(doc);

builder.writeln("hello"); // this is not tracked

doc.startTrackRevisions("user1");
builder.writeln("message from user1"); // this is marked as inserted by user1

doc.startTrackRevisions("user2");
builder.writeln("comment from user2"); // this is marked as inserted by user2

doc.startTrackRevisions("user3", new Date());
doc.removeAllChildren(); // both paragraphs inserted above are marked as deleted

doc.stopTrackRevisions();

builder.writeln("let’s start it all over"); // this is not marked because tracking has been stopped.

doc.save("D:\temp\aw-android-java-17.4.docx");

Hope, this helps.

Best regards,

thanks again for the help.

You have been very helpful. We are working on creating this so that blind people can use technology to read track changes. Could you answer another question:
In your example code, you create a delete revision by removing characters.
Would it work, if we know the characters that are to be within a delete revision, to first write them with track revisions off and then to remove them with track revisions on?

Hi Brian,

Thanks for your inquiry. Generally the operations, that you can perform on documents by using Microsoft Word, can also be performed by using ‘Aspose.Words for Android via Java’ programmatically. Could you please attach your input and expected Word documents here for our reference? We will investigate the structure of your expected document as to how you want your final output be generated like. You can create expected document by using Microsoft Word. We will then provide you code to achieve the same by using ‘Aspose.Words for Android via Java’. Thanks for your cooperation.

Best regards,

We’re using Aspose Words for Android to read and to write documents, but we’re using our own structures to manage the document in between. We capture the revision information (location, text, author, time stamp) within Android spans when reading, and need to be able to reproduce them when writing. We also, of course, are including new insert and delete revisions that editing the document generated.
My coder’s question is : While writing a document from scratch with a Document Builder, how do we add a delete revision? We know where to add it, what text it should contain, etc. I’m assuming that what we need to do is to write the text while stopTrackRevisions() is in effect, and then to remove exactly that same text while startTrackRevisions() is in effect. Is that correct?
If so, how do we delete exactly the characters that we just wrote (with DocumentBuilder.write - not writeln)?

Hi Brian,

Thanks for your inquiry. Please try using the following code:

// Write Document from scratch
Document doc = new Document();
DocumentBuilder builder = new DocumentBuilder(doc);

// Normal text
builder.writeln("Hello world");

// You can simply write this Special Content inside a Bookmark
BookmarkStart bmStart =  builder.startBookmark("bm");
builder.write("You know that this content belongs to a delete Revision and will be marked as Deletion Revision in future. ");
builder.write("The content inside Bookmark can also be an Image or whole Table or any other element");
builder.insertImage("D:/temp/aspose.words.jpg");
builder.endBookmark("bm");

// Again Normal text
builder.writeln(" End of document");

// Now mark the whole Bookmarked content as Deletion Revision
doc.startTrackRevisions("user1");
bmStart.getBookmark().setText("");
doc.stopTrackRevisions();

doc.save("D:\\temp\\aw-android-java-17.4.docx");

Hope, this helps.

Best regards,

AwaosAwais, thanks again for your great support. My coder has another question. he is blind…

Do you know the full sequence of default colours that Word uses for revision
authors (e.g. red for the first, blue for the second, etc)? If so, what are
they or where can I look it up? Also, does Aspose Words define colour constants
for them?

Hi Brian,

Thanks for your inquiry. You can use different properties/members of RevisionOptions class to meet this requirement. Please see attached output PDF and following code for example:

doc.getLayoutOptions().getRevisionOptions().setDeletedTextColor(Color.YELLOW.getRGB());

PS: ‘Aspose.Words for Android via Java’ API is very similar to ‘Aspose.Words for Java’ API. So, you can use the same API Reference Guide.

Best regards,

Hello again.We’re getting several paragraphs when we’re creating a new document via
DocumentBuilder.write() even though we aren’t doing anything specific to get
them. Is there something (perhaps writing a newline) that automatically causes
paragraph breaks?

Is my understanding correct that the only difference between a region comment
and a position commment is the presence of the CommentRangeStart and
CommentRangeEnd objects?

Can the Comment, CommentRangeStart, and CommentRangeEnd objects occur in any
order? Is there a preferred or required order?

What’s the preferred action to take if there’s a missing or extra
CommentRangeStart and/or CommentRangeEnd object, i.e. if there isn’t exactly
one or none of each?
thx for the great support we are receiving

Hi Brian,

Thanks for your inquiry. Yes, there are a few Control characters that can cause paragraph break. e.g.

Document doc = new Document();
DocumentBuilder builder = new DocumentBuilder(doc);
builder.write("Hello " + ControlChar.CR + "world");
doc.save("D:\\temp\\out-17.4.docx");

Also, please check ControlChar.CR_LF, ControlChar.PARAGRAPH_BREAK and ControlChar.PARAGRAPH_BREAK_CHAR etc (see ControlChar Class for more details).

Brian:
Is my understanding correct that the only difference between a region comment
and a position commment is the presence of the CommentRangeStart and
CommentRangeEnd objects?

Yes. Your understanding is correct.

Brian:
Can the Comment, CommentRangeStart, and CommentRangeEnd objects occur in any
order? Is there a preferred or required order?

CommentRangeStart should come before CommentRangeEnd. However, corresponding Comment object can be placed anywhere in the Document Object Model.

Brian:
What’s the preferred action to take if there’s a missing or extra
CommentRangeStart and/or CommentRangeEnd object, i.e. if there isn’t exactly
one or none of each?

Please see attached input/output documents. There are no CommentRangeStart and CommentRangeEnd objects in input document but only Comment object is present. From this Comment object’s ID, you can build Comment region. For example:

Document doc = new Document("D:\\temp\\input.doc");

// Get main Comment
Comment comment = (Comment) doc.getLastSection().getBody().getLastParagraph().getFirstChild();

// Create a new Comment Region and link it to main Comment
// You can optionally search if CommentRangeStart or CommentRangeEnd already are present in DOM
CommentRangeStart commentRangeStart = new CommentRangeStart(doc, comment.getId());
CommentRangeEnd commentRangeEnd = new CommentRangeEnd(doc, comment.getId());

Run run1 = (Run) doc.getChildNodes(NodeType.RUN, true).get(0);
Run run2 = (Run) doc.getChildNodes(NodeType.RUN, true).get(1);

run1.getParentNode().insertAfter(commentRangeStart, run1);
run2.getParentNode().insertAfter(commentRangeEnd, run2);

doc.save("D:\\temp\\out-17.4.docx");

Hope, this helps.

Best regards,

Is there a way, after creating a Comment object, to move to that comment object and use builder.write() to create its content without desturbing the flow of writing the document itself? If this is possible, how would I then move right back to where I left off writing the document and resume?
Can I do: builder.moveTo(comment)?
Can I add a Paragraph object to the comment and then use moveToParagraph()?
So far, even if something like the above will work, I haven’t found a way to go back. If multiple write() calls continue creating the same Run object, then, if possible, I’d like to know a way to go back without interrupting that Run.

Hi Brian,

Thanks for your inquiry. Yes, you can use DocumentBuilder.moveTo(Node) method to move cursor to Comment object or to the Paragraph inside Comment object. You can also drop a hidden Bookmark at the position where you want to return back at some later time. Please try using the following code:

Document doc = new Document();
DocumentBuilder builder = new DocumentBuilder(doc);

// write some content to document
builder.writeln("Some text is added.");
builder.writeln("Some more text is added.");

// Insert a hidden Bookmark where you want to return at some later time
builder.startBookmark("_hdnBM");
builder.endBookmark("_hdnBM");

builder.writeln();
builder.writeln("Some more text is added.");

// create a new Comment  object
Comment comment = new Comment(doc, "Awais Hafeez", "AH", new Date());
builder.getCurrentParagraph().appendChild(comment);

// add a new Paragraph to Comment object
comment.getParagraphs().add(new Paragraph(doc));
comment.getFirstParagraph().getRuns().add(new Run(doc, "Comment text."));

// go back to the return point
builder.moveToBookmark("_hdnBM", false, true);
builder.write(" SOME MORE TEXT IN CAPS.");

// move to inside Comment object again and write something
builder.moveTo(comment.getFirstParagraph());
builder.write(" --> inside comment");

doc.save("D:\\temp\\out-17.5.docx");

Hope, this helps.

Best regards,

Awais,
I can’t thank yoiu enough for your amazing support and with a quick turn around. We are making a great editot for the blind thanks to your support. Here are a couple more questions from our coder…
Does each builder.write() (not writeln) create a new Run object, or do multiple writes append to the same Run object unless something significant (like a font) has changed?
Since it seems that revision tracking can only be enabled to one level, how do we build a document where a paragraph is a revision and some of its runs are also revisions?
Also, If, when revision tracking is on, text is written and then deleted, what will that result in? Will it create a revision that’s marked as both insert and delete, in no revision, or something else?

Hi Brian,

Thanks for your feedback.

Brian:
Does each builder.write() (not writeln) create a new Run object, or do multiple writes append to the same Run object unless something significant (like a font) has changed?

Each DocumentBuilder.write(String) will create a new Run object irrespective of Font. However, in case where you want to merge adjacent Runs with same formatting into a single Run, you can use Document.joinRunsWithSameFormatting method at the end before calling Save method.

Brian:
Since it seems that revision tracking can only be enabled to one level, how do we build a document where a paragraph is a revision and some of its runs are also revisions?

A user can insert a complete new Paragraph and mark it as a Revision. Another user can append Run to the same Paragraph and mark the Run as a Revision. In this case the Paragraph will be a Revision and the last Run of this Paragraph will also be a separate Revision. Please try using the following code:

Document doc = new Document();
DocumentBuilder builder = new DocumentBuilder(doc);

doc.startTrackRevisions("user1");
builder.writeln("This paragraph is an Insertion revision ");
doc.stopTrackRevisions();

// Now Create a new run of text.
Run run = new Run(doc, "This Run has a separate Insertion revision ");
// Specify character formatting for the run of text.
Font f = run.getFont();
f.setName("Courier New");
f.setSize(36);

doc.startTrackRevisions("user2");
// Append the run of text to the end of the first paragraph
doc.getFirstSection().getBody().getFirstParagraph().appendChild(run);
doc.stopTrackRevisions();

builder.writeln("this is last paragraph without revision.");

doc.save("D:\\temp\\android-via-java-17.5.docx");

Brian:
Also, If, when revision tracking is on, text is written and then deleted, what will that result in? Will it create a revision that’s marked as both insert and delete, in no revision, or something else?

‘Aspose.Words for Android via Java’ API mimics the behavior of MS Word i.e. if text is written and then deleted by the same user, then this will result in No Revisions. If text is written by user1 and same text is then deleted by user2, then this text will have two Revisions i.e. one Insertion and one Deletion revision.

Hope, this helps.

Best regards,

Continued thanks! Here is another follow-up question:
Aspose.Words mimics the behavior of MS Word i.e. if text is written and then deleted by the same user, then this will result in No Revisions. If text is written by user1 and same text is then deleted by user2, then this text will have two Revisions i.e. one Insertion and one Deletion revision.
I have an MS Word document which has a couple of Run objects that are marked as both an insertion and a deletion. In other words, run.isInsertRevision() and
run.isDeleteRevision() both return true. How cn such a Run object be created when building a document?

Hi Brian,

Thanks for your inquiry. Please attach your sample Word document (.docx file) here for testing. We will investigate the scenario on our end and provide you more information.

Best regards,

Hello Awais,
I am not sure where to attache a file for you to look at. Still looking for where to attach it.