Storing DPI and Paper Size information in a JPEG with Java

前端 未结 4 1449
南笙
南笙 2021-01-13 03:13

I have the following code:

ImageIO.write(originalImage, OUTPUT_TYPE, resultOutput);

This is an invocation of the following javax.imag

相关标签:
4条回答
  • 2021-01-13 03:53

    This question is already partially answered: Write dpi metadata to a jpeg image in Java

    From the image editor GIMP I know that .png has a resolution (DPI) one may save as meta data. There is EXIF data for JPEG, info of the camera in general, including resolution.

    0 讨论(0)
  • 2021-01-13 04:01

    You may consider using Commons Sanselan, instead of ImageIO for this task.

    See http://commons.apache.org/sanselan/whysanselan.html for more info.

    0 讨论(0)
  • 2021-01-13 04:02

    Luckily, the java image I/O API lets you do just that. It also allows for setting the image metadata in a format-independent way. For example to specify the desired DPI, which was what I wanted to do.

    As it turned out, setting the dpi was less straightforward than expected…

    The java image I/O API is plug-in based, for every file format you wish to use an appropriate plug-in must be available. The API provides the necessary abstractions for writing plug-in neutral code and has a repository of available implementations that you can query at run-time. As of J2SE 6, every JRE has to provide plug-ins for PNG, JPG and BMP file formats (plus some others that I haven’t tried out yet).

    The spec for setting image metadata is the standard (plug-in neutral) metadata format specification. Strangely enough, the spec is an XML schema. That’s right, if you want to set the DPI, you’ll have to do it by building a DOM-tree using IIOMetadataNodes and merge it in with the rest! Sigh..

    Beware that plug-ins can differ in their support for the standard metadata :

    • some don’t support changing the standard metadata
    • some don’t support the standard metadata format at all
    • some don’t support merging your DOM-tree with the current metadata and will silently replace it

    Anyway, the relevant tags when you want to set the DPI are HorizontalPixelSize and VerticalPixelSize :

    <!ELEMENT "HorizontalPixelSize" EMPTY>
       <!-- The width of a pixel, in millimeters,
                 as it should be rendered on media -->
       <!ATTLIST "HorizontalPixelSize" "value" #CDATA #REQUIRED>
       <!-- Data type: Float -->
    <!ELEMENT "VerticalPixelSize" EMPTY>
       <!-- The height of a pixel, in millimeters,
                 as it should be rendered on media -->
       <!ATTLIST "VerticalPixelSize" "value" #CDATA #REQUIRED>
       <!-- Data type: Float -->
    

    Note that the spec clearly states that both have to be expressed in millimeters per dot. How to disregard your own spec, Sun style

    Sun has implemented this metadata spec for their PNG and JPG plug-ins and includes it in their current JDK and JRE distributions. The relevant classes are

    com.sun.imageio.plugins.png.PNGImageWriter
    com.sun.imageio.plugins.jpeg.JPEGImageWriter
    

    You can tell from the com.sun package that they’re not part of the J2SE API, but specific to Sun’s implementation.

    Remember how the spec required millimeters per dot? Well, have a look at the following table to see how Sun actually implemented the spec :

    plug-in          unit                 bug report   date reported
    PNGImageWriter   dots per millimeter  bug 5106305  23 sep 2004
    JPEGImageWriter  decimeter per dot    bug 6359243  05 dec 2005
    

    These bugs have been known for a very long time and their fixes are really simple. Unfortunately, they were given a low priority and haven’t even been evaluated at the time of writing (July 2008). Great. Now what?

    Well, because the workaround is so trivial I decided to stick with the image I/O API. If you give these classes what they want, the bitmaps will come out fine. To ensure that your export code also works on platforms that implement the spec correctly, it must check the actual implementation classes that are being used and compensate for the bugs.

    If you find yourself in a similar situation, make sure your workaround code will be able to cope when the bugs are fixed eventually. More on this in the article 'How bugs in the J2SE api can bite you twice'.

    Oh, and if you use instanceof to check for instances of a buggy class that is not guaranteed to exist on all platforms, be sure to catch NoClassDefFoundError ;)

    0 讨论(0)
  • 2021-01-13 04:07

    I found this post for setting DPI on PNG Files. It pointed out that you should use 'metadata.mergeTree' to properly save your metadata.

    With that in mind, here is some working groovy code that takes a BMP file and creates a JPG file at arbitrary DPI:

    import java.awt.image.BufferedImage
    import java.io.File
    import java.util.Hashtable
    import java.util.Map
    import javax.imageio.*
    import javax.imageio.stream.*
    import javax.imageio.metadata.*
    import javax.imageio.plugins.jpeg.*
    import org.w3c.dom.*
    
    File sourceFile = new File("sample.bmp")
    File destinationFile = new File("sample.jpg")
    
    dpi = 100
    
    BufferedImage sourceImage = ImageIO.read(sourceFile)
    ImageWriter imageWriter = ImageIO.getImageWritersBySuffix("jpeg").next();
    ImageOutputStream ios = ImageIO.createImageOutputStream(destinationFile);
    imageWriter.setOutput(ios);
    def jpegParams = imageWriter.getDefaultWriteParam();
    
    IIOMetadata data = imageWriter.getDefaultImageMetadata(new ImageTypeSpecifier(sourceImage), jpegParams);
    Element tree = (Element)data.getAsTree("javax_imageio_jpeg_image_1.0");
    Element jfif = (Element)tree.getElementsByTagName("app0JFIF").item(0);
    jfif.setAttribute("Xdensity", Integer.toString(dpi));
    jfif.setAttribute("Ydensity", Integer.toString(dpi));
    jfif.setAttribute("resUnits", "1"); // density is dots per inch                 
    data.mergeTree("javax_imageio_jpeg_image_1.0",tree)
    
    // Write and clean up
    imageWriter.write(data, new IIOImage(sourceImage, null, data), jpegParams);
    ios.close();
    imageWriter.dispose();
    

    Worked fine for me in that OSX's Preview app and Gimp both reported that the resulting image was 100 DPI. As to Paper Size...I imagine this is directly determined by DPI? I couldn't find any JPEG property that would set that particular value.

    0 讨论(0)
提交回复
热议问题