Как создать изображение в java для печати на принтере с разрешением 300 dpi

Я хочу сделать изображение на Java и распечатать его на принтере с меткой 300 dpi на этикетке размером 150 х 100 мм. Как сделать изображение таким образом, чтобы линия (или какой-либо элемент) была напечатана точно в позиции (10,10) (в миллиметрах), и эта линия заканчивается в позиции (10,50)?

Другими словами: Моя задача заключается не в том, как сделать строку (я использую Graphics2D, bufferedImage), но это то, как можно точно указать, где эта строка должна быть (в миллиметрах) на ярлыке.

Любые идеи?

Ответ 1

API печати Java в основном работает при условии, что все делается с разрешением 72 dpi. Это означает, что вы можете использовать это как основу для преобразования в/из разных измерений...

Это просто означает, что вам нужно и начать измерение ценности и цели...

// The number of CMs per Inch
public static final double CM_PER_INCH = 0.393700787d;
// The number of Inches per CMs
public static final double INCH_PER_CM = 2.545d;
// The number of Inches per mm's
public static final double INCH_PER_MM = 25.45d;

/**
 * Converts the given pixels to cm based on the supplied DPI
 * @param pixels
 * @param dpi
 * @return 
 */
public static double pixelsToCms(double pixels, double dpi) {
    return inchesToCms(pixels / dpi);
}

/**
 * Converts the given cm to pixels based on the supplied DPI
 * @param cms
 * @param dpi
 * @return 
 */
public static double cmsToPixel(double cms, double dpi) {
    return cmToInches(cms) * dpi;
}

/**
 * Converts the given cm to inches
 * @param cms
 * @return 
 */
public static double cmToInches(double cms) {
    return cms * CM_PER_INCH;
}

/**
 * Converts the given inches to cm 
 * @param inch
 * @return 
 */
public static double inchesToCms(double inch) {
    return inch * INCH_PER_CM;
}

Итак, чтобы напечатать изображение размером 10 мм x 10 мм, вам нужно будет преобразовать его в пиксель на дюйм

double point = cmsToPixel(1, 72);

Вероятно, вам также придется подумать о том, чтобы уменьшить изображение, чтобы оно поместилось в область печати. ​​

Для некоторых примеров...

Обновление с результатами тестирования

Итак, я сделал несколько тестов (код для последующего)...

Сначала я настроил PrintRequestAttributeSet, чтобы запросить только службы печати, способные поддерживать 300x300 dpi...

PrintRequestAttributeSet aset = new HashPrintRequestAttributeSet();
aset.add(new PrinterResolution(300, 300, PrinterResolution.DPI));
aset.add(new MediaPrintableArea(0, 0, 150, 100, MediaPrintableArea.MM));

При печати моя Printable была передана с возможностью рисования площадью 425,20 х 283,46 пикселя, что соответствует 15,03 х 10,02 см при 72 dpi (примерно). Так работает Java, базовый API печати всегда работал в предположении 72dpi.

Итак. Если я подготовлю изображение размером 10 x 50 мм @72 DPI, я получаю изображение размером 28,346 x 141,732 пикселя, которое легко поместится в пределах области изображения (425,20 x 283,46).

Однако, если я использую 300 dpi, я получаю изображение размером 118,11 х 590,551 пикселя, что вызовет у нас проблемы, требуя, чтобы мы уменьшили изображение...

Это, на самом деле, может быть желательно, вам нужно будет выполнить некоторое тестирование, чтобы узнать.

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import javax.print.attribute.HashPrintRequestAttributeSet;
import javax.print.attribute.PrintRequestAttributeSet;
import javax.print.attribute.standard.MediaPrintableArea;
import javax.print.attribute.standard.PrinterResolution;

public class TestHiResPrinting {

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                PrintRequestAttributeSet aset = new HashPrintRequestAttributeSet();
                aset.add(new PrinterResolution(300, 300, PrinterResolution.DPI));
                aset.add(new MediaPrintableArea(0, 0, 150, 100, MediaPrintableArea.MM));

                PrinterJob pj = PrinterJob.getPrinterJob();
                pj.setPrintable(new PrintTask());

