Хранение информации о DPI и размере бумаги в формате JPEG с Java - PullRequest
3 голосов
/ 23 декабря 2011

У меня есть следующий код:

ImageIO.write(originalImage, OUTPUT_TYPE, resultOutput);

Это вызов следующего javax.imageio.ImageIO метода:

public static boolean write(RenderedImage im,
                            String formatName,
                            File output)
                     throws IOException

Это превращает исходное изображение BMP в вывод JGP. Можно ли также хранить информацию о DPI и размере бумаги в формате JPEG для облегчения операций печати?

Ответы [ 4 ]

13 голосов
/ 05 января 2012

К счастью, API ввода / вывода изображений Java позволяет вам сделать это.Это также позволяет устанавливать метаданные изображения независимо от формата.Например, чтобы указать желаемый DPI, что я и хотел сделать.

Как оказалось, настройка dpi была менее простой, чем ожидалось…

Java-изображение I /O API - это плагин , для каждого формата файла, который вы хотите использовать, соответствующий плагин должен быть доступен.API предоставляет необходимые абстракции для написания нейтрального кода плагина и имеет хранилище доступных реализаций, которые можно запрашивать во время выполнения.Начиная с J2SE 6, каждый JRE должен предоставлять подключаемые модули для форматов файлов PNG, JPG и BMP (плюс некоторые другие, которые я еще не опробовал).

Спецификацией для установки метаданных изображения является стандарт (плагин нейтральный) спецификация формата метаданных.Как ни странно, спецификация представляет собой XML-схему.Правильно, если вы хотите установить DPI, вам нужно сделать это путем построения DOM -дерева с использованием IIOMetadataNodes и , объединить его состальное!Вздох ..

Обратите внимание, что плагины могут отличаться по своей поддержке стандартных метаданных:

В любом случае, соответствующие теги, когда вы хотите установить DPI: HorizontalPixelSize и 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 -->

Обратите внимание, что в спецификации четко указано, что оба должны быть выражены вмиллиметры на точку.Как игнорировать вашу собственную спецификацию, Sun style

Sun реализовала эту спецификацию метаданных для своих плагинов PNG и JPG и включила ее в свои текущие дистрибутивы JDK и JRE.Соответствующие классы:

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

. Из пакета com.sun можно сказать, что они не являются частью J2SE API, но специфичны для реализации Sun.

Помните, как требовалась спецификациямиллиметры на точку?Итак, взгляните на следующую таблицу, чтобы увидеть, как Sun на самом деле реализовала спецификацию:

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

Эти ошибки были известны очень давно, и их исправления действительно просты.К сожалению, им был присвоен низкий приоритет, и они даже не оценивались на момент написания (июль 2008 г.).Отлично.И что теперь?

Ну, так как обходной путь настолько тривиален, я решил придерживаться API ввода / вывода изображения.Если вы дадите этим классам то, что они хотят, растровые изображения получатся хорошими.Чтобы гарантировать, что ваш код экспорта также работает на платформах, которые правильно реализуют спецификацию, он должен проверить фактические используемые классы реализации и компенсировать ошибки.

Если вы оказались в подобной ситуации, убедитесь, чтоВаш обходной код сможет справиться, когда ошибки будут исправлены в конце концов.Подробнее об этом в статье ' Как ошибки в API J2SE могут кусать вас дважды '.

О, и если вы используете instanceof для проверки на наличие экземпляров класса с ошибками, что не гарантируетсячтобы существовать на всех платформах, обязательно перехватывайте NoClassDefFoundError;)

4 голосов
/ 31 декабря 2011

На этот вопрос уже частично дан ответ: Запись метаданных dpi в изображение jpeg на Java

Из редактора изображений GIMP я знаю, что .png имеет разрешение(DPI) можно сохранить как метаданные.Для JPEG имеются данные EXIF ​​, информация о камере в целом, включая разрешение.

3 голосов
/ 06 января 2012

Я нашел этот пост для установки DPI для файлов PNG .Он указал, что вы должны использовать metadata.mergeTree для правильного сохранения ваших метаданных.

Имея это в виду, вот некоторый рабочий код Groovy, который принимает файл BMP и создает файл JPG с произвольным DPI:1005 *

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();

У меня в приложении Preview для OSX все работало нормально, и Gimp оба сообщили, что результирующее изображение имеет разрешение 100 DPI.Что касается размера бумаги ... Я полагаю, это напрямую определяется DPI?Я не смог найти ни одного свойства JPEG, которое бы устанавливало это конкретное значение.

3 голосов
/ 31 декабря 2011

Вы можете использовать Commons Sanselan вместо ImageIO для этой задачи.

Подробнее см. http://commons.apache.org/sanselan/whysanselan.html.

...