Fit image to table cell width


#1

We are using Apose.Words for Java 19.6.

We are trying to insert images using DocumentBuilder.insertImage and resizing them to scale to the containing table cell by using the cell’s cellformat.getwidth() method. This works most of the time but in one scenario it doesn’t. If a table is inserted into another table and the nested table has width 100%, cellformat.getwidth() returns as width the page width instead of the width of the outer table’s cell.

Is there a way to get the correct width? Document.updateTableLayout() works sometimes, but not always. Or perhaps there’s a simpler way to resize an image to fill the containing cell?


#2

@bmpi

To ensure a timely and accurate response, please attach the following resources here for testing:

  • Your input Word document.
  • Please attach the output Word file that shows the undesired behavior.
  • Please attach the expected output Word file that shows the desired behavior.
  • Please create a simple Java application ( source code without compilation errors ) that helps us to reproduce your problem on our end and attach it here for testing.

As soon as you get these pieces of information ready, we will start investigation into your issue and provide you more information. Thanks for your cooperation.

PS: To attach these resources, please zip and upload them.


#3

The situation has changed a bit. We have solved the above mentioned problem by using layoutEnumerator.getRectangle().getWidth(). But there’s a new problem. If for example the parent table has a width of 4cm, and the nested table has a width of 16cm, the image is resized to 16cm when the nested table is inserted into the parent table. If you do it inside word however the nested table is automatically resized. We would expect the image that is inserted into the nested table to be resized to 4cm. Here’s the code:

    Document documentA = new Document("input.docx");
    DocumentBuilder builderA = new DocumentBuilder(documentA);
    Table parent = null;
    Table nested = null;
    Boolean first = true;
    for (Object node : documentA.getChildNodes(com.aspose.words.NodeType.TABLE, true)) {
        if (first) {
            parent = (Table)node;
            first = false;
        } else {
            nested = (Table)node;
            break;
        }
    }
    builderA.moveTo(parent.getRows().get(0).getCells().get(0).getFirstParagraph());        
    Table copy = (Table)nested.deepClone(true);
    com.aspose.words.CompositeNode paragraph = builderA.getCurrentParagraph();
    com.aspose.words.CompositeNode body = paragraph.getParentNode();
    body.insertBefore(copy, paragraph);
    builderA.moveTo(copy.getRows().get(0).getCells().get(0).getFirstParagraph());
    LayoutEnumerator layoutEnumerator = new LayoutEnumerator(documentA);
    Object e = new LayoutCollector(documentA).getEntity(copy.getRows().get(0).getCells().get(0).getFirstParagraph());
    layoutEnumerator.setCurrent(e);
    layoutEnumerator.moveParent();
    Rectangle2D.Float rect = layoutEnumerator.getRectangle();
    double width2 = rect.getWidth() - copy.getRows().get(0).getCells().get(0).getCellFormat().getLeftPadding() - copy.getRows().get(0).getCells().get(0).getCellFormat().getRightPadding();
    Shape img2 = builderA.insertImage("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><svg viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'><path d='M80,67C47,121-25,60,10,22C5,89,70,80,80,67zM30,16C2,42,56,87,80,47C70,57,24,57,30,16M50,10C45,34,72,37,80,31C57,55,32,21,50,10z' fill='#4082ae'/></svg>".getBytes());
    img2.setAspectRatioLocked(true);
    img2.setWidth(width2); 
    documentA.save("output.docx");

The attached zip file has the documents. Expected.docx has the image resized to the expected width. Can you have a look at that please?

documents.zip (28.8 KB)


#4

@bmpi

Please remove the following line of code from your code to get the desired output.

img2.setWidth(width2);


#5

But that line is needed to resize the image to fit the cell. Unless you are saying that Aspose.Words automatically resizes the image when inserting it?


#6

@bmpi

You are using DocumentBuilder.InsertImage method (Byte[]). This method inserts an image that is inserted inline and at 100% scale. You can use DocumentBuilder.InsertImage method (Byte[], Double, Double) method to get the desired output.


#7

Yes, that might be true. But we’re trying to rescale the image proprtionally so that the width of the image is the same as the width of the cell. I think in this case the main problem is that layoutEnumerator.getRectangle() returns a rectangle that has a width of 16cm instead of 4cm. So my question mainly becomes: how do you reliably get the width of a nested cell?


#8

@bmpi

We have tested the scenario using the shared input.docx and have not found the issue with LayoutEnumerator.Rectangle. Your input document does not have nested table. Could you please ZIP and attach the document for which you are facing this issue?


#9

