I have below code and it fails on line
le.Current = lc.GetEntity(para);
because lc.GetEntity(para) gets NULL value and it says parameter value can’t be null.
Could you please guide what is causing this to fail?
private static ParaLocationInfo GetParaBoundsEnd(LayoutEnumerator le, LayoutCollector lc, Paragraph para)
{
var sib = para.NextSibling;
object firstLine = null;
if (sib == null)
{
lc.GetStartPageIndex(para);
le.Current = lc.GetEntity(para);
@ankit.chhelavda
It appears that you are encountering a NULL
value when calling lc.GetEntity(para)
, which is causing the error. This typically occurs when the para
parameter is not a valid node type that the GetEntity
method can handle.
Cause: The issue arises when the para
variable is not a Paragraph
node, as the GetEntity
method only works with Paragraph
nodes and certain inline nodes. If para
is of a different type (like a Run
, Cell
, or Table
), it will return NULL
.
Solution: To resolve this issue, follow these steps:
-
Check Node Type: Ensure that the para
variable is indeed a Paragraph
node. If it is of a different type, you will need to adjust your code accordingly.
-
Verify Parent Node: If para
is a valid Paragraph
, check if it has been added to the document. Newly created paragraphs that haven’t been appended to a section or body may not be recognized by the layout collector.
-
Update Layout: Before calling GetEntity
, ensure that the document’s layout is updated. You can do this by calling doc.UpdatePageLayout()
after making changes to the document structure.
-
Debugging: Add debugging statements to log the type of para
and its parent node. This will help confirm that you are working with the expected node type.
Here’s a minimal code snippet to illustrate how you might check the node type:
if (para.NodeType == NodeType.Paragraph)
{
le.Current = lc.GetEntity(para);
}
else
{
Console.WriteLine("The provided node is not a Paragraph.");
}
Next Steps: Implement the above checks in your code and run it again. If the issue persists, please provide additional details about the context in which this error occurs.
Let me know if this resolves your issue or if you need further assistance!
@ankit.chhelavda This can occur because the node is in header/footer or inside shape. LayoutCollector
and LayoutEnumerator
classes work only with nodes in the main document body.
Please try adding the following condition:
// LayoutCollector and LayoutEnumerator does not work with nodes in header/footer or in textboxes.
if (r.getAncestor(NodeType.HEADER_FOOTER) != null || r.getAncestor(NodeType.SHAPE) != null)
// Skip processing .................
@alexey.noskov I tried applying this if condition but it still goes to else condition and does all the processing.
Could you please help here with other things to check?
@ankit.chhelavda Could you please attach a simple document and code that will allow us to reproduce the problem? We will check the issue and provide you more information.
@alexey.noskov Sure, let me check internally if the document can be shared or not. Thanks.
1 Like
@alexey.noskov, So from the system we are getting byte array and then we are doing next set of processing on this.
Could you please suggest what can I share so that you can check further?
@ankit.chhelavda Save byte array to file and attach it here and provide simple code example that will allow us to reproduce the problem with the problematic file on our side.
@alexey.noskov
Could you please check attached test.RTF file in the?
This will throw the error saying “Value cannot be null. Parameter name: value” in .NET
test.7z (15.9 KB)
@ankit.chhelavda Thank you for additional information. The problem occurs because the paragraph is hidden. By default hidden paragraphs are ignored upon building document layout. You should either skip hidden paragraphs:
Document doc = new Document(@"C:\Temp\in.rtf");
LayoutCollector collector = new LayoutCollector(doc);
LayoutEnumerator enumerator = new LayoutEnumerator(doc);
foreach (Paragraph p in doc.GetChildNodes(NodeType.Paragraph, true))
{
// LayoutCollector and LayoutEnumerator does not work with nodes in header/footer or in textboxes.
if (p.GetAncestor(NodeType.HeaderFooter) != null || p.GetAncestor(NodeType.Shape) != null)
continue;
// Skip hidden
if (p.ParagraphBreakFont.Hidden)
continue;
enumerator.Current = collector.GetEntity(p);
Console.WriteLine($"Page: {enumerator.PageIndex}; Bounds: {enumerator.Rectangle}");
}
Or enable rendering of hidden text in document layout options:
Document doc = new Document(@"C:\Temp\in.rtf");
// Enable rendering of hidden text
doc.LayoutOptions.ShowHiddenText = true;
LayoutCollector collector = new LayoutCollector(doc);
LayoutEnumerator enumerator = new LayoutEnumerator(doc);
foreach (Paragraph p in doc.GetChildNodes(NodeType.Paragraph, true))
{
// LayoutCollector and LayoutEnumerator does not work with nodes in header/footer or in textboxes.
if (p.GetAncestor(NodeType.HeaderFooter) != null || p.GetAncestor(NodeType.Shape) != null)
continue;
enumerator.Current = collector.GetEntity(p);
Console.WriteLine($"Page: {enumerator.PageIndex}; Bounds: {enumerator.Rectangle}");
}
@alexey.noskov thanks for the update.
Instead of checking only paragraph type with below condition
if (p.ParagraphBreakFont.Hidden)
continue;
Can we check with any type of element if it is hidden then can we skip it?
@ankit.chhelavda You can use Font.Hidden property to check whether element is marked as hidden in the document.
@alexey.noskov thanks for the update.
Currently I have a foreach loop and in this loop every time I have to check hidden condition about the paragraph and before that I have to derive the paragraph node as you can check in below code I have two lines where in first I get the paragraph in variable startPara and then in second line it checks the hidden property of it in the if condition.
Checking this every time is going to add more time in getting the output.
So is there any where with which I can remove all the hidden elements in one go and the code doesn’t get any hidden elements in the foreach loop.
var changeStarts = _doc.GetChildNodes(NodeType.BookmarkStart, true).ToArray().Cast<BookmarkStart>().Where(x => x.Name.StartsWith("_DV_C"));
var changeEnds = _doc.GetChildNodes(NodeType.BookmarkEnd, true).ToArray().Cast<BookmarkEnd>().Where(x => x.Name.StartsWith("_DV_C")).ToDictionary(x => x.Name);
ChangeRanges result = new ChangeRanges();
foreach (var start in changeStarts)
{
var name = start.Name;
if (changeEnds.TryGetValue(name, out var end))
{
Paragraph startPara = start.GetAncestor(NodeType.Paragraph) as Paragraph;
if (startPara != null && !startPara.ParagraphBreakFont.Hidden)
{
//do something
}
}
}
@ankit.chhelavda You can try using code like this:
List<Bookmark> bookmarks = doc.Range.Bookmarks
.Where(bk => bk.Name.StartsWith("_DV_C") && !BookmarkStartIsHidden(bk))
.Cast<Bookmark>().ToList();
private static bool BookmarkStartIsHidden(Bookmark bk)
{
Paragraph parentPara = bk.BookmarkStart.GetAncestor(NodeType.Paragraph) as Paragraph;
return (parentPara != null && parentPara.ParagraphBreakFont.Hidden);
}
Thank you @alexey.noskov.
Should this be applied to both NodeType.BookmarkStart and NodeType.BookmarkEnd element types?
@ankit.chhelavda You can adjust the code according to your needs. The provided code just demonstrate the technique you can use.
@alexey.noskov alright.
So with your example you have casted the list into Bookmark type whereas I have BookmarkStart and BookmarkEnd.
For BookmarkStart I am able to get the Hidden property but for BookmarkEnd it is not available.
Could you please let me know how to find the Hidden property for BookmarkEnd?
@ankit.chhelavda Bookmark
is facade class. You can access both BookmakrStart
and BookmarkEnd
through this facade using the appropriate Bookmark.BookmarkStart and Bookmark.BookmarkEnd properties. So you can apply the same logic to both nodes.
@alexey.noskov Thank you for the help Alexey! Appreciate it. 
1 Like