thandley
Forum Replies Created
-
AuthorPosts
-
Glad your code is working Alan!
It seems that updating my NSID values using my approach or yours produces the same effect. It’s hard to describe, but it looks like some lists are now interweaved. Like a list starts numbering 1, 2, 3 and then it jumps to 1 again, and then back to 4, 5, then theres a 2…
Some places have badly formatted lists that seem to have appeared out of nowhere… it’s all just messed up.
I think I’ll need to do more pre-merge cleaning of my input documents. The source documents are not under my control so they can come in many forms and may or may not be in good shape when passed to my program. It’s tough.
Arrgh… that all worked on my simple test documents. But when I tested it on real world use cases that are much more complex then it failed.
I suppose those Nsid values are referenced in other places.
At least I’m on the right track.
I figured it out!!
It was not related to the ToC update modal as I last suspected.
The numbered list pairs that were misbehaving each had matching <w:nsid w:val=”X”/> nodes in their abstractNum definition nodes.
So I think I can just do a post process to make all nsid’s unique and I should be good.
EDIT:
I was able to solve this with the following codeprivate static void FixNumberingPart(MainDocumentPart mainPart) { List<AbstractNum> nums = mainPart.NumberingDefinitionsPart.Numbering.Descendants<AbstractNum>().ToList(); foreach (AbstractNum num in nums) { bool isDuplicate = nums.Count(instance => instance.Nsid.Val.Value == num.Nsid.Val.Value) > 1; if (isDuplicate) { Console.WriteLine($"Found duplicate Nsid = {num.Nsid.Val.Value}"); num.Nsid.Val = HexBinaryValue.FromString(GetRandomHexNumber(8)); Console.WriteLine($"New Nsid = {num.Nsid.Val.Value}"); } } } private static readonly Random Random = new Random(); private static string GetRandomHexNumber(int digits) { byte[] buffer = new byte[digits / 2]; Random.NextBytes(buffer); string result = string.Concat(buffer.Select(x => x.ToString("X2")).ToArray()); if (digits%2 == 0) { return result; } return result + Random.Next(16).ToString("X"); }
- This reply was modified 8 years, 1 month ago by thandley.
I found my issue, now to work out the solution.
At the bottom of the numbering.xml part I have found that the lists that are not resetting to 1 correctly are mapping to the same abstractNumId:
<w:num w:numId="10"> <w:abstractNumId w:val="9"/> </w:num> <w:num w:numId="11"> <w:abstractNumId w:val="9"/> </w:num> <w:num w:numId="12"> <w:abstractNumId w:val="12"/> </w:num> <w:num w:numId="13"> <w:abstractNumId w:val="12"/> </w:num> <w:num w:numId="14"> <w:abstractNumId w:val="3"/> </w:num> <w:num w:numId="15"> <w:abstractNumId w:val="3"/> </w:num> <w:num w:numId="16"> <w:abstractNumId w:val="4"/> </w:num> <w:num w:numId="17"> <w:abstractNumId w:val="4"/> </w:num>
If I create new abstractNum nodes, and update all guids to new unique values and remap one of each of these pairs to the new version then it resets as expected.
EDIT:
So it turns out that my document is correct until after I open it and say OK to the “Update fields” modal dialog that I enabled to refresh the ToC.After my program is finished with the document, the numbering part has unique numbering IDs and unique abstract numbering IDs that match to the document part numbered lists as I would expect.
When I say OK and then save and close the document to see what changed in the XML I can see that the numbering part has been changed to have duplicate abstract numbering IDs as shown above.
- This reply was modified 8 years, 1 month ago by thandley.
I’ll attempt to clarify my issue.
Document 1 has two numbered lists consecutively as such:
1. <content>
2. <content>
3. <content>
— page break, new section —
1. <content>
2. <content>
3. <content>Document 2 has the same situation.
I merge Doc1 and Doc2 such that all four numbered lists are consecutive in the final document, so we want:
1. <content>
2. <content>
3. <content>
— page break, new section —
1. <content>
2. <content>
3. <content>
— page break, new section —
1. <content>
2. <content>
3. <content>
— page break, new section —
1. <content>
2. <content>
3. <content>But instead we get
1. <content>
2. <content>
3. <content>
— page break, new section —
4. <content>
5. <content>
6. <content>
— page break, new section —
1. <content>
2. <content>
3. <content>
— page break, new section —
4. <content>
5. <content>
6. <content>EDIT:
Before including the changes from this thread I was seeing the four lists all numbered in one sequence from 1 to 12.- This reply was modified 8 years, 1 month ago by thandley.
I managed to get past that error, and the documents merge successfully but it’s still not what I am expecting.
In your last posted code you would need to change this section:
var abstractNumberingNumIds = wordDoc.MainDocumentPart.NumberingDefinitionsPart.RootElement.Descendants<AbstractNumId>().ToList(); logger.LogDebug("Found " + abstractNumberingNumIds.Count + " abstract num ids." + wordDoc.MainDocumentPart.NumberingDefinitionsPart.RootElement.XName.LocalName); foreach (var abstractNumberingNumId in abstractNumberingNumIds) abstractNumberingNumId.Val += highestAbstractListNumbering; //Keep the max nums up to date if (abstractNumberingNumIds.Count > 0) highestAbstractListNumbering = Math.Max(highestAbstractListNumbering, abstractNumberingNumIds.Max(ln => (ln.Val.HasValue ? ln.Val.Value : 0)));
to this:
var abstractNums = wordDoc.MainDocumentPart.NumberingDefinitionsPart.RootElement.Descendants<AbstractNum>().ToList(); logger.LogDebug("Found " + abstractNums.Count + " abstract nums." + wordDoc.MainDocumentPart.NumberingDefinitionsPart.RootElement.XName.LocalName); foreach (var abstractNum in abstractNums) abstractNum.AbstractNumberId += highestAbstractListNumbering; //Keep the max nums up to date if (abstractNums.Count > 0) highestAbstractListNumbering = Math.Max(highestAbstractListNumbering, abstractNums.Select(a => a.AbstractNumberId).Max(n => n.HasValue ? n.Value : 0));
My code is a bit different so I only modified your section by eye here, apologies if I missed something in the syntax changes. The idea is that you were previously updating the values of Descendants<AbstractNumId>() twice on accident… the previous foreach that grabs Descendants<NumberingInstance>() changes the same AbstractNumId instances that you were changing here, so each pass was updating them twice!
The formatting of my document remains correct, but there are still some numbered lists that aren’t restarting at 1 for me. Maybe I’m missing a step in here somewhere still.
EDIT:
It looks like the numbered lists from different source documents are correctly restarting at 1 as expected, but some of my documents have multiple consecutive numbered lists that continue ordering when I expect them to restart at 1.- This reply was modified 8 years, 1 month ago by thandley.
I’m running into an issue in DocumentBuilder.CopyNumbering
// Copy abstract numbering element, if necessary (use matching NSID) string abstractNumId = element .Elements(W.abstractNumId) .First() .Attribute(W.val) .Value; XElement abstractElement = oldNumbering .Descendants() .Elements(W.abstractNum) .Where(p => ((string)p.Attribute(W.abstractNumId)) == abstractNumId) .First();
An exception is thrown when setting XElement abstractElement, because there are no elements in the sequence that match the abstractNumId.
Is that what you’re seeing, Alan?
Alan,
I have encountered a very similar issue. Maybe we can work though this together?
I’m going to try implementing a solution similar to what you have produced so far and will let you know if I can solve the styles issue.
It’s always fun when a google search only turns up questions without full solutions 🙂
Time to take a deep dive into OpenXML I guess!
-Trevor
-
AuthorPosts