UpdatePageLayout make Table broken

I just updated from version 21.1.0 to 22.8.0 and the render output not look like the old output
I have used UpdatePageLayout function in 1 method and then it will break all table layout in all page
do we have any other solution for update page layout ?

My scenario is I have a table and it maybe stay in 1 or 2 pages , then I need to define the first row and the last row to know that table will continue in the next page and I will print the continue text to the first row of next page

Example if the first row in page 1 and the last row in page 2 then I know that the table will have next page

@TanPham Could you please attach your input document along with code that will allow us to reproduce the problem? We will check the issue and provide you more information.

the code is so long, hard to provide it. I also have a case when i try to GetStartPageIndex after that the table become null

@TanPham Unfortunately, it is difficult to say what is going wrong on your side without reproducing the problem on our side. So please try to create a simple code that will allow us to replicate the problem on our side.
Also, please make sure the license is properly applied. You have mentioned that your old version is 21.1.0, most likely your license for this version has been expired and to upgrade to the latest 22.8.0 version you should also renew your license. If the license was not applied, Aspose.Words work in evaluation mode and truncates the document, which might cause the problem.

I just reliazed when we set width of cell in table it not auto-fit table layout anymore, do we have some way do that in the new version ? can you take a look the picture I have attached @alexey.noskov

in the old version I use builder.CellFormat.Width to set width for each cell but it still can auto fit column
example in the picture the first column can resize smaller , but the new version it didnt do that

@TanPham I think, you should set Table.AllowAutoFit property.
Also, there is Table.AutoFit method, which allow to autofit the table to it’s content, window or use fixed column widths.
Please see our documentation to learn more about applying formatting to cells, rows and tables.

@alexey.noskov
I try many things but still not like my expect

you can see this picture, the table layout before and after I use function doc.UpdatePageLayout . In the version 21.1.0 when I use doc.UpdatePageLayout it will not break the table like that
This is my code

