ListLevel->TabPosition is used sometimes, and sometimes not

Attached is a document. AmericanStyleFirstLineIndent-Prose Default-processed.zip (17.2 KB)
The first paragraph has
para->ParagraphFormat->LeftIndent: 0
para->ParagraphFormat->FirstLineIndent: 72
para->ListFormat->ListLevel->TabPosition: 90, and that is reflected in Word with the number being at 72, and the text starting at 90.

The second paragraph however has
para->ParagraphFormat->LeftIndent: 72
para->ParagraphFormat->FirstLineIndent: -36
para->ListFormat->ListLevel->TabPosition: 144 however in Word the paragraph is lined up at 72.

So the question is, how does Word interpret and resolve the LeftIndent / TabPosition attributes? I am guessing that it does something like

if (ParagraphFormat->FirstLineIndent < 0) { 
   return ParagraphFormat->LeftIndent;
} else {
   return ListFormat->ListLevel->TabPosition;
}

Do you happen to know what logic they use to determine where the text starts in a list?

@tkbtadmin,

Thanks for your inquiry. ParagraphFormat.FirstLineIndent is used for first line or hanging indent. The positive value is for first-line indent and negative value is for hanging indent.

If the value of ParagraphFormat.FirstLineIndent is zero, you can get the left indent of paragraph using ParagraphFormat.LeftIndent property.

If the value of ParagraphFormat.FirstLineIndent is greater than zero, you can get the left indent of paragraph using ParagraphFormat.LeftIndent + ParagraphFormat.FirstLineIndent + ListLevels.TabPosition.

If the value of ParagraphFormat.FirstLineIndent is less than zero, you can get the left indent of paragraph using ParagraphFormat.LeftIndent + ListLevels.TabPosition.

We suggest you please get the value of tab position using List.ListLevels[0].TabPosition property.

I’ve added the following to my code

   Console::WriteLine(para->ListFormat->ListLevel->TabPosition);
   Console::WriteLine(para->ListFormat->List->ListLevels[para->ListFormat->ListLevelNumber]->TabPosition);
   Console::WriteLine(para->ListFormat->List->ListLevels[0]->TabPosition);
   Console::WriteLine(para->ParagraphFormat->LeftIndent);
   Console::WriteLine(para->ParagraphFormat->FirstLineIndent);

In the attached document the first paragraph has:

para->ListFormat->ListLevel->TabPosition: 90
para->ListFormat->List->ListLevels[para->ListFormat->ListLevelNumber]->TabPosition: 90
para->ListFormat->List->ListLevels[0]->TabPosition: 36
para->ParagraphFormat->LeftIndent: 0
para->ParagraphFormat->FirstLineIndent: 72

In Word, the number is aligned at 2.54cm (72 points), the text is aligned at ~3.81cm (108 points).

So I can get to 108 using your algorithm
LeftIndent + FirstLineIndent + TabPosition (of level 0)

Why do I need to use para->ListFormat->List->ListLevels[0]->TabPosition instead of para->ListFormat->ListLevel->TabPosition?? Do I always use ListLevels[0] regardless of what level my list is?

And for bonus points: Why is it like that?

@tkbtadmin,

Thanks for your inquiry.

You are using this property correctly.

The ListLevel.NumberPosition property returns or sets the position (in points) of the number or bullet for the list level. NumberPosition corresponds to LeftIndent plus FirstLineIndent of the paragraph

ListLevel.TabPosition property returns or sets the tab position (in points) for the list level.

Could you please share some more detail about your requirement along with screenshot of position that you want to get using Aspose.Words? We will then provide you more information about your query.

image.png (13.3 KB)

This is a screenshot of the original Word document I uploaded in the first post (AmericanStyleFirstLineIndent-Prose-Default-processed.zip - just rename it to docx).

You can see that the number (a) is at 2.54cm and the text is at 3.81cm. The text position of 3.81cm corresponds to 108 points. So word is drawing the text at 108, and I have the following values for that paragraph:

