Table Layout is incorrect when saving HTML Table as SVG file

Hello,

We are seeing an issue while performing HTML to SVG conversion using Aspose Words Java API where the SVG containing HTML Table renders incorrectly when saved as SVG. This behavior is seen while testing with Aspose Words for Java version 21.7.0. I have attached the sample input HTML file and resulting 2 SVG output files based on different configuration set to match SVG output with HTML.

We are facing few issues during the conversion process which is listed below.

  1. During Save process the Table is split into 2 Sections - FirstChild is Table and LastChild is paragraph which results in Page break as seen the SVG files attached.
  2. The Table height in attached SVG files is incorrect when compared to actual HTML file but Table width is matching.
  3. When setting Table.PreferredWidth to AUTO and fromPoints(PageSetup.PageWidth) the resulting SVG output is different with respect to Column Rendering and Width.
    -TestSample1.svg with AUTO, the last 2 columns of the Table is rendered incorrectly
    -TestSample2.svg with fromPoints(), table right border is missing and the columns width is incorrect when compared to actual HTML file.
  4. The Document.ViewOptions.ZoomType and Document.ViewOptions.ZoomPercent is provided but it has no effect on the resulting Table within the attached SVG outputs.

Sample Java Code for the HTML to SVG conversion:

double objHeight = 822.0;
double objWidth = 1680.0;
InputStream inputStream = new ByteArrayInputStream(htmlBytes);
FontSettings fontSettings = FontSettings.getDefaultInstance();
fontSettings.setFontsFolder(fontDirectory.getDeployedFontFolder(), true);
	
//set load options and font settings
HtmlLoadOptions loadOptions = new HtmlLoadOptions();
loadOptions.setLoadFormat(LoadFormat.HTML);
loadOptions.setEncoding(StandardCharsets.UTF_8);
loadOptions.setFontSettings(fontSettings);	
	
//load the HTML document with ASPOSE Document object
Document wordDoc = new Document(inputStream, loadOptions);
DocumentBuilder builder = new DocumentBuilder(wordDoc);
	
//get the HTML document pageSetup to alter margin and size
PageSetup pageSetup = builder.getPageSetup();
double pageHeight = ConvertUtil.pixelToPoint(objHeight);
double pageWidth = ConvertUtil.pixelToPoint(objWidth);
pageSetup.setPageHeight(pageHeight);
pageSetup.setPageWidth(pageWidth);
pageSetup.setLeftMargin(0);
pageSetup.setRightMargin(0);
pageSetup.setTopMargin(0);
pageSetup.setBottomMargin(0);
	
Section section = builder.getCurrentSection();
Body body = section.getBody();
Iterator<Node> nodes = body.getChildNodes().iterator();
while(nodes.hasNext()) {
	Node node = nodes.next();
	int nodeType = node.getNodeType();
		
	//NodeType.TABLE = 5, NodeType.PARAGRAPH = 8
	switch(nodeType) {
	case NodeType.TABLE:
		Table table = Table.class.cast(node);
		table.setAllowAutoFit(false);
			
		//Uncomment to get TestSample1.svg output
		//table.setPreferredWidth(PreferredWidth.AUTO);
			
		//Uncomment to get TestSample2.svg output.
		//table.setPreferredWidth(PreferredWidth.fromPoints(pageWidth));
			
		//This method will also perform re-rendering of table layout
		//based on table properties set explicitly.
		wordDoc.updateTableLayout();
		break;
	case NodeType.PARAGRAPH:
		System.out.println("I am inside Paragraph");
		break;
	default:
		break;
	}
}
	
//get the SVG SaveOptions to update the output within Document
SvgSaveOptions saveOptions = new SvgSaveOptions();
saveOptions.setShowPageBorder(false);
saveOptions.setOptimizeOutput(true);
wordDoc.save("TestSample.svg", saveOptions);

Also, when I used the WarningCallBack option I got the following error:
Warning Type: MINOR_FORMATTING_LOSS, Warning Source: LAYOUT,
Warning: Table column widths may need to be calculated. Rendered column widths could differ. At Table 1, Section 1.

Environment Details:

Aspose Words for Java 21.7.0
Java version 1.8
Linux 7.

File description in the TestSample.zip (30 KB) attachment contains:

TestSample.html: HTML file that is imported into the new Document per the code above has 30 rows and 17 columns.
TestSample1.svg: SVG file produced from the HTML to SVG conversion code mentioned above with PreferredWidth.AUTO settings.
TestSample2.svg: SVG file produced from the HTML to SVG conversion code mentioned above with PreferredWidth.fromPoints(PageSetup.PageWidth) settings.

TestSample.zip (29.7 KB)

