Render worksheet page at desired DPI (Java)

I’m trying to render worksheet pages at a certain DPI.

I’m using documents prepared with Excel. I’m also using the worksheet’s default printing area and pagination. I want to find out the page size at certain DPI in pixels, create a BufferedImage for it and let SheetRender.toImage() paint the desired page on it.

The result is always the page painted at 96 DPI, regardless what I specify in ImageOrPrintOptions.setHorizontalResolution() and ImageOrPrintOptions.setVerticalResolution(). Using setDesiredSize() has no effect.

Actual implementation:

  private static BufferedImage getSheetPageImageDpi(Worksheet sheet, int sheetPageNo, int dpi) throws DocumentProcessingException {
    ImageOrPrintOptions imgOptions = new ImageOrPrintOptions();
    imgOptions.setHorizontalResolution(dpi);
    imgOptions.setVerticalResolution(dpi);
    SheetRender sr;
    try {
      sr = new SheetRender(sheet, imgOptions);
    } catch (Exception ex) {
      throw new DocumentProcessingException("Unable to initialize renderer for sheet '" + sheet.getName() + "'", ex);
    }
    imgOptions.setPageIndex(sheetPageNo);
    sr.setPageCount(1);
    float[] ptPageSize = sr.getPageSize(sheetPageNo);
    int pxWidth = ConvUtils.ptsToPixels(ptPageSize[0], dpi);
    int pxHeight = ConvUtils.ptsToPixels(ptPageSize[1], dpi);
    imgOptions.setDesiredSize(pxWidth, pxHeight);
    BufferedImage bi = new BufferedImage(pxWidth, pxHeight, BufferedImage.TYPE_INT_RGB);
    Graphics2D g2d = bi.createGraphics();
    sr.toImage(sheetPageNo, g2d);
    g2d.dispose();
    return bi;
  }

