How to calculate height and width of splited content control

Hello team,
I am trying to calculate height and width of splited content control on multiple page and set height and width as a bookmark name and add this bookmark inside content control first child. How can i implement this ?

Document sourceDoc = new Aspose.Words.Document(@"C:\Temp\SourceDoc.docx");
var sdts = outputDoc.GetChildNodes(NodeType.StructuredDocumentTag, true).Where(x => ((Aspose.Words.Markup.StructuredDocumentTag)x).Tag.StartsWith("Rangescop_"));
foreach (Aspose.Words.Markup.StructuredDocumentTag sdt in sdts)
{
    CalculateHeightAndWidth(ref outputDoc, sdt.Tag, (Paragraph)sdt.GetChildNodes(NodeType.Paragraph, true).FirstOrDefault(), (Paragraph)sdt.GetChildNodes(NodeType.Paragraph, true).LastOrDefault());
}
outputDoc.Save(@"C:\Temp\OutputDoc.docx", SaveFormat.Docx);

I already have logic for calculate height and width but this logic not working on calculate height and width of splited content control.

This is my function for calculate height and width :

private void CalculateHeightAndWidth(ref Document document, string Tag, Paragraph startParagraph, Paragraph endParagraph)
{
    try
    {
        LayoutCollector LayoutCollector = new LayoutCollector(document);

        LayoutEnumerator = new LayoutEnumerator(document)
        {
            Current = LayoutCollector.GetEntity(startParagraph)
        };
        while (LayoutEnumerator.Type != LayoutEntityType.Line)
            LayoutEnumerator.MoveParent();
        //Get the rectangle occuped by the first line of the paragraph.
        RectangleF rect1 = LayoutEnumerator.Rectangle;
        LayoutEnumerator.Current = LayoutCollector.GetEntity(endParagraph);
        while (LayoutEnumerator.Type != LayoutEntityType.Line)
            LayoutEnumerator.MoveParent();

        RectangleF rect2 = LayoutEnumerator.Rectangle;

        //Union of the rectangles is the region occuped by the paragraph.
        RectangleF result = RectangleF.Union(rect1, rect2);
        Builder.MoveTo(startParagraph);
        string temp = "HW_" + Tag.ToString() + "_" + result.Height.ToString().Split('.')[0] + "_" + result.Width.ToString().Split('.')[0];
        Builder.StartBookmark(temp);
        Builder.EndBookmark(temp);
        LayoutCollector.Clear();
        document.UpdatePageLayout();
    }
    catch (Exception e)
    {

    }
}

SourceDoc.docx (68.9 KB)

@AlpeshChaudhari12345 You can use the approach, I have suggested in another your thread, to detect where structured document tag goes to the next page:
https://forum.aspose.com/t/how-to-check-current-sdt-content-is-splited-in-two-pages/258484/2

@alexey.noskov yes i have used this logic but some time height and width not getting properly. Please refer this source doc.

SourceDoc.docx (89.1 KB)

here some content control height and width not getting proper.
second content control in getting height: 63.9639969. its wrong height.

Document sourceDoc = new Aspose.Words.Document(@"C:\Temp\SourceDoc.docx");
var sdts = outputDoc.GetChildNodes(NodeType.StructuredDocumentTag, true).Where(x => ((Aspose.Words.Markup.StructuredDocumentTag)x).Tag.StartsWith("Rangescop_"));
foreach (Aspose.Words.Markup.StructuredDocumentTag sdt in sdts)
{
    AddBookMarkOnSplitedContentControl(sdt, ref sourceDoc)
    }
outputDoc.Save(@"C:\Temp\OutputDoc.docx", SaveFormat.Docx);

this is my logic for detect first paragraph and last paragraph of each content control and calculate height and width of content control.

private void AddBookMarkOnSplitedContentControl(Aspose.Words.Markup.StructuredDocumentTag sdt, ref Document ouputDoc)
{

    LayoutCollector layoutCollector = new LayoutCollector(ouputDoc);
    DocumentBuilder builder = new DocumentBuilder(ouputDoc);
    // Split all Run nodes in the SDT to make them not more than one word.
    List<Run> runs = sdt.GetChildNodes(NodeType.Run, true).Cast<Run>().ToList();
    List<Paragraph> startParagraphs = new List<Paragraph>();

    List<Paragraph> endParagraphs = new List<Paragraph>();
    int j = 0;
    int previosRunIndex = layoutCollector.GetStartPageIndex(runs[0]);
    foreach (Run r in runs)
    {
        int currentRunIndex = layoutCollector.GetStartPageIndex(r);
        if (currentRunIndex != previosRunIndex)
        {
            Run current = r;
            while (current.Text.IndexOf(' ') >= 0)
                current = SplitRun(current, current.Text.IndexOf(' ') + 1);
        }
        previosRunIndex = layoutCollector.GetStartPageIndex(r);
    }

    //Now update page layout and reset LayoutCollector to work with the updated document model.
    layoutCollector.Clear();
    ouputDoc.UpdatePageLayout();
    NodeCollection updatedRuns = sdt.GetChildNodes(NodeType.Run, true);
    int currentPageIndex = layoutCollector.GetStartPageIndex(updatedRuns[0]);
    startParagraphs.Add((Paragraph)updatedRuns[0].ParentNode);
    // Loop through the runs and detect where page index changes.
    for (int i = 0; i < updatedRuns.Count; i++)
    {

        int runPageIndex = layoutCollector.GetStartPageIndex(updatedRuns[i]);
        if (runPageIndex != currentPageIndex && i != 0)
        {
            startParagraphs.Add((Paragraph)updatedRuns[i].ParentNode);
            endParagraphs.Add((Paragraph)updatedRuns[i - 1].ParentNode);
            currentPageIndex = runPageIndex;
        }

    }

    endParagraphs.Add((Paragraph)updatedRuns.LastOrDefault().ParentNode);
    layoutCollector.Clear();
    ouputDoc.UpdatePageLayout();
    for (int i = 0; i < startParagraphs.Count; i++)
    {
        CalculateHeightAndWidth(ref ouputDoc, sdt.Tag, startParagraphs[i], endParagraphs[i]);
    }
}