public class RowList
{
    public List<CellData> CellDatas { get; set; }
}
public class CellData
{
    public string Value { get; set; }
    public int Width { get; set; }
    public int TopPadding { get; set; } = 1;
    public int BottomPadding { get; set; } = 0;
    public int RightPadding { get; set; } = 2;
    public int LeftPadding { get; set; } = 0;
    public CellVerticalAlignment VerticalAlignment { get; set; } = CellVerticalAlignment.Bottom;
    public ParagraphAlignment HorizontalAlignment { get; set; } = ParagraphAlignment.Right;
}
public class RenderFunction()
{
    Document doc = new Document();
    DocumentBuilder builder = new DocumentBuilder(doc);
    //if this sheet is empty 
    AsposeWordsHelper.SetPageSetup(doc, Orientation.Portrait, 50, 60, 28.08, 38.08);
    #region BuildHeader
    builder.MoveToHeaderFooter(HeaderFooterType.HeaderPrimary);
    builder.PageSetup.HeaderDistance = 24;
    builder.Font.Bold = true;
    builder.Write("INFORMATION");
    builder.Font.Size = 7;
    builder.Writeln();
    builder.MoveToDocumentEnd();
    #endregion
    var currentPageNumber = doc.PageCount;
    builder.ParagraphFormat.Style.Font.Size = 9;
    builder.StartTable();
    var data = new List<RowList>();
    var firstRow = new CellData { Value = "PERFORMANCE HIGHLIGHTS (continued)", Width = 560, RightPadding = 6, HorizontalAlignment = ParagraphAlignment.Left, VerticalAlignment = CellVerticalAlignment.Top };
    data.Add(new RowList { CellDatas = new List<CellData> { firstRow
    ///data row 2
    var dataRow2 = new List<CellData>();
    dataRow2.Add(new CellData { Value = "Series", Width = 35, HorizontalAlignment = ParagraphAlignment.Left });
    dataRow2.Add(new CellData { Value = "As at", Width = 45 });
    dataRow2.Add(new CellData { Value = " ", Width = 10 });
    dataRow2.Add(new CellData { Value = "Total net asset value", Width = 50 });
    dataRow2.Add(new CellData { Value = " ", Width = 10 });
    dataRow2.Add(new CellData { Value = "Number of securities outstanding1", Width = 60 });
    dataRow2.Add(new CellData { Value = " ", Width = 10 });
    dataRow2.Add(new CellData { Value = "Management expense ratio (%)²", Width = 65 });
    dataRow2.Add(new CellData { Value = " ", Width = 10 });
    dataRow2.Add(new CellData { Value = "MER before waivers or absorptions(%)²", Width = 65 });
    dataRow2.Add(new CellData { Value = " ", Width = 10 });
    dataRow2.Add(new CellData { Value = "Trading expense ratio (%)3", Width = 65 });
    dataRow2.Add(new CellData { Value = " ", Width = 10 });
    dataRow2.Add(new CellData { Value = "Portfolio turnover rate (%)4", Width = 50 });
    dataRow2.Add(new CellData { Value = " ", Width = 10 });
    dataRow2.Add(new CellData { Value = "Net asset value per security ($)", Width = 55 });
    data.Add(new RowList { CellDatas = dataRow2 });
    ///data row 3
    var dataRow3 = new List<CellData>();
    dataRow3.Add(new CellData { Value = "1", Width = 30, HorizontalAlignment = ParagraphAlignment.Left });
    dataRow3.Add(new CellData { Value = "This information is provided as at each period shown.", Width = 530 });
    data.Add(new RowList { CellDatas = dataRow3 });
    ///
    builder.RowFormat.HeightRule = HeightRule.Exactly;
    builder.RowFormat.Height = 20;
    builder.RowFormat.AllowBreakAcrossPages = false;
    builder.RowFormat.HeadingFormat = true;
    foreach (var row in data)
    {
        foreach (var cell in row.CellDatas)
        {
            builder.InsertCell();
            builder.CellFormat.Width = cell.Width;
            builder.Write(cell.Value);
        }
        builder.EndRow();
     }
    builder.EndTable();
    builder.MoveToDocumentEnd();
    doc.UpdatePageLayout();
}

Although the width of first row is 530 and its equal total width of cell in second row but after use UpdatePageLayOut then it not right anymore

@TanPham Thank you for additional information. I have managed to reproduce the problem on my side. For a sake of correction it has been logged as WORDSNET-24252. We will keep you informed and let you know once it is resolved.
Upon analysis I detected that the problem does not occur if save the document to DOCX before calling UpdatePageLayout:

// If save the document to DOCX before calling UpdatePageLayout, the table looks correct in both documents.
doc.Save(@"C:\Temp\out.docx");
doc.UpdatePageLayout();
doc.Save(@"C:\Temp\out_updatePageLayout.docx");

that 's so weird, thanks for your support

1 Like

@TanPham We have completed analysis of the issue and concluded it is not a bug, but an expected behavior.
The issue occurs because an incomplete table definition constructed by your code is not interpreted as the you expect.
The behavior is intentional and it matches MS Word behavior if a similar table structure is read from the source document.

Your code constructs a table with 1 cell in row 1, 16 cells in row 2 and 2 cells in row 3. No cell grid spans or horizontal cell merges are specified. As the table is auto-fit, table cell widths are updated when UpdatePageLayout() is called. A table grid is for a jagged table is calculated and saved in the document model.

Earlier Aspose.Words versions did not support table grid re-calculation for the problematic table. Since 22.2 version, the table is handled by the new logic which matches MS Word for the case when the same document model is read from the source docx. This logic works as designed.

Without UpdatePageLayout() call, docx writer detects that table grid with column width data is missing. As it needs the data to write tblGrid element, the code assumes that all cells actually have the currently specified width and updates cell grid spans according to the current cell widths, which makes the cell in row 1 to span 16 columns and so on. This is the behavior expected by you.

However, in that scenario, table content metrics and container column width are ignored completely. The grid produced by the method (and saved to docx) does not match MS Word as MS Word actually squeezes most columns in order to fit into container. The generated output in this scenario has inconsistent data in tblGrid element and it may cause layout differences when the document is opened in earlier Aspose.Words versions. The cell widths displayed in MS Word will not actually match the widths specified in your code.

For consistent results, your code should specify the table structure completely, including which cell spans which column.
This can be computed from the specified cell widths by calling Table.AutoFit(AutoFitBehavior.FixedColumnWidths) explicitly.
The method will reset the table layout to fixed however, so that the cell widths specified by your code are preserved.
In order to get the expected result, table layout and preferred widths should be set after the above method.

Adding the following lines after constructing the table will yield consistent results both with and without UpdateTableLayout() or intermediate saving to docx:

...
Table table = builder.EndTable();

// Calculate cell grid spans from the widths specified in the builder.
table.AutoFit(AutoFitBehavior.FixedColumnWidths);
// Make the table auto-fit, but preserve the specified preferred widths.
table.AllowAutoFit = true;
// Make the table fit to container if possible.
table.PreferredWidth = PreferredWidth.FromPercent(100.0);

I have the new issue when I use foreach to insert row data , I have to call doc.updatepagelayout() to check the new page count (doc.PageCount) to excute some logic . Then the Table was broken
Example:

 var oldPage = doc.PageCount;
 InsertRowData();
 doc.UpdatePageLayout();
 var currentPage = doc.PageCount;
 if(currentPage != oldPage){ Excute some logic here}

@TanPham As it was described in the above provided analysis, the problem occurs because you are calling UpdatePageLayout when table structure is incomplete. In the provided code you are calling UpdatePageLayout while building the table, so in this case the table structure is incomplete is still incomplete.
I would recommend you to call UpdatePageLayout after the table is built and then detect which row goes to the next page. Such approach is also better because UpdatePageLayout is quite time and memory consuming operation and calling it multiple times might lead to significant performance degradation of your code.

do we have anyway to detect the row goes to next page after the table is built ? let example I need to add the title at the first row of the tables ( the title will be change it not a fix content )

@TanPham Yes, it is better to detect where table breaks to the next page after building the table. For example see the following simple code that demonstrates the scenario you have described:

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

// Build some table.
builder.StartTable();

builder.InsertCell();
builder.Write("This is a header row on the page 1");
builder.EndRow();
for (int i = 0; i < 100; i++)
{
    builder.InsertCell();
    builder.Write("This is data row");
    builder.EndRow();
}
Table table = builder.EndTable();

// Now detect where table breaks and insert an unique header row at the beginning of each page.
LayoutCollector collector = new LayoutCollector(doc);
int currentPage = collector.GetStartPageIndex(table.FirstRow);
foreach (Row r in table.Rows)
{
    int rowPageIndex = collector.GetStartPageIndex(r);

    if (rowPageIndex > currentPage)
    {
        // Add an unique header
        Row headerRow = (Row)r.Clone(true);
        r.ParentNode.InsertBefore(headerRow, r);
        headerRow.FirstCell.RemoveAllChildren();
        headerRow.EnsureMinimum();
        builder.MoveTo(headerRow.FirstCell.FirstChild);
        builder.Write(string.Format("This is a header row on the page {0}", rowPageIndex));

        currentPage = rowPageIndex;

        collector.Clear();
        doc.UpdatePageLayout();
    }
}

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

As you can see in this case UpdatePageLayout is called only 3 times (once internally when LayoutCollector is created and 2 times explicitly in the loop) instead of 100 times if check row page index after building each row.