Tables are rendered differently in Word and PDF

Hey all,

look at this code and the generated Word and PDF. The PDF table overflows the right margin of the page regardless of what is set for table.setAllowAutoFit or table.autoFit. What needs to be done to obtain a PDF output
similar to the Word one?

@Test
public void testCellWidth() throws Exception
{
    DocumentBuilder docBuilder = new DocumentBuilder();
    com.aspose.words.Style style = docBuilder.getDocument().getStyles().get("Normal");
    if (style != null)
    {
        docBuilder.getParagraphFormat().setStyle(style);
    }

    docBuilder.clearRunAttrs();

    // begin table
    Table table = docBuilder.startTable();

    docBuilder.getParagraphFormat().clearFormatting();
    docBuilder.getCellFormat().clearFormatting();
    docBuilder.getRowFormat().clearFormatting();

    // The first row
    docBuilder.getRowFormat().setHeight(0);

    // The first cell
    docBuilder.insertCell();

    // setting or not setting the AutoFitBehavior doesn’t seem to make any difference
    // table.autoFit( AutoFitBehavior.AUTO_FIT_TO_CONTENTS);
    // table.autoFit( AutoFitBehavior.AUTO_FIT_TO_WINDOW);
        
    // setting or not setting the setAllowAutoFit doesn’t seem to make any difference on PDF.
    // setting to FALSE results in a Word document expanding past the border which is expected
    table.setAllowAutoFit( true);

    docBuilder.getCellFormat().clearFormatting();
    docBuilder.getCellFormat().getBorders().setLineStyle(LineStyle.SINGLE);
    docBuilder.getCellFormat().getBorders().setLineWidth(1);
    docBuilder.getCellFormat().setWidth( 100);
    docBuilder.getCellFormat().setPreferredWidth( PreferredWidth.fromPoints( 100));

    docBuilder.getParagraphFormat().clearFormatting();
    docBuilder.write("Lorem ipsum dolor sit amet");

    // The second cell
    docBuilder.insertCell();
    docBuilder.getCellFormat().clearFormatting();
    docBuilder.getCellFormat().getBorders().setLineStyle(LineStyle.SINGLE);
    docBuilder.getCellFormat().getBorders().setLineWidth(1);
    docBuilder.getCellFormat().setWidth( 300);
    docBuilder.getCellFormat().setPreferredWidth( PreferredWidth.fromPoints( 300));

    docBuilder.getParagraphFormat().clearFormatting();
    docBuilder.write("Lorem ipsum dolor sit amet");

    // The third cell
    docBuilder.insertCell();
    docBuilder.getCellFormat().clearFormatting();
    docBuilder.getCellFormat().getBorders().setLineStyle(LineStyle.SINGLE);
    docBuilder.getCellFormat().getBorders().setLineWidth(1);
    docBuilder.getCellFormat().setPreferredWidth( PreferredWidth.AUTO);

    docBuilder.getParagraphFormat().clearFormatting();
    docBuilder.write("Lorem ipsum dolor sit amet");

    // The fourth cell
    docBuilder.insertCell();
    docBuilder.getCellFormat().clearFormatting();
    docBuilder.getCellFormat().getBorders().setLineStyle(LineStyle.SINGLE);
    docBuilder.getCellFormat().getBorders().setLineWidth(1);
    docBuilder.getCellFormat().setPreferredWidth( PreferredWidth.AUTO);

    docBuilder.getParagraphFormat().clearFormatting();
    docBuilder.write("Lorem ipsum dolor sit amet");

    // End the first row
    docBuilder.endRow();

    // end table
    docBuilder.endTable();

    docBuilder.getDocument().save("c:\testtable1.doc");
    docBuilder.getDocument().save("c:\testtable1.pdf");
}

Regards,
Dragos

Hi

Thanks for your request. Please try calling Table.autoFit method after you finish building the table. There is no sense to call this method before.

Best regards,

Putting this at the end makes no difference. Please try the snipet I provided and move the call to autoFit between the endRow and endTable. The same results is obtained.