Actually i am converting docx to FixedHTML so i need to calculate proper height and width of content control. And add height, width with tag id as a bookmark name. after converted html fixed file i am using Html Agility pack tool to find bookmark based on bookmark name attribute and after getting this bookmark in html agility node i will set this height and width on content control div element.
You have any suggestion for my requirement ?

@AlpeshChaudhari12345 I have adopted the code provided here to calculate bounding box rectangle of SDT on each page. Please see the following code:

Document doc = new Document(@"C:\Temp\in1.docx");

List<StructuredDocumentTag> sdts = doc.GetChildNodes(NodeType.StructuredDocumentTag, true).Cast<StructuredDocumentTag>()
    .Where(x => x.Tag.StartsWith("Rangescop_")).ToList();

// Split all Run nodes in the SDTs to make them not more than one word.
foreach (StructuredDocumentTag sdt in sdts)
{
    // Split all Run nodes in the SDT to make them not more than one word.
    List<Run> runs = sdt.GetChildNodes(NodeType.Run, true).Cast<Run>().ToList();
    foreach (Run r in runs)
    {
        Run current = r;
        while (current.Text.IndexOf(' ') >= 0)
            current = SplitRun(current, current.Text.IndexOf(' ') + 1);
    }
}

// Create LayoutCollector
LayoutCollector collector = new LayoutCollector(doc);

int sdtIndex = 0;
foreach (StructuredDocumentTag sdt in sdts)
{
    if (collector.GetNumPagesSpanned(sdt) == 0)
    {
        // If the SDT spans only one page, whole SDT content into a bookmark.
        BookmarkStart start = new BookmarkStart(doc, string.Format("sdt_{0}", sdtIndex));
        BookmarkEnd end = new BookmarkEnd(doc, start.Name);

        sdt.InsertBefore(start, sdt.FirstChild);
        sdt.InsertAfter(end, sdt.LastChild);
    }
    else
    {
        // If the SDT spans more than one page, wrap each SDT page content into a temporary bookmark.
        NodeCollection runs = sdt.GetChildNodes(NodeType.Run, true);
        int currentPageIndex = -1;
        // Loop through the runs and detect where page index changes.
        BookmarkEnd end = null;
        for (int i = 0; i < runs.Count; i++)
        {
            // Insert  bookmark before the Run where page index changes.
            Node currnetRun = runs[i];
            int runPageIndex = collector.GetStartPageIndex(currnetRun);
            if (runPageIndex != currentPageIndex)
            {
                // Insert end before the previouse run.
                if (end != null)
                {
                    if (i > 0)
                        runs[i - 1].ParentNode.InsertBefore(end, runs[i - 1]);
                    else
                    currnetRun.ParentNode.InsertBefore(end, currnetRun);
                }

                BookmarkStart start = new BookmarkStart(doc, string.Format("sdt_{0}_{1}", sdtIndex, runPageIndex));
                currnetRun.ParentNode.InsertBefore(start, currnetRun);

                currentPageIndex = runPageIndex;
                end = new BookmarkEnd(doc, start.Name);
            }
        }
        sdt.InsertAfter(end, sdt.LastChild);
    }

    sdtIndex++;
}


// Now once we wrapped per-page content of SDTS with bookmarks we can calculate bounding boxes of the SDTs.
collector.Clear();
doc.UpdatePageLayout();
LayoutEnumerator enumerator = new LayoutEnumerator(doc);

foreach (StructuredDocumentTag sdt in sdts)
{
    List<Bookmark> tmpBookmakrs = sdt.Range.Bookmarks.Cast<Bookmark>().Where(b => b.Name.StartsWith("sdt_")).ToList();
    foreach (Bookmark bk in tmpBookmakrs)
    {
        enumerator.Current = collector.GetEntity(bk.BookmarkStart);
        // Move enumerator to the line where bookmakr start is located.
        while (enumerator.Type != LayoutEntityType.Line)
            enumerator.MoveParent();

        RectangleF firstLineRect = enumerator.Rectangle;

        enumerator.Current = collector.GetEntity(bk.BookmarkEnd);
        // Move enumerator to the line where bookmark end is located.
        while (enumerator.Type != LayoutEntityType.Line)
            enumerator.MoveParent();

        RectangleF lastLineRect = enumerator.Rectangle;

        // Union of the rectangles is the bounding box of the SDT part located on the page.
        RectangleF result = RectangleF.Union(firstLineRect, lastLineRect);

        Console.WriteLine(result);

        // For demonstraction purposes put floating shape rectangle on top of SDT.
        Shape rect = new Shape(doc, ShapeType.Rectangle);
        rect.Filled = false;
        rect.Stroked = true;
        rect.StrokeColor = Color.Red;
        rect.WrapType = WrapType.None;
        rect.RelativeHorizontalPosition = RelativeHorizontalPosition.Page;
        rect.RelativeVerticalPosition= RelativeVerticalPosition.Page;
        rect.Left = result.Left;
        rect.Top = result.Top;
        rect.Width = result.Width;
        rect.Height = result.Height;
        bk.BookmarkStart.ParentNode.InsertAfter(rect, bk.BookmarkStart);
    }
}

doc.Save(@"C:\Temp\out1.docx");

For demonstration purposes a red rectangle is drawn to show the calculated rectangle in the output document. I have tested with both document you have attached and the rectangles are calculate properly.