The code works like this:

  1. Read Input.docx
  2. Copy second table into the first table (second table becomes a nested table)
  3. Insert the image
  4. Resize the image based on the width of the first cell of the nested table

Output.docx has the nested table you are looking for. If you delete the image in output.docx you’ll see a table inside another table that has a grey background. That’s the nested table. That is the table for which the Rectangle gives the wrong width.


#10

@bmpi

We have tested the scenario and have managed to reproduce the same issue at our side. For the sake of correction, we have logged this problem in our issue tracking system as WORDSNET-18921. You will be notified via this forum thread once this issue is resolved.

We apologize for your inconvenience.


#11

@bmpi

It is to inform you that the issue which you are facing is actually not a bug in Aspose.Words. So, we have closed this issue (WORDSNET-18921) as ‘Not a Bug’.

Please use the following code example to get the desired output.

Document doc = new Document(MyDir + "input.docx");
DocumentBuilder builder = new DocumentBuilder(doc);
Table table1 = (Table)doc.getFirstSection().getBody().getTables().get(0).deepClone(true);
Table table2 = doc.getFirstSection().getBody().getTables().get(1);

Table node = (Table)table2.getFirstRow().getFirstCell().insertAfter(table1, table2.getFirstRow().getFirstCell().getFirstParagraph());

builder.moveTo(node.getFirstRow().getFirstCell().getFirstParagraph());
Shape img2 = builder.insertImage(MyDir + "input.svg");

doc.updateTableLayout();
LayoutCollector collector = new LayoutCollector(doc);
LayoutEnumerator enumerator = new LayoutEnumerator(doc);

enumerator.setCurrent(collector.getEntity(node.getFirstRow().getFirstCell().getFirstParagraph()));

// Enumerator now points to a paragraph break.
// Ascend to the parent entity to get the container cell.
enumerator.moveParent();

img2.setAspectRatioLocked(true);
img2.setWidth(enumerator.getRectangle().getWidth());

doc.save(MyDir + "19.7-java.docx");

#12

You changed my code to something else, that’s why it works. Your code puts the 4-cell table into the 1-cell table. My code however puts the 1-cell table into the first cell of the 4-cell table. The (gray) 1-cell table has a width set that is larger than the cell it is being put into. That’s the problem. Calling updateTableLayout does not fix the issue either.

The workaround we are using right now is setting the 1-cell table to 100% width instead of centimetres. But we can’t always control that so a different solution would be nice.


#13

@bmpi

Please replace copy object with parent object in the following line of code to get the desired output.

Object e = new LayoutCollector(documentA).getEntity(parent.getRows().get(0).getCells().get(0).getFirstParagraph());

Please check the following complete code example.

Document documentA = new Document(MyDir + "input.docx");
DocumentBuilder builderA = new DocumentBuilder(documentA);
Table parent = null;
Table nested = null;
Boolean first = true;
for (Object node : documentA.getChildNodes(com.aspose.words.NodeType.TABLE, true)) {
    if (first) {
        parent = (Table)node;
        first = false;
    } else {
        nested = (Table)node;
        break;
    }
}
builderA.moveTo(parent.getRows().get(0).getCells().get(0).getFirstParagraph());
Table copy = (Table)nested.deepClone(true);

com.aspose.words.CompositeNode paragraph = builderA.getCurrentParagraph();
com.aspose.words.CompositeNode body = paragraph.getParentNode();
body.insertBefore(copy, paragraph);
builderA.moveTo(copy.getRows().get(0).getCells().get(0).getFirstParagraph());
LayoutEnumerator layoutEnumerator = new LayoutEnumerator(documentA);
Object e = new LayoutCollector(documentA).getEntity(parent.getRows().get(0).getCells().get(0).getFirstParagraph());
layoutEnumerator.setCurrent(e);
layoutEnumerator.moveParent();
Rectangle2D.Float rect = layoutEnumerator.getRectangle();
double width2 = rect.getWidth() - copy.getRows().get(0).getCells().get(0).getCellFormat().getLeftPadding() - copy.getRows().get(0).getCells().get(0).getCellFormat().getRightPadding();
Shape img2 = builderA.insertImage("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><svg viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'><path d='M80,67C47,121-25,60,10,22C5,89,70,80,80,67zM30,16C2,42,56,87,80,47C70,57,24,57,30,16M50,10C45,34,72,37,80,31C57,55,32,21,50,10z' fill='#4082ae'/></svg>".getBytes());
img2.setAspectRatioLocked(true);
img2.setWidth(width2);
documentA.save(MyDir + "output.docx");