@oraspose Please note that Aspose.Words is designed to work with MS Word document and it interprets HTML more like MS Word than as Web browser.

MS Word content is layout into pages, Aspose.Words does the same when you render the document. To get the desired output you can adjusts page size so the whole table fit one page. For example the following code gives quite good result on my side:

Document doc = new Document("C:\\Temp\\TestSample.html");

PageSetup ps = doc.getFirstSection().getPageSetup();
// Reset margins
ps.setLeftMargin(0);
ps.setRightMargin(0);
ps.setTopMargin(0);
ps.setBottomMargin(0);

// Set page size to fit the whole table. Note Aspose.Words uses points as the main measurement unit.
ps.setPageWidth(1200);
ps.setPageHeight(750);

doc.getFirstSection().getBody().getTables().get(0).autoFit(AutoFitBehavior.AUTO_FIT_TO_CONTENTS);

doc.save("C:\\Temp\\out.svg");

out.zip (13.5 KB)

In your code you use Document.updateTableLayout method, which is deprecated and is not normally recommended to use. This method will be removed in one of future versions of Aspose.Words.

Document.ViewOptions.ZoomType and Document.ViewOptions.ZoomPercent are applicable only to MS Word formats.

If you need your HTML to be interpreted and rendered as Web Browser does, you can consider using Aspose.HTML product:
https://docs.aspose.com/html/java/converting-between-formats/

The solution provided fits the table in 1 page but it is not applicable for my use-case. Thank you for the response.

@oraspose Please feel free to ask in case of any other issues. We will be glad to help you.

Hi,

I am trying to set custom height for the Document using PageSetup.setPageHeight() method but the conversion process does not set the height as per provided value. The Document width is accepting the correct value as provided after pixelToPoint conversion takes place.

double height = 175.0;
double width = 654.0;
double newWidth = ConvertUtil.pixelToPoint(width);
double newHeight = ConvertUtil.pixelToPoint(height);
    
DocumentBuilder builder = new DocumentBuilder(doc);
PageSetup pageSetup = builder.getPageSetup();
pageSetup.setPageHeight(newHeight);
pageSetup.setPageWidth(newWidth);

SvgSaveOptions options = new SvgSaveOptions();
options.setFitToViewPort(false);
options.setShowPageBorder(false);

doc.save("test.svg", options);

The resulting SVG has the following changes:

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="654" height="361">

Why the conversion process did not accept the custom height?

SVG output attached for reference. SVG-Test 1.zip (4.1 KB)

@oraspose I cannot reproduce the problem on my side. I have used the following simple code for testing:

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

builder.write("Hello, World!!!");

double height = 175.0;
double width = 654.0;
double newWidth = ConvertUtil.pixelToPoint(width);
double newHeight = ConvertUtil.pixelToPoint(height);

PageSetup pageSetup = builder.getPageSetup();
pageSetup.setPageHeight(newHeight);
pageSetup.setPageWidth(newWidth);

SvgSaveOptions options = new SvgSaveOptions();
options.setFitToViewPort(false);
options.setShowPageBorder(false);

doc.save("C:\\Temp\\test.svg", options);

And the produced SVG has correct page size:

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="654" height="175">

Hi,

Could you try with this sample HTML file attached? Could you confirm the SVG height and width matches with the HTML file?

Test 1.zip (1.1 KB)

@oraspose The problem occurs because table does not fit one page in your case and two pages are rendered into SVG. You can increase document height to overcome this. For example see the following code:

Document doc = new Document("C:\\Temp\\in.html");

double height = 175.0;
double width = 654.0;
double newWidth = ConvertUtil.pixelToPoint(width);
double newHeight = ConvertUtil.pixelToPoint(height);

PageSetup pageSetup = doc.getFirstSection().getPageSetup();
pageSetup.setPageHeight(newHeight);
pageSetup.setPageWidth(newWidth);
pageSetup.setLeftMargin(0);
pageSetup.setRightMargin(0);
pageSetup.setTopMargin(0);
pageSetup.setBottomMargin(0);

// Check whether document fits one page. If not increase page height.
while (doc.getPageCount() > 1)
{
    double addition = doc.getPageCount() > 2 ? (doc.getPageCount() - 1) * pageSetup.getPageHeight() : 10;
    pageSetup.setPageHeight(pageSetup.getPageHeight() + addition);
    doc.updatePageLayout();
}

SvgSaveOptions options = new SvgSaveOptions();
options.setFitToViewPort(false);
options.setShowPageBorder(false);

doc.save("C:\\Temp\\test.svg", options);

test.zip (3.3 KB)

Hi,