para->ListFormat->ListLevel->TabPosition: 90
para->ListFormat->List->ListLevels[para->ListFormat->ListLevelNumber]->TabPosition: 90
para->ListFormat->List->ListLevels[0]->TabPosition: 36
para->ParagraphFormat->LeftIndent: 0
para->ParagraphFormat->FirstLineIndent: 72

So there are two ways that I can calculate the value of 108:

para->ParagraphFormat->LeftIndent + 
para->ParagraphFormat->FirstLineIndent + 
para->ListFormat->List->ListLevels[0]->TabPosition
---------------------
0 + 72 + 36 = 108

para->ListFormat->ListLevel->TabPosition + 
(para->ListFormat->ListLevel->TabPosition - para->ParagraphFormat->FirstLineIndent)
---------------------
90 + (90 - 72)
90 + 18 = 108

They are the only ways I can get to 108, and they are certainly not logical. The logical thing of:

para->ParagraphFormat->LeftIndent + 
para->ParagraphFormat->FirstLineIndent + 
para->ListFormat->ListLevel->TabPosition
------------------------------------------------
0 + 72 + 90 = 162

162 is 5.71cm which is NOT where Word is putting the paragraph.

So the question remains: How do I calculate where Word is going to be drawing the paragraph? Why does using the TabPosition of para->ListFormat->List->ListLevels[0]->TabPosition work? Or is that just happening by chance?

@tkbtadmin,

Thanks for sharing the detail. We have logged a feature request as WORDSNET-15784 in our issue tracking system to get the start position of text for list items. You will be notified via this forum thread once this feature is available. We apologize for your inconvenience.

Could you please share some detail about your use case in which you want the position text start for list items? Thanks for your cooperation.

@tkbtadmin,

Thanks for your patience. It is to update you that we have closed the issue (WORDSNET-15784) with “Won’t Fix” resolution.

Please use the following code example to get the start position of text for list items.

Document document = new Document(MyDir + @"document2.docx");
var nodes = document.FirstSection.Body.GetChildNodes(NodeType.Paragraph, true);
foreach (var p in nodes)
{
    Paragraph para = (Paragraph)p;
    if (para.IsListItem)
    {
        int leftIndent = (int)para.ParagraphFormat.LeftIndent;
        int fli = (int)para.ParagraphFormat.FirstLineIndent;
        TabStop[] stops = para.GetEffectiveTabStops();
        ListLevel level = para.ListFormat.ListLevel;
        double levelTabPos = level.TabPosition;
        double textPos = levelTabPos - leftIndent;
        for (int i = 0; i < stops.Length; ++i)
        {
            if (!stops[i].IsClear)
            {
                textPos = stops[i].Position;
                break;
            }
        }
        Console.WriteLine(textPos);
    }
}

Code seems suspect.

Why the casts to int for LeftIndent and FirstLineIndent? Why not keep them as double? Is it important that they be cast to int?

What’s the point of storing the FirstLineIndent as fli? It’s not used in calculating textPos. Should the fli be used somewhere?

@tkbtadmin,

Thanks for your inquiry. In general case calculation text position is very complex task. In your case, you need to get all tab stops applied to this paragraph and choose the first tab stop without IsClear flag. IsClear means this tab stop clears any existing tab stops in this position. If there aren’t any tab stops then text position is calculating as subtraction current level tab position and left indent.

Yes, you can use the double variable instead of int.

Please remove this line from the code.
int fli = (int)para.ParagraphFormat.FirstLineIndent;

Can you please post the code for the general case calculation? I need to be able to calculate the text position of any ListItem in any document.

@tkbtadmin,

Thanks for your inquiry. You can use the same code example to get the desired output.

Ok so you’re saying that the posted code works to calculate ANY text position of a list item? i.e. FirstLineIndent never takes part in the calculation of text position.

@tkbtadmin,

Thanks for your inquiry. We are in communication with our product team about your query. We will get back to you soon.

The issues you have found earlier (filed as WORDSNET-15784) have been fixed in this Aspose.Words for .NET 18.7 update and this Aspose.Words for Java 18.7 update.