                if (pj.printDialog(aset)) {
                    try {
                        pj.print(aset);
                    } catch (PrinterException ex) {
                        ex.printStackTrace();
                    }
                }
            }
        });
    }

    // The number of CMs per Inch
    public static final double CM_PER_INCH = 0.393700787d;
    // The number of Inches per CMs
    public static final double INCH_PER_CM = 2.545d;
    // The number of Inches per mm's
    public static final double INCH_PER_MM = 25.45d;

    /**
     * Converts the given pixels to cm based on the supplied DPI
     *
     * @param pixels
     * @param dpi
     * @return
     */
    public static double pixelsToCms(double pixels, double dpi) {
        return inchesToCms(pixels / dpi);
    }

    /**
     * Converts the given cm to pixels based on the supplied DPI
     *
     * @param cms
     * @param dpi
     * @return
     */
    public static double cmsToPixel(double cms, double dpi) {
        return cmToInches(cms) * dpi;
    }

    /**
     * Converts the given cm to inches
     *
     * @param cms
     * @return
     */
    public static double cmToInches(double cms) {
        return cms * CM_PER_INCH;
    }

    /**
     * Converts the given inches to cm's
     *
     * @param inch
     * @return
     */
    public static double inchesToCms(double inch) {
        return inch * INCH_PER_CM;
    }

    public static class PrintTask implements Printable {

        private BufferedImage img;

        public PrintTask() {
            double width = cmsToPixel(1, 72);
            double height = cmsToPixel(5, 72);

            img = new BufferedImage((int) Math.round(width), (int) Math.round(height), BufferedImage.TYPE_INT_ARGB);
            Graphics2D g2d = img.createGraphics();
            g2d.setColor(Color.RED);
            g2d.draw(new Rectangle2D.Double(0, 0, width - 1, height - 1));
            g2d.draw(new Line2D.Double(0, 0, width, height));
            g2d.draw(new Line2D.Double(0, height, width, 0));
            g2d.dispose();
        }

        @Override
        public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
            int result = NO_SUCH_PAGE;
            if (pageIndex < 2) {
                Graphics2D g2d = (Graphics2D) graphics;
                double width = pageFormat.getImageableWidth();
                double height = pageFormat.getImageableHeight();

                System.out.println("Page width = " + width + " = " + pixelsToCms(width, 72));
                System.out.println("Page height = " + height + " = " + pixelsToCms(height, 72));

                g2d.translate((int) pageFormat.getImageableX(),
                                (int) pageFormat.getImageableY());
                double x = cmsToPixel(1, 72);
                double y = cmsToPixel(1, 72);
                System.out.println("Draw At " + x + "x" + y);
                g2d.drawRect(0, 0, (int)width - 1, (int)height - 1);
                g2d.drawImage(img, (int)x, (int)y, null);
                result = PAGE_EXISTS;
            }
            return result;
        }

    }
}

Ответ 2

Ну, есть много вещей, которые нужно учитывать, большинство из которых - базовая математика. Я не знаком с Java2D, поэтому я не могу сказать вам, есть ли какие-либо вспомогательные функции, но вот математика:

150 x 100 миллиметров составляет примерно 6x4 дюймов. При 300 DPI вам требуется разрешение в пикселях 1800x1200.

1 "равно 300 пикселям и составляет 25,4 мм это среднее значение s, что 1 мм равно примерно 12 пикселям (11,8).

Итак, если вы хотите сделать строку, начинающуюся с 10x10 мм, вам нужно умножить это с количеством пикселей в мм, в этом случае 12. Итак, начните рисовать свою линию с разрешением 120x120 пикселей.

Аналогично, если вам нужно закончить линию на 10x50 мм, вам нужно закончить рисование линии на 120x600.

В каждом случае математика отличается в зависимости от того, какое разрешение принт печатает, но для заданного вопроса эти числа должны быть достаточными.

Надеюсь, что это помогло.

Ответ 3

В моих тестах с использованием принтера Brother DPC-135C, запустив отредактированную программу MadProgrammer выше, комментируя следующие строки:

//   aset.add(new PrinterResolution(300, 300, PrinterResolution.DPI));
//   aset.add(new MediaPrintableArea(0, 0, 150, 100, MediaPrintableArea.MM));

...
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
   g2d.drawRect((int) pageFormat.getImageableX(), (int) pageFormat.getImageableY(), (int) pageFormat.getImageableWidth(), (int) pageFormat.getImageableHeight());
...

Я думаю, что g2d.drawRect() выше правильно покрывает весь mediaprintablearea, как определено в javadoc (PageFormat) pageFormat.getImageableWidth() и pageFormat.getImageableHeight(), возвращая значение в 1/72 дюйма. Например, pageFormat.getImageableWidth(), возвращающая 451,2755, означает 451,2755/72 = ~ 6,26 дюйма ширины области печати. Поэтому начиная с pageFormat.getImageableX(), (int) pageFormat.getImageableY() каждый миллиметр составляет около 2,8346 единиц (72 единицы - 1 дюйм, 1 дюйм = 25,4 мм). Итак, чтобы нарисовать изображение (пиксели изображения масштабируются, чтобы соответствовать указанной ширине и высоте):

 g2d.drawImage(img, (int) (pageFormat.getImageableX() + 2.8346 * 10),   // start printing at 10 mm from left of printable area
                    (int) (pageFormat.getImageableY() + 2.8346 * 10),   // start printing at 10 mm from top of printable area
                    (int) 2.8346 * 100,   // width of 100 mm
                    (int) 2.8346 * 150,   // height of 150 mm
                    this);

В диалоговом окне печати вам нужно будет удалить поля. Установка точки на дюйм, как в вышеприведенном коде MadProgrammer. Я думаю, что искажает измерение того, что именно является дюймом в распечатке (в отношении того, что указано в SDK, что pageFormat.getImageableWidth() и pageFormat.getImageableHeight() возвращаются в 1 72 дюйма). В своих тестах я измерил напечатанное изображение с помощью линейки, и оно довольно точное.