Regards,
Dragos

Hi Dragos,

Thanks for your inquiry.

I managed to reproduce the issue on my side, your request has been linked to the appropriate issue and you will be informed as soon as it’s resolved.

In the mean time you can export the table in the correct format by calling doc.updateTableLayout() before saving to PDF.

Thanks,

Hey Adam,

thanks, the solution works to a certain extent. However as soon as you introduce a field in the document and updateFields the table overlaps in PDF once again.

@Test
public void testCellWidthPDFOverflow() throws Exception
{
    DocumentBuilder docBuilder = new DocumentBuilder();
    com.aspose.words.Style style = docBuilder.getDocument().getStyles().get("Normal");
    if (style != null)
    {
        docBuilder.getParagraphFormat().setStyle(style);
    }

    docBuilder.clearRunAttrs();

    // begin table
    Table table = docBuilder.startTable();

    docBuilder.getParagraphFormat().clearFormatting();
    docBuilder.getCellFormat().clearFormatting();
    docBuilder.getRowFormat().clearFormatting();

    // The first row
    docBuilder.getRowFormat().setHeight(0);

    // The first cell
    docBuilder.insertCell();

    docBuilder.getCellFormat().clearFormatting();
    docBuilder.getCellFormat().getBorders().setLineStyle(LineStyle.SINGLE);
    docBuilder.getCellFormat().getBorders().setLineWidth(1);
    docBuilder.getCellFormat().setWidth( 150);
    docBuilder.getCellFormat().setPreferredWidth( PreferredWidth.fromPoints( 150));

    docBuilder.getParagraphFormat().clearFormatting();
    docBuilder.write("Lorem ipsum dolor sit amet");

    // The second cell
    docBuilder.insertCell();
    docBuilder.getCellFormat().clearFormatting();
    docBuilder.getCellFormat().getBorders().setLineStyle(LineStyle.SINGLE);
    docBuilder.getCellFormat().getBorders().setLineWidth(1);
    docBuilder.getCellFormat().setWidth( 300);
    docBuilder.getCellFormat().setPreferredWidth( PreferredWidth.fromPoints( 300));

    docBuilder.getParagraphFormat().clearFormatting();
    docBuilder.write("Lorem ipsum dolor sit amet");

    // The third cell
    docBuilder.insertCell();
    docBuilder.getCellFormat().clearFormatting();
    docBuilder.getCellFormat().getBorders().setLineStyle(LineStyle.SINGLE);
    docBuilder.getCellFormat().getBorders().setLineWidth(1);
    docBuilder.getCellFormat().setWidth( 100);
    docBuilder.getCellFormat().setPreferredWidth( PreferredWidth.fromPoints( 100));

    docBuilder.getParagraphFormat().clearFormatting();
    docBuilder.write("Lorem ipsum dolor sit amet");

    // End the first row
    docBuilder.endRow();

    table.setAllowAutoFit( true);

    // end table
    docBuilder.endTable();

    // add a page number in the footer
    docBuilder.insertField("PAGE", "");

    docBuilder.getDocument().updateFields();
    docBuilder.getDocument().updateTableLayout();

    docBuilder.getDocument().save("c:\testtable_overflow.doc");
    docBuilder.getDocument().save("c:\testtable_overflow.pdf");
}

Regards,
Dragos

Hi Dragos,

Thanks for this additional information.

Please try moving the call to updateTableLayout to before the call to updateFields. More than likely page layout is being invoked during field update and this is rendering the document in memory. After this any changes to the document will not applied until page layout is rebuilt.

Thanks,

Thanks Adam, it seems to be working but I want to make some more tests before claiming victory.

Regards,
Dragos

Hey Adam,

changing the order fixes this issue for the current testcase I’m on but another issue shows up: single cell table with lots of contents is very narrow in PDF. My test is more complex than what I posted here but for a simple way to reproduce this behavior see this post: RowFormat setAllowAutoFit can not work as expected

Regards,
Dragos