The solution works to some extent meaning if the “updatePageLayout()” API is deprecated(mentioned in the first reply for this question at the top), then what is the alternate way to update pageLayout so that the page count keeps reducing based on page height.

@oraspose The updateTableLayout method is deprecated, not the updatePageLayout. So the proposed solution should work as expected for you.

Thank you for the information.

1 Like

Hi,

I noticed in the SVG output(Test1.png file) that even though I set LeftMargin as 0 but there is still some space visible on the left. Any idea how to fix it? I also noticed this issue in the file “test.zip” from the previous reply.

Also, is there a way to stretch the width and height of the table or paragraph to fill remaining blue area? The blue area shown in the image “Test 2.png”

Document doc = new Document("D:\\Temp\\Test 1.html");

double height = 174.0;
double width = 662.0;
double newWidth = ConvertUtil.pixelToPoint(width);
double newHeight = ConvertUtil.pixelToPoint(height);

PageSetup pageSetup = doc.getFirstSection().getPageSetup();
pageSetup.setPageHeight(newHeight);
pageSetup.setPageWidth(newWidth);
pageSetup.setLeftMargin(0);
pageSetup.setRightMargin(0);
pageSetup.setTopMargin(0);
pageSetup.setBottomMargin(0);

// Check whether document fits one page. If not increase page height.
while (doc.getPageCount() > 1)
{
    double addition = doc.getPageCount() > 2 ? (doc.getPageCount() - 1) * pageSetup.getPageHeight() : 10;
    pageSetup.setPageHeight(pageSetup.getPageHeight() + addition);
    doc.updatePageLayout();
}

SvgSaveOptions options = new SvgSaveOptions();
options.setFitToViewPort(false);
options.setShowPageBorder(false);

doc.save("D:\\Temp\\Test 1.svg", options);

Test 1.zip (1.1 KB)
Test 1.png (12.8 KB)
Test 2.png (31.1 KB)

@oraspose As I already mentioned Aspose.Words is not designed to convert HTML to SVG, it is designed to work with MS Word documents. In MS Word document a table cannot be the only node in the document, there is always an empty paragraph after the table. This paragraph causes an empty space at the bottom on your table.

You can use LayoutCollector and LayoutEnumerator classes to calculate actual table dimensions and render a table more accurately. For example see the following code that renders table to series of SVG images (one image for page if table spans several pages):

Document doc = new Document("C:\\Temp\\in.html");
Iterable<Table> tables = doc.getChildNodes(NodeType.TABLE, true);
int tableIndex = 0;
for (Table t : tables)
{
    // Reset left indent. since table might be shifted left.
    t.setLeftIndent(0);

    // Render only top level tables
    if (t.getAncestor(NodeType.TABLE) == null)
        RenderTable(t, tableIndex++);
}
private static void RenderTable(Table table, int tableIndex) throws Exception
{
    Document tempDoc = (Document)table.getDocument().deepClone(false);
    tempDoc.appendChild(tempDoc.importNode(table.getAncestor(NodeType.SECTION).deepClone(false), false));
    tempDoc.ensureMinimum();

    tempDoc.getFirstSection().getBody().prependChild(tempDoc.importNode(table, true));

    if(tempDoc.getPageCount()==1) {
        RenderTablePart(tempDoc, tableIndex,0);
    }
    else {
        for (int i = 0; i < tempDoc.getPageCount(); i++)
        {
            RenderTablePart(tempDoc.extractPages(i, 1), tableIndex, i);
        }
    }
}
private static void RenderTablePart(Document oneTableDoc, int tableIndex, int partIndex) throws Exception
{
    LayoutCollector collector = new LayoutCollector(oneTableDoc);
    LayoutEnumerator enumerator = new LayoutEnumerator(oneTableDoc);

    Table table = oneTableDoc.getFirstSection().getBody().getTables().get(0);

    // Calculate table size.
    // For demonstration purposes the example purposes the while table is on the same page.
    enumerator.setCurrent(collector.getEntity(table.getFirstRow().getFirstCell().getFirstParagraph()));
    // Move enumerator to a row.
    while (enumerator.getType()!= LayoutEntityType.ROW)
        enumerator.moveParent();

    double top = enumerator.getRectangle().y;
    double left = enumerator.getRectangle().x;

    // Move enumerator to the last row.
    enumerator.setCurrent(collector.getEntity(table.getLastRow().getFirstCell().getFirstParagraph()));
    // Move enumerator to a row.
    while (enumerator.getType()!= LayoutEntityType.ROW)
        enumerator.moveParent();

    double bottom = enumerator.getRectangle().y + enumerator.getRectangle().height;
    double right = enumerator.getRectangle().x + enumerator.getRectangle().width;

    // Reset margins
    PageSetup ps = oneTableDoc.getFirstSection().getPageSetup();
    ps.setPageWidth(ps.getPageWidth()-ps.getLeftMargin()-ps.getRightMargin());
    ps.setLeftMargin(0);
    ps.setRightMargin(0);
    ps.setPageHeight(ps.getPageHeight()-ps.getTopMargin()-ps.getBottomMargin());
    ps.setTopMargin(0);
    ps.setBottomMargin(0);

    // Set calculated width
    ps.setPageWidth(right - left);
    ps.setPageHeight(bottom - top);

    oneTableDoc.updatePageLayout();

    // Save table part. Render only the first page.
    SvgSaveOptions opt = new SvgSaveOptions();
    opt.setPageSet(new PageSet(0));
    oneTableDoc.save("C:\\Temp\\out_" + tableIndex + "_" + partIndex + ".svg", opt);
}

