Extreme slowness when merging multiple images

Hi everybody,

I have attached a .zip archive containing the following files:

- a template named verbale_consegna_it.doc
- a filled template named report_mod_98_5.doc
- a .xml file containing the data for the region dpi_all
- a .xml file containing the data for the region dpi

the two region are then merged with a relation defined on the fields

new String[] {"id_dipendente", "id_azienda", "id_sede", "revisione_documento"}

The problem here is that to produce that report it took more than five minutes.

Is this normal? We just migrated this report from JasperReport to mail merge, but it did not take too long before, it was filled in a matter of seconds.

Clearly, our end users will never accept such a long waiting time…

It would seem that I have found the issue, which was in our own code, sorry for placing it upon you.

Here is how we handled image merging:

public void imageFieldMerging(ImageFieldMergingArgs ifma) throws Exception
{
    try
    {
        DocumentBuilder builder = new DocumentBuilder(ifma.getDocument());
        builder.moveToMergeField(ifma.getDocumentFieldName());

        double width = -1;
        double height = -1;

        // se si trova in una cella di una tabella
        Cell cell = (Cell) builder.getCurrentParagraph().getAncestor(NodeType.CELL);
        if (cell != null)
        {
            CellFormat format = cell.getCellFormat();

            format.setLeftPadding(0.0);
            format.setRightPadding(0.0);
            format.setTopPadding(0.0);
            format.setBottomPadding(0.0);
            format.setWrapText(false);
            format.setFitText(true);

            // uso dimensioni cella
            width = format.getWidth();
            height = cell.getParentRow().getRowFormat().getHeight();
        }

        // se si trova in un textbox
        Shape shape = (Shape) builder.getCurrentParagraph().getAncestor(NodeType.SHAPE);
        if (shape != null && shape.getShapeType() == ShapeType.TEXT_BOX)
        {
            TextBox tb = shape.getTextBox();

            tb.setInternalMarginBottom(0.0);
            tb.setInternalMarginTop(0.0);
            tb.setInternalMarginLeft(0.0);
            tb.setInternalMarginRight(0.0);

            // uso dimensioni textbox
            width = shape.getWidth();
            height = shape.getHeight();
        }

        // immagine come array di byte
        byte[] bytes = (byte[]) ifma.getFieldValue();

        if (width> 0)
        {
            // ricostruisco immagine per calcolare proporzioni
            BufferedImage img = ImageIO.read(new ByteArrayInputStream(bytes));

            // ricavo fattore di scala
            int imgw = img.getWidth();
            int imgh = img.getHeight();
            double scale = Math.max(imgw / width, imgh / height);

            // inserisce l'immagine specificando le dimensioni
            builder.insertImage(img, imgw / scale, imgh / scale);
        }
        else
        {
            // altrimenti inserisce immagine senza specificare dimensioni
            builder.insertImage(bytes);
        }
    }
    catch (Exception e)
    {
        // se qualcosa va male, setta immagine a null
        ifma.setImage(null);
    }
}

The method checked wether the image was in a table cell or inside a shape and, if so, scaled the image according to its container’s dimensions.

Now, I have removed such resize, and all works well, however the image are distorted 'cause the new sizes, set from its container. Here is the new code:

public void imageFieldMerging(ImageFieldMergingArgs ifma) throws Exception
{
    try
    {
        double width = ifma.getImageWidth().getValue();
        double height = ifma.getImageHeight().getValue();

        // se le dimensioni non sono giα specificate
        if (width <0 && height <0)
        {
            DocumentBuilder builder = new DocumentBuilder(ifma.getDocument());
            builder.moveToMergeField(ifma.getDocumentFieldName(), false, false);

            // se si trova in una cella di una tabella
            Cell cell = (Cell) builder.getCurrentParagraph().getAncestor(NodeType.CELL);
            if (cell != null)
            {
                CellFormat format = cell.getCellFormat();

                format.setLeftPadding(0.0);
                format.setRightPadding(0.0);
                format.setTopPadding(0.0);
                format.setBottomPadding(0.0);
                format.setWrapText(false);
                format.setFitText(true);

                // uso dimensioni cella
                width = format.getWidth();
                height = cell.getParentRow().getRowFormat().getHeight();
            }

            // se si trova in un textbox
            Shape shape = (Shape) builder.getCurrentParagraph().getAncestor(NodeType.SHAPE);
            if (shape != null && shape.getShapeType() == ShapeType.TEXT_BOX)
            {
                TextBox tb = shape.getTextBox();

                tb.setInternalMarginBottom(0.0);
                tb.setInternalMarginTop(0.0);
                tb.setInternalMarginLeft(0.0);
                tb.setInternalMarginRight(0.0);

                // uso dimensioni textbox
                width = shape.getWidth();
                height = shape.getHeight();
            }
        }

        // immagine come array di byte
        ifma.setImageStream(new ByteArrayInputStream((byte[]) ifma.getFieldValue()));

        // imposto le dimensioni
        ifma.getImageWidth().setValue(width);
        ifma.getImageHeight().setValue(height);
    }
    catch (Exception e)
    {
        // se qualcosa va male, setta immagine a null
        ifma.setImage(null);
    }
}

As a feature request/suggestion, would it be possible for you to handle a case where one needs proportional resizing?

I would suggest something like:

ImageFieldMergingArgs.getImageWidth().setValueProportional();

and

ImageFieldMergingArgs.getImageHeight().setValueProportional();

Those two method should take the values set and the actual image size, and resize accordingly.

Perhaps you are able to implement it in a much optimized way than I am able to!

Hi Matteo,

Thanks for your inquiry. Please use the following code snippet to resize the image with proportional height/width according to table’s cell. You can use the same highlighted code snippet for TextBox shape as well. Hope this helps you. Please let us know if you have any more queries.

Document doc = new Document(MyDir + "resizeImage.Cell.docx");
DocumentBuilder builder = new DocumentBuilder(doc);
Cell cell = (Cell)doc.getChild(NodeType.CELL, 0, true);
CellFormat format = cell.getCellFormat();
format.setLeftPadding(0.0);
format.setRightPadding(0.0);
format.setTopPadding(0.0);
format.setBottomPadding(0.0);
format.setWrapText(false);
format.setFitText(true);
double width = format.getWidth();
double height = cell.getParentRow().getRowFormat().getHeight();
builder.moveTo(cell.getFirstChild());
Shape image = builder.insertImage(MyDir + "Chrysanthemum.jpg");
double freePageWidth = width;
double freePageHeight = height;
// Is one of the sides of this image too big for the page?
ImageSize size = image.getImageData().getImageSize();
boolean exceedsMaxPageSize = size.getWidthPoints() > freePageWidth || size.getHeightPoints() > freePageHeight;

if (exceedsMaxPageSize)
{
    // Calculate the ratio to fit the page size based on which side is longer.
    boolean widthLonger = (size.getWidthPoints() > size.getHeightPoints());
    double ratio = widthLonger ? freePageWidth / size.getWidthPoints() : freePageHeight / size.getHeightPoints();
    // Set the new size.
    image.setWidth(size.getWidthPoints() * ratio);
    image.setHeight(size.getHeightPoints() * ratio);
}
doc.save(MyDir + "Out.docx");