I’ve managed to simplify this to as little as using two tables and a field. I assume it would have worked with just two tables and using updatePageLayout instead of updateFields. In the code bellow switch the order of the updateFields and updateTableLayout and you will see 2 distinct issues:

  • updateFields first - table overflows page margin
  • updateTableLayout first - single cell table does not resize
@Test
public void testSingleCell() throws Exception
{
    DocumentBuilder docBuilder = new DocumentBuilder();
    com.aspose.words.Style style = docBuilder.getDocument().getStyles().get("Normal");
    if (style != null)
    {
        docBuilder.getParagraphFormat().setStyle(style);
    }

    docBuilder.clearRunAttrs();

    docBuilder.writeln( "First table");
    // first table
    {

        // begin table
        Table table = docBuilder.startTable();

        docBuilder.getParagraphFormat().clearFormatting();
        docBuilder.getCellFormat().clearFormatting();
        docBuilder.getRowFormat().clearFormatting();

        // The first row
        docBuilder.getRowFormat().setHeight(0);

        // The first cell
        docBuilder.insertCell();

        docBuilder.getCellFormat().clearFormatting();
        docBuilder.getCellFormat().getBorders().setLineStyle(LineStyle.SINGLE);
        docBuilder.getCellFormat().getBorders().setLineWidth(1);
        docBuilder.getCellFormat().setWidth( 150);
        docBuilder.getCellFormat().setPreferredWidth( PreferredWidth.fromPoints( 150));

        docBuilder.getParagraphFormat().clearFormatting();
        docBuilder.write("Lorem ipsum dolor sit amet");

        // The second cell
        docBuilder.insertCell();
        docBuilder.getCellFormat().clearFormatting();
        docBuilder.getCellFormat().getBorders().setLineStyle(LineStyle.SINGLE);
        docBuilder.getCellFormat().getBorders().setLineWidth(1);
        docBuilder.getCellFormat().setWidth( 300);
        docBuilder.getCellFormat().setPreferredWidth( PreferredWidth.fromPoints( 300));

        docBuilder.getParagraphFormat().clearFormatting();
        docBuilder.write("Lorem ipsum dolor sit amet");

        // The third cell
        docBuilder.insertCell();
        docBuilder.getCellFormat().clearFormatting();
        docBuilder.getCellFormat().getBorders().setLineStyle(LineStyle.SINGLE);
        docBuilder.getCellFormat().getBorders().setLineWidth(1);
        docBuilder.getCellFormat().setWidth( 100);
        docBuilder.getCellFormat().setPreferredWidth( PreferredWidth.fromPoints( 100));

        docBuilder.getParagraphFormat().clearFormatting();
        docBuilder.write("Lorem ipsum dolor sit amet");

        // End the first row
        docBuilder.endRow();

        table.setAllowAutoFit( true);

        // end table
        docBuilder.endTable();
    }

    // second table
    docBuilder.writeln( "");
    docBuilder.writeln( "Second table");
    {
        // begin table
        Table table = docBuilder.startTable();

        docBuilder.getParagraphFormat().clearFormatting();
        docBuilder.getCellFormat().clearFormatting();
        docBuilder.getRowFormat().clearFormatting();

        // The first row
        docBuilder.getRowFormat().setHeight(0);

        // The first cell
        docBuilder.insertCell();

        docBuilder.getCellFormat().clearFormatting();
        docBuilder.getCellFormat().getBorders().setLineStyle(LineStyle.SINGLE);
        docBuilder.getCellFormat().getBorders().setLineWidth(1);

        docBuilder.getParagraphFormat().clearFormatting();
        docBuilder.write("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent vitae blandit lectus. Vestibulum nec dolor at leo consequat pellentesque. Ut euismod aliquet massa dapibus mattis. Nunc tristique eros nec quam feugiat vel euismod erat tincidunt. Suspendisse potenti. Quisque euismod vestibulum facilisis. Quisque tempus, lectus ut molestie fermentum, nisl purus mattis sapien, ut facilisis turpis nulla eget purus. Cras sit amet justo tortor. Curabitur sed tellus nec sem ultrices accumsan in vel est. Nunc feugiat laoreet purus, et mollis orci dictum a. Aliquam a ante risus. Integer feugiat posuere orci eu sagittis. Cras molestie diam et eros molestie interdum. Nam dignissim enim a velit fringilla a rhoncus ipsum iaculis. Cras varius orci et odio fermentum nec semper magna fermentum. Pellentesque tempor fermentum nibh ullamcorper consectetur.");

        // none of the below helps
        table.autoFit( AutoFitBehavior.AUTO_FIT_TO_WINDOW);
        table.setAllowAutoFit( true);

        // end table
        docBuilder.endTable();
    }

    // add a page number in the footer
    docBuilder.insertField("PAGE", "");

    docBuilder.getDocument().updateTableLayout();
    docBuilder.getDocument().updateFields();

    docBuilder.getDocument().save("c:\testtable_singlecell.doc");
    docBuilder.getDocument().save("c:\testtable_singlecell.pdf");
}