This code produces the following output, which however, still has slight empty space at the bottom of the table: out_0_0.zip (3.3 KB)

1 Like

Hi,

Appreciate your help and support throughout this issue. The example works fine for me and I was able to get rid of the bottom border by doing opt.setShowPageBorder(false). The bottom white space should be fine since it is very minimum. Thank you again for the help. Have a good day.

1 Like

Hi,

Could explain in detail what the While loop is calculating? When I debug this code, it looks like it just adds the page height of each page and sets Document page count to be 1. I was expecting more of a logic where we are able to remove pages if empty so that if document has single page it has the correct size based on the content.

Below is the sample print statements of above logic when debugged.

Start Document PageCnt: 1
Start Document PageHeight: 792.0

//Now here we make call to set page height. Width is not set
pageSetup.setPageHeight(ConvertUtil.pixelToPoint(174.0))

Before starting WhileLoop Document PageCnt: 11
Before starting WhileLoop Document PageHeight: 131.25

//Now when running while loop
Current Document PageCnt: 11.0
pageHeight : 131.250000, addition: 1312.500000 = 1443.750000

//Now, outside while loop the final page count and page height
End Document PageCnt: 1
End Document PageHeight: 1443.75

If I had to set the document page size to be exact 5inch x 5inch with custom paper size provided, how would I achieve it? Could you provide a full fledged code for this scenario? For this please consider the attached SVG file as input.

Test 1.zip (4.1 KB)

@oraspose The loop is the workaround to make the document to fit all the content on one page. As I already mentioned Aspose.Words is designed to work with MS Word documents and when you convert document to Fixed Page formats, like SVG, Image or PDF, the document is layout into pages.
You should also note that the code will not work for all the cases, because there is a limit of the maximum page size in MS Word documents and in Aspose.Words as well - 1584 pt.
If your goal is to render table as an SVG image, please use the code suggested in this reply:
https://forum.aspose.com/t/table-layout-is-incorrect-when-saving-html-table-as-svg-file/255933/13
Before using this code you can set the maximum allowed page height, the code will calculate the actual size of the table and use this size to generate an image.

1 Like

Thank you for the information.

1 Like

Hi,

I noticed something strange when saving the HTML as SVG output. If the page size is provided in Decimals such as 489.5666, then the pixel to point conversion returns correct decimal value. When this decimal value is set on the page size using PageSetup.setPageWidth() and PageSetup.setPageHeight(), the SVG attributes width and height does not show the decimal values.

Is the APOSE Document.Save() method converting the Decimals to Integers before rendering the SVG?

double pageWidthPx = 489.5666;
double pageHeightPx = 242.88;

//pixel to point conversion
double pageWidthPt = ConvertUtil.pixelToPoint(pageWidthPx);
double pageHeightPt = ConvertUtil.pixelToPoint(pageHeightPx);

//set page size    
PageSetup pageSetup = doc.getBuilder().getPageSetup();
pageSetup.setPageWidth(pageWidthPt);
pageSetup.setPageHeight(pageHeightPt);

//reset page margin to 0
pageSetup.setLeftMargin(0);
pageSetup.setRightMargin(0);                            
pageSetup.setTopMargin(0);
pageSetup.setBottomMargin(0);

//save as SVG
SvgSaveOptions svgOptions = new SvgSaveOptions();
svgOptions.setFitToViewPort(false);
svgOptions.setShowPageBorder(false);
svgOptions.setPageSet(new PageSet(0));
doc.save("C:\\Temp\Test.svg", svgOptions);

Test 1 - SVG.zip (7.9 KB)

@oraspose While saving to SVG page width and height in pixels are rounded to integer. So fraction parts are lost. This is expected behavior.

Thank you for the confirmation.

1 Like