danfickle/openhtmltopdf

max-width / max-height not redering correctly in pdf when height / width set in tag

swarl opened this issue · 2 comments

swarl commented

Version: 1.0.1

When in a html image tag the properties height and width are specified and css styles max-width (and max-height) is added, then the image is not rendered correctly in the pdf.

Reproduce with:

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.LinkedHashMap;

import org.apache.commons.io.FileUtils;
import org.junit.Test;

import com.openhtmltopdf.pdfboxout.PdfRendererBuilder;
[...]
   @Test
   public void shouldAnswerWithTrue() throws URISyntaxException, IOException {

      String htmlContent = "<html>\n"
         + "<head>\n"
         + "    <style>\n"
         + "        img {\n"
         + "            max-height: 885px;\n"
         + "            max-width: 690px;\n"
         + "        }\n"
         + "    </style>\n"
         + "</head>\n"
         + "<body>\n"
         + "<div class=\"content\">\n"
         + "    <div>"
         + "        <p><span><span>Inline image:</span></span></p>\n"
         + "        <img height=\"310\" src=\"file:_2019-11-22_11-34-28.png\" width=\"700\"/>\n"
         //         + "        <img height=\"3301\" src=\"file:_2019-11-22_11-34-28.png\" width=\"1462\"/>\n"
         + "    </div>\n"
         + "</div>\n"
         + "</body>\n"
         + "</html>";

      try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
         PdfRendererBuilder builder = new PdfRendererBuilder();
         builder.usePdfAConformance(PdfRendererBuilder.PdfAConformance.PDFA_2_A);

         builder
            .withHtmlContent(htmlContent, "./")
            .useFastMode()
            .toStream(outputStream).run();
         FileUtils.writeByteArrayToFile(new File("test.pdf"), outputStream.toByteArray());
      } catch (Exception e) {
         throw new IllegalStateException(e.getMessage(), e);
      }
   }

_2019-11-22_11-34-28
test.pdf

swarl commented

To repoduce: https://github.com/swarl/html2pdf.git
Problem seem to be in BlockBox.sizeReplacedElement :

        boolean haveExactDims = cssWidth >= 0 && cssHeight >= 0;
        
        int intrinsicWidth = re.getIntrinsicWidth();
        int intrinsicHeight = re.getIntrinsicHeight();
        
        cssWidth = !getStyle().isMaxWidthNone() && 
                (intrinsicWidth > getCSSMaxWidth(c) || cssWidth > getCSSMaxWidth(c)) ? 
                          getCSSMaxWidth(c) : cssWidth;
        [...]
        
        cssHeight = !getStyle().isMaxHeightNone() &&
                (intrinsicHeight > getCSSMaxHeight(c) || cssHeight > getCSSMaxHeight(c)) ?
                          getCSSMaxHeight(c) : cssHeight;
        [...]

        int nw;
        int nh;
        
        if (cssWidth > 0 && cssHeight > 0) {
            if (haveExactDims) {
                // We only warp the aspect ratio if we have explicit width and height values.
                nw = cssWidth;
                nh = cssHeight;
            } [...]
       }

Here max-width and max-height are applied without any conversion. So this only would work if both values are in the same aspect ratio as the picture...

swarl commented

This would need to be something like this:

        boolean maxWidthApplied = false;
        if (!getStyle().isMaxWidthNone() &&
                (intrinsicWidth > getCSSMaxWidth(c) || cssWidth > getCSSMaxWidth(c))) {
            maxWidthApplied = true;
            cssWidth = getCSSMaxWidth(c);
            cssHeight = (int) ((double) intrinsicHeight / ((double) intrinsicWidth / (double) getCSSMaxWidth(c)));
        }

        cssWidth = cssWidth >= 0 && getCSSMinWidth(c) > 0 && cssWidth < getCSSMinWidth(c) ?
                getCSSMinWidth(c) : cssWidth;

        if (!getStyle().isMaxHeightNone() &&
                (intrinsicHeight > getCSSMaxHeight(c) && !maxWidthApplied || cssHeight > getCSSMaxHeight(c))) {
            cssWidth = (int) ((double) cssWidth / ((double) cssHeight / (double) getCSSMaxHeight(c)));
            cssHeight = getCSSMaxHeight(c);
        }

        cssHeight = cssHeight >= 0 && getCSSMinHeight(c) > 0 && cssHeight < getCSSMinHeight(c) ?
                getCSSMinHeight(c) : cssHeight;