@cdokolasiac,
Thank you for your query. Please share your template Excel file, runnable console application with us having all the references resolved (like ConvUtils.ptsToPixels() and the program output and the expected output. It will help us to observe the problem and provide assistance at the earliest.

Try this code with the xls in the zip:

  private static void getSheetPageImageDpi() throws Exception {
    //initializations
    int dpi = 300;
    String pathname = "EmptyWorkbook.p1.jpg";
    Workbook wb = new Workbook("EmptyWorkbook.xls");
    Worksheet sheet = wb.getWorksheets().get(0);
    int sheetPageNo = 0;
    //imaging options
    ImageOrPrintOptions imgOptions = new ImageOrPrintOptions();
    imgOptions.setHorizontalResolution(dpi);
    imgOptions.setVerticalResolution(dpi);
    //renderer
    SheetRender sr;
    sr = new SheetRender(sheet, imgOptions);
    imgOptions.setPageIndex(sheetPageNo);
    sr.setPageCount(1);
    //get image size at dpi
    float[] ptPageSize = sr.getPageSize(sheetPageNo);
    int pxWidth = ptsToPixels(ptPageSize[0], dpi);
    int pxHeight = ptsToPixels(ptPageSize[1], dpi);
    imgOptions.setDesiredSize(pxWidth, pxHeight);
    //render to Buffered image
    BufferedImage bi = new BufferedImage(pxWidth, pxHeight, BufferedImage.TYPE_INT_RGB);
    Graphics2D g2d = bi.createGraphics();
    sr.toImage(sheetPageNo, g2d);
    g2d.dispose();
    //save image
    FileOutputStream os = new FileOutputStream(pathname);
    ImageIO.write(bi, "jpg", os);
    os.close();
  }

  static int ptsToPixels(double points, int dpi) {
    double inches = points / 72f;
    double pixels = inches * dpi;
    return (int) Math.round(pixels);
  }

The file: EmptyWorkbook.zip (6.3 KB)

@cdokolasiac,
We were able to observe the issue but we need to look into it more. We have logged the issue in our database for investigation and for a fix (if applicable). Once, we will have some news for you, we will update you in this topic.

This issue has been logged as
CELLSJAVA-43061 - Render Excel worksheet to image at desired DPI

@cdokolasiac,
We have analysed the requirement in detail and following are the comments:

First, if you want to render sheet to jpg image, you can use the API: SheetRender.toImage(int pageIndex, String fileName) or SheetRender.toImage(int pageIndex, java.io.OutputStream stream). The two APIs can set horizontal and vertical resolution in jpg image property to the desired dpi(e.g. 300). Also, it seems that you are using SheetRender.setPageCount(int value) incorrectly, you should use ImageOrPrintOptions.setPageCount(int value) . So the code will be:

//initializations
int dpi = 300;
String pathname = "EmptyWorkbook.p1.jpg";
Workbook wb = new Workbook("EmptyWorkbook.xls");
Worksheet sheet = wb.getWorksheets().get(0);
int sheetPageNo = 0;
//imaging options
ImageOrPrintOptions imgOptions = new ImageOrPrintOptions();
imgOptions.setHorizontalResolution(dpi);
imgOptions.setVerticalResolution(dpi);
imgOptions.setPageIndex(sheetPageNo);
imgOptions.setPageCount(1);
imgOptions.setImageType(ImageType.JPEG);

//renderer
SheetRender sr;
sr = new SheetRender(sheet, imgOptions);

sr.toImage(0, pathname);
//sr.toImage(0, new FileOutputStream(pathname));

Second, if you still want to render sheet to Graphics2D , the dpi of converting page size from point to pixel should not be the image’s resolution. You should change this code:

int pxWidth = ptsToPixels(ptPageSize[0], dpi);
int pxHeight = ptsToPixels(ptPageSize[1], dpi);

to:

int pxWidth = ptsToPixels(ptPageSize[0], 96);
int pxHeight = ptsToPixels(ptPageSize[1], 96);

Also, it is required to set the horizontal and vertical resolution in jpg image property to the desired dpi in this case while you save BufferedImage to file.

I made the fix for setPageCount. Apparently the one in SheetRender is obsolete because it’s missing from the javadocs.

About the issue now:

The purpose of the original method (see original post) is to produce a BufferedImage with the rendition of the page at the desired DPI. This BufferedImage will be further processed and may be saved in some format (not necessarily JPEG). I don’t see how creating a BufferedImage for a 96 dpi rendition solves the problem. The result will always be an image size for rendering the page at 96 dpi. Also, setting the DPI in the file metadata is just that an indication of physical size; it is not involved in the process of rendering the page.

The solution is actually simpler than I thought (based on the APIs in Aspose.Words and Aspose.Slides which support scaling with Document.renderToSize() and ISlide.renderToGraphics() respectively). I only had to adjust the scaling by adding a transformation in the Graphics2D object. This is what should logically happen when SheetRender.toImage() is called (i.e. observe the resolution settings in the ImageOrPrintOptions object).

Note: I also had to remove the setPageCount because it caused a change in the handling of SheetRender.toImage(int, Graphics2D) (the page number is now relative to the one set by setPageCount).

This is the final code:

  private static PageRenditionBundle getSheetPageImageDpi(Worksheet sheet, int sheetPageNo, int dpi) throws DocumentProcessingException {
    ImageOrPrintOptions imgOptions = new ImageOrPrintOptions();
    SheetRender sr;
    try {
      sr = new SheetRender(sheet, imgOptions);
    } catch (Exception ex) {
      throw new DocumentProcessingException("Unable to initialize renderer for sheet '" + sheet.getName() + "'", ex);
    }
    float[] ptPageSize = sr.getPageSize(sheetPageNo);
    int pxWidth = ConvUtils.ptsToPixels(ptPageSize[0], dpi);
    int pxHeight = ConvUtils.ptsToPixels(ptPageSize[1], dpi);
    BufferedImage bi = new BufferedImage(pxWidth, pxHeight, BufferedImage.TYPE_INT_RGB);
    Graphics2D g2d = bi.createGraphics();
    float scale = dpi / 96f; //re-scale from 96dpi
    g2d.setTransform(AffineTransform.getScaleInstance(scale, scale));
    sr.toImage(sheetPageNo, g2d);
    g2d.dispose();
    return new PageRenditionBundle(bi);
  }

With this note, you should consider the issue resolved.

@cdokolasiac,
Thank you for sharing the information and good to know that your issue is sorted out. Feel free to contact us at any time if you need further help or have some other issue or queries, we will be happy to assist you soon.

@cdokolasiac,

Actually, we do the scale transformation according to the resolution settings in the ImageOrPrintOptions object.

Please try the following code, changing from the your original post. It will get the right output.

private static BufferedImage getSheetPageImageDpi(Worksheet sheet, int sheetPageNo, int dpi) throws DocumentProcessingException {
    ImageOrPrintOptions imgOptions = new ImageOrPrintOptions();
    imgOptions.setHorizontalResolution(dpi);
    imgOptions.setVerticalResolution(dpi);
    SheetRender sr;
    try {
      sr = new SheetRender(sheet, imgOptions);
    } catch (Exception ex) {
      throw new DocumentProcessingException("Unable to initialize renderer for sheet '" + sheet.getName() + "'", ex);
    }

    float[] ptPageSize = sr.getPageSize(sheetPageNo);
    int pxWidth = ConvUtils.ptsToPixels(ptPageSize[0], 96);
    int pxHeight = ConvUtils.ptsToPixels(ptPageSize[1], 96);

    BufferedImage bi = new BufferedImage(pxWidth, pxHeight, BufferedImage.TYPE_INT_RGB);
    Graphics2D g2d = bi.createGraphics();
    sr.toImage(sheetPageNo, g2d);
    g2d.dispose();
    return bi;
  }

the key change is changing the code:

int pxWidth = ptsToPixels(ptPageSize[0], dpi);
int pxHeight = ptsToPixels(ptPageSize[1], dpi);

to:

int pxWidth = ptsToPixels(ptPageSize[0], 96);
int pxHeight = ptsToPixels(ptPageSize[1], 96);

The page size getting from the API SheetRender.getPage(int pageIndex) is in point unit. It is calculated from pixel unit to point unit in 96 dpi.

This won’t work. Bitmap is the wrong size (always 96 dpi). Check with the devs.

@cdokolasiac,

We have recorded your comments and will share our feedback after analysis.

@cdokolasiac,
The paper size sheet “Sheet1” in shared “EmptyWorkbook.xls” source file is Paper A4. Paper A4 is 21cm X 29.7cm. It is [21/2.54300, 29.7/2.54300] in pixel in 300 dpi.

Please use the code of “IssueTest.java” (see attachment), the dimension of the output image(see attachment “EmptyWorlbook.jpg”) matches Paper A4 dimension([21/2.54300, 29.7/2.54300]) in 300 dpi. DPI.zip (31.5 KB)