Hi Dragos,

Thanks for this additional information.

I managed to reproduce the issue on my side. This appears to be linked to the same issue as reported before.

In the mean time you can avoid this bug by removing the call to updateTableLayout and ensuring that the total of the widths of your columns does not exceed the available width on the page. In your example code the total widths of the cells are 520 points whereas the available space on the page is only around 430 points. Please see the code below.

PageSetup ps = docBuilder.getCurrentSection().getPageSetup();
double availableSpace = ps.getPageWidth() - ps.getLeftMargin() - ps.getRightMargin();

Thanks,

Thanks for the update Adam.

Once this and the resize issue cause by hyperlinks are fixed, will it still be required to use the updateTableLayout function? When and why should that function be used?

Regards,
Dragos

Hi Dragos,

Thanks for your inquiry.

Once the issue is resolved there should be no need to use the updateTableLayout method.

A cell in a table can have a width and a preferred width as well. The width stores the last “last calculated width” which was calculated when table layout was last updated (either by Microsoft Word or Aspose.Words etc).

Sometimes this value can become outdated and because table layout isn’t updated unless explicitly called this can then lead to the table looking different to what they look like when opened in Microsoft Word when saved to some formats using Aspose.Words e.g PDF.

Currently our algorithm for calculating table widths is little different to Microsoft Word which can cause problems, therefore we recommend only to update table layout if yo need to (such as in the above situation or your situation). We are working to improve the table layout algorithm to be identical to the algorithm used in MS Word.

Thanks,

Hi Dragos,

Hope you are well.

I’m a developer walking on an issue linked to this tread. The issue occurs with the following code snippet (I removed irrelevant lines from your snippet above in this thread for brevity):

docBuilder.InsertCell();

docBuilder.CellFormat.ClearFormatting();
docBuilder.CellFormat.Width = 300;
docBuilder.CellFormat.PreferredWidth = PreferredWidth.FromPoints(300);

docBuilder.Write("Lorem ipsum dolor sit amet");

docBuilder.InsertCell();

docBuilder.CellFormat.ClearFormatting();
docBuilder.CellFormat.PreferredWidth = PreferredWidth.Auto;

The issue is that the width of the second cell is not Auto in pdf output, resulting in cells going beyond the right page boundary.

The issue occurs because, according to the API Reference, CellFormat.ClearFormatting() does not clear Width or PreferredWidth settings.

So the code for the last cell actually keeps Width setting from the previous cell, resulting in a somewhat contradictory setting like this:

docBuilder.CellFormat.Width = 300;
docBuilder.CellFormat.PreferredWidth = PreferredWidth.Auto;

Aspose.Words in case of saving to pdf takes Width into account, and in case of saving to doc it takes PreferredWidth.

I agree that this is confusing at the least.

However, an obvious workaround would be to set Width and PreferredWidth explicitly after CellFormat.ClearFormatting(), bearing in mind that it does not actually clear width settings.

I keep the issue linked to this thread open. We eventually plan to unify table layout algorithms between pdf layout engine and doc formats, but it’s pretty complex and will not happen in the nearest releases. Please use the workaround above until then.

Thanks & Best Regards,

Hey Dmitry,

I am not sure what table your workaround applies. I have 2 tables in my output and you seem to be referring to the 1st one while my problem is with the second table. Applying your recommended workaround does not seem to help in my scenario anyway.

Attached are my JUnit along with the PDF output where the issue is obvious. I also recommend you observe the behavior after commenting the call to updateTableLayout.

Regards,
Dragos

Hi Dragos,

Thank you for the additional information.

My previous post actually applies to the code snippet in your very first post in this thread. There is only one table there. That was the code snippet in the issue description in our defect database, and that’s why I thought that the problem was really with this code snippet.

Please let me know if you still have problems with cell sizes in that snippet after contradicting Width/PreferredWidth settings are reconciled.

Now I realize that there is also an issue with a two-table test you attached in your latest post.

Actually, I did tested with that code as well when working on the original issue in the very first post. However, I assumed that the problem should a rendering difference between .doc and .pdf output, and found none. That’s why I did not try to make any comments on that issue earlier.

From the pdf file your attached in your previous post I assume that the problem must be the narrow cell size of the only cell in the second table. I assume you intended to make it span the whole column width.

However, I was not able to reproduce the issue with the code you attached.

I tested with the latest (end of December) release. The only cell in the second table spans the whole page.

I was able to get a narrow cell size in the second table only on removing this call from your test:

table.autoFit( AutoFitBehavior.*AUTO_FIT_TO_WINDOW*);

In this case, in absence of explicitly set Width or PreferredWidth, this cell uses the current values from docBuilder.CellFormat property. They were actually set to 100 points when configuring the last cell of a previous table. That’s why the cell in the second table is rendered narrow.

Please let me know if setting AutoFitBehavior.AUTO_FIT_TO_WINDOW with the latest release still does not yield the desired result for this test.

Calling autoFit() method recalculates the cell widths and overwrites previously set values.

You can also make the cell span the whole page without calling this method. For example, this settings for the second table work fine for me as well:

docBuilder.getCellFormat().setWidth( 0);
docBuilder.getCellFormat().setPreferredWidth( PreferredWidth.fromPercent(100d));
…
table.setAllowAutoFit(true);

I conducted all the tests without calling updateTableLayout(). This method is generally not a recommended way, though it may help in some cases. Look how cautious is the API reference about this method:

Remarks

You do not normally need to call this method as cell and table widths are maintained automatically. You can call
this method before exporting to PDF (or any other fixed-page format), only
in rare cases
where you confirmed that tables appear incorrectly laid out
in the output document. Calling this method might help to correct the
output.

In your scenario, the situation is even more complicated for
this method, because you clear the default format settings by calling
clearFormatting(). The default format settings were designed to provide the
results similar to MS Word with minimum additional settings. So when you clear
them, you actually get CellFormat which is somewhat different from a “naturally
expected” default. I believe in this case calling updateTableLayout may create
more problems than it solves.

For example, this code:

DocumentBuilder docBuilder = new DocumentBuilder();
printRowFormat(docBuilder.getRowFormat(), "Before clearFormatting():");
docBuilder.getRowFormat().clearFormatting();
printRowFormat(docBuilder.getRowFormat(), "After clearFormatting():");
private static void printRowFormat(RowFormat rowFormat, String comment)
{
    System.out.println(comment);
    System.out.println(" PreferredWidth: " + rowFormat.~~getPreferredWidth~~());
    System.out.println(" AllowAutoFit: " + rowFormat.~~getAllowAutoFit~~());
}

Will produce the following output:

Before clearFormatting():
PreferredWidth: 100%
AllowAutoFit: true
After clearFormatting():
PreferredWidth: Auto
AllowAutoFit: false

You can see that the settings controlling width are quite different. So you may need to specify explicitly attributes that clearFormat() clears to get the desired results.

Hope this helps. Please let me know if you still have issues, I will be glad to help you.

Thanks and Best Regards,

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