Давайте начнем с очевидного ...
- A A4 лист бумаги 21.0cm х 29,7.
- В 300dpi, что делает его 2480.315x3507.874 пикселей
- В 72dpi, что делает его 595.2756x841.8898 пикселей
Почему это так важно? Java отображает экран с разрешением 72dpi, но может печатать с разрешением 300 dpi (сверху и снизу, но это хорошая цифра). Это означает, что, грубо говоря, вы должны масштабировать изображение на экране UP в 4 раза. Увеличение масштабирования никогда не бывает приятным, как показано на рисунке here
Лучшее решение будет работать с DPI принтера и масштабировать изображение до экрана.
Для удобства, вы можете использовать что-то вроде ...
public static final float CM_PER_INCH = 0.393700787f;
public static float cmsToPixel(float cms, float dpi) {
return cmToInches(cms) * dpi;
}
public static float cmToInches(float cms) {
return cms * CM_PER_INCH;
}
Для преобразования Мюррея в пиксели в заданном DPI.
Теперь к интересной части. Вы можете «использовать» компоненты Swing для рендеринга основного макета, это может быть проще, но вам придется масштабировать графику вниз, поскольку API-интерфейс основного принтера предполагает DPI 72 (не спрашивайте). Теперь уменьшение масштаба обычно приводит к лучшему результату, но есть и другое решение.
Вместо этого вы можете использовать графический 2D API и генерировать вывод сами ...
public static class Ticket {
public enum TextAlignment {
LEFT,
RIGHT,
CENTRE
}
protected static final int STUB_NUMBER_Y_POS = 12;
private Font plainFont;
private Font boldFont;
private Stroke dashedStroke;
public void paint(Graphics2D g2d, double pageWidth, double pageHeight, int stubNumber) {
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
paintLeftStub((Graphics2D) g2d.create(), pageWidth, pageHeight, stubNumber);
paintBody((Graphics2D) g2d.create(), pageWidth, pageHeight, stubNumber);
paintRightStub((Graphics2D) g2d.create(), pageWidth, pageHeight, stubNumber);
paintEndStub((Graphics2D) g2d.create(), pageWidth, pageHeight, stubNumber);
g2d = (Graphics2D) g2d.create();
g2d.setColor(Color.GRAY);
g2d.setStroke(getDashedStroke());
g2d.draw(new Line2D.Double(0, pageHeight - 1, pageWidth, pageHeight - 1));
g2d.dispose();
}
protected void paintLeftStub(Graphics2D graphics, double pageWidth, double pageHeight, int stubNumber) {
graphics.setColor(Color.BLACK);
double stubWidth = pageWidth/4;
Graphics2D g2d = (Graphics2D) graphics.create();
Font font = getBoldFont().deriveFont(18f);
g2d.setFont(font);
FontMetrics fm = g2d.getFontMetrics();
// Did mention I hate doing inline transformations :P
g2d.rotate(Math.toRadians(-90), stubWidth/2, pageHeight/2);
g2d.translate(0, -((stubWidth - pageHeight)/2));
String lines[] = {"MORATUFIESTA", "", "Sat. 3 Auguest 2015", "Adult - $3"};
double x = 2;
double y = 0;
double maxWidth = 0;
for (String text : lines) {
x = calculateHorizontalCenterPositionFor(text, fm, stubWidth);
maxWidth = Math.max(maxWidth, fm.stringWidth(text));
g2d.drawString(text, (int) Math.round(x), (int) Math.round(y + fm.getAscent()));
y += fm.getHeight();
}
double blockWidth = y;
// Easier then trying to undo the transformation...
g2d.dispose();
g2d = (Graphics2D) graphics.create();
String text = "Low";
font = getPlainFont().deriveFont(6f);
g2d.setFont(font);
fm = g2d.getFontMetrics();
double xPos = calculateHorizontalCenterPositionFor(text, fm, blockWidth) + 2;
double yPos = (pageHeight - maxWidth)/2;
g2d.drawString(text, (int) Math.round(xPos), (int) Math.round(yPos - fm.getAscent()));
g2d.setStroke(getDashedStroke());
g2d.draw(new Line2D.Double(stubWidth, 0, stubWidth, pageHeight));
g2d.dispose();
g2d = (Graphics2D) graphics.create();
drawStubNumber(g2d, stubWidth - 8 - fm.getHeight() - fm.getAscent(), STUB_NUMBER_Y_POS, stubNumber);
g2d.dispose();
}
protected Stroke getDashedStroke() {
if (dashedStroke == null) {
float dash[] = {10.0f};
dashedStroke = new BasicStroke(1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 10f, dash, 0.0f);
}
return dashedStroke;
}
public Font getPlainFont() {
if (plainFont == null) {
plainFont = UIManager.getFont("Label.font");
}
return plainFont;
}
public Font getBoldFont() {
if (boldFont == null) {
boldFont = getPlainFont();
boldFont = boldFont.deriveFont(Font.BOLD);
}
return boldFont;
}
protected double calculateHorizontalCenterPositionFor(String text, FontMetrics fm, double width) {
return (width - fm.stringWidth(text))/2d;
}
protected void paintBody(Graphics2D graphics, double pageWidth, double pageHeight, int stubNumber) {
int padding = 8;
double xOffset = pageWidth/4d;
graphics.setColor(Color.BLACK);
double bodyWidth = (pageWidth/2d);
Graphics2D g2d = (Graphics2D) graphics.create();
g2d.translate(xOffset, 0);
g2d.setFont(getPlainFont().deriveFont(12f));
FontMetrics fm = g2d.getFontMetrics();
String text = "Moratu Fiesta";
double xPos = bodyWidth - fm.stringWidth(text) - padding;
double yPos = padding;
g2d.drawString(text, (int) Math.round(xPos), (int) Math.round(yPos + fm.getAscent()));
g2d.dispose();
Font plainFont = getPlainFont().deriveFont(9.5f);
TextLine[] addressLines01 = new TextLine[]{
new TextLine("61 Railway Pde North", plainFont),
new TextLine("Glen Waverley", plainFont),
new TextLine("03 9836 8673", plainFont)
};
TextLine[] addressLines02 = new TextLine[]{
new TextLine("1120, Glen Huntly", plainFont),
new TextLine("Glen Huntly", plainFont),
new TextLine("03 9571 5544", plainFont)
};
TextLine[] sponsorLines = new TextLine[]{
new TextLine("Proudly supported by", plainFont),
new TextLine("Quality Groceries", plainFont.deriveFont(Font.BOLD)),
new TextLine("Visit for all your grocery needs", plainFont)
};
Area area = new Area();
addTo(g2d, area, addressLines01);
addTo(g2d, area, addressLines02);
addTo(g2d, area, sponsorLines);
int height = area.getBounds().height;
double bottomBlockYPos = pageHeight - height - padding;
g2d = (Graphics2D) graphics.create();
g2d.translate(xOffset, 0);
drawTextLines(g2d, padding, bottomBlockYPos, bodyWidth - (padding * 2), TextAlignment.LEFT, addressLines01);
drawTextLines(g2d, padding, bottomBlockYPos, bodyWidth - (padding * 2), TextAlignment.CENTRE, sponsorLines);
drawTextLines(g2d, padding, bottomBlockYPos, bodyWidth - (padding * 2), TextAlignment.RIGHT, addressLines02);
g2d.dispose();
plainFont = getPlainFont().deriveFont(10f);
TextLine[] textLines = new TextLine[]{
new TextLine("On Saturday, August 3, 2013 from 6.30pm till midnight", plainFont.deriveFont(Font.BOLD)),
new TextLine("At 21, Sacred Heart Parish Hall, Johnson Street, Oakleigh", plainFont)
};
int blockHeight = getSizeFor(g2d, textLines).height;
double mainBlockYPos = (pageHeight - blockHeight)/2;
g2d = (Graphics2D) graphics.create();
g2d.translate(xOffset, 0);
drawTextLines(g2d, 0, mainBlockYPos, bodyWidth, TextAlignment.CENTRE, textLines);
g2d.dispose();
g2d = (Graphics2D) graphics.create();
g2d.translate(xOffset, 0);
Font boldFont = getBoldFont().deriveFont(9f);
double upperYPos = (mainBlockYPos + blockHeight);
double middleBlockYPos = upperYPos + ((bottomBlockYPos - upperYPos)/2) - (g2d.getFontMetrics(boldFont).getHeight()/2);
drawTextLines(g2d, padding, middleBlockYPos, bodyWidth - (padding * 2), TextAlignment.LEFT, new TextLine("Melway Ref, 69 FB", boldFont));
drawTextLines(g2d, padding, middleBlockYPos, bodyWidth - (padding * 2), TextAlignment.CENTRE, new TextLine("Music by REDEMPTION", boldFont));
drawTextLines(g2d, padding, middleBlockYPos, bodyWidth - (padding * 2), TextAlignment.RIGHT, new TextLine("Donations $30", boldFont));
g2d.dispose();
g2d = (Graphics2D) graphics.create();
g2d.translate(xOffset, 0);
g2d.setStroke(getDashedStroke());
g2d.draw(new Line2D.Double(bodyWidth, 0, bodyWidth, pageHeight));
g2d.dispose();
g2d = (Graphics2D) graphics.create();
drawStubNumber(g2d, padding + xOffset, STUB_NUMBER_Y_POS, stubNumber);
g2d.dispose();
}
protected void drawStubNumber(Graphics2D g2d, double x, double y, int stubNumber) {
Font font = getBoldFont().deriveFont(18f);
g2d.setFont(font);
FontMetrics fm = g2d.getFontMetrics();
String text = Integer.toString(stubNumber);
g2d.translate(x, y);
g2d.rotate(Math.toRadians(-90), fm.stringWidth(text)/2, fm.getHeight()/2);
g2d.drawString(text, 0, fm.getAscent());
g2d.dispose();
}
protected void paintRightStub(Graphics2D graphics, double pageWidth, double pageHeight, int stubNumber) {
graphics.setColor(Color.BLACK);
int padding = 8;
double xOffset = (pageWidth/4d) * 3;
double stubWidth = (pageWidth/4d)/2;
Graphics2D g2d = (Graphics2D) graphics.create();
g2d.translate(xOffset, 0);
g2d.setStroke(getDashedStroke());
g2d.draw(new Line2D.Double(stubWidth, 0, stubWidth, pageHeight));
g2d.dispose();
g2d = (Graphics2D) graphics.create();
drawStubNumber(g2d, padding + xOffset, STUB_NUMBER_Y_POS, stubNumber);
g2d.dispose();
g2d = (Graphics2D) graphics.create();
String text = "Cafe Little Hut";
Font font = getPlainFont().deriveFont(23f);
g2d.setFont(font);
FontMetrics fm = g2d.getFontMetrics();
double x = xOffset + ((stubWidth - fm.stringWidth(text))/2d);
double y = ((pageHeight - fm.getHeight())/2d);
g2d.translate(x, y);
g2d.rotate(Math.toRadians(-90), fm.stringWidth(text)/2, fm.getHeight()/2);
g2d.drawString(text, 0, fm.getAscent());
g2d.dispose();
}
protected void paintEndStub(Graphics2D graphics, double pageWidth, double pageHeight, int stubNumber) {
graphics.setColor(Color.BLACK);
int padding = 8;
double stubWidth = (pageWidth/4d)/2;
double xOffset = ((pageWidth/4d) * 3 + stubWidth);
Graphics2D g2d = (Graphics2D) graphics.create();
g2d.translate(xOffset, 0);
g2d.setStroke(getDashedStroke());
g2d.draw(new Line2D.Double(stubWidth, 0, stubWidth, pageHeight));
g2d.dispose();
g2d = (Graphics2D) graphics.create();
drawStubNumber(g2d, padding + xOffset, STUB_NUMBER_Y_POS, stubNumber);
g2d.dispose();
g2d = (Graphics2D) graphics.create();
String text = "Entrance";
Font font = getBoldFont().deriveFont(32f);
g2d.setFont(font);
FontMetrics fm = g2d.getFontMetrics();
double x = xOffset + ((stubWidth - fm.stringWidth(text))/2);
double y = ((pageHeight - fm.getHeight())/2);
g2d.translate(x, y);
g2d.rotate(Math.toRadians(-90), fm.stringWidth(text)/2, fm.getHeight()/2);
g2d.drawString(text, 0, fm.getAscent());
g2d.dispose();
}
protected Dimension drawTextLines(Graphics2D g2d, double xPos, double yPos, double width, TextAlignment textAlignment, TextLine... textLines) {
Area area = new Area();
for (TextLine textLine : textLines) {
g2d.translate(xPos, yPos);
Dimension size = textLine.getBounds(g2d);
textLine.paint(g2d, width, textAlignment);
area.add(new Area(new Rectangle2D.Double(xPos, yPos, size.width, size.height)));
g2d.translate(-xPos, -yPos);
yPos += size.height;
}
return area.getBounds().getSize();
}
protected void addTo(Graphics2D g2d, Area area, TextLine... textLines) {
area.add(new Area(new Rectangle(getSizeFor(g2d, textLines))));
}
protected Dimension getSizeFor(Graphics2D g2d, TextLine... textLines) {
int yPos = 0;
int width = 0;
for (TextLine textLine : textLines) {
Dimension size = textLine.getBounds(g2d);
yPos += size.height;
width = Math.max(size.width, width);
}
return new Dimension(width, yPos);
}
protected class TextLine {
private String text;
private Font font;
public TextLine(String text, Font font) {
this.text = text;
this.font = font;
}
public String getText() {
return text;
}
public Font getFont() {
return font;
}
public Dimension getBounds(Graphics2D g2d) {
FontMetrics fm = g2d.getFontMetrics(getFont());
return new Dimension(fm.stringWidth(text), fm.getHeight());
}
public void paint(Graphics2D g2d, double width, TextAlignment textAlignment) {
Dimension bounds = getBounds(g2d);
FontMetrics fm = g2d.getFontMetrics(getFont());
g2d.setFont(font);
double x = 0;
switch (textAlignment) {
case CENTRE:
x = (width - bounds.width)/2;
break;
case RIGHT:
x = width - bounds.width;
break;
}
g2d.drawString(getText(), (int) Math.round(x), fm.getAscent());
}
}
}
Почему ты сделал это? Это проще, чтобы обернуть вокруг Printable
и печати на принтере, и вы также можете «краска» к компоненту (или изображения)
Например ...
public class TicketPrintable implements Printable {
private Ticket ticket;
public TicketPrintable(Ticket ticket) {
this.ticket = ticket;
}
@Override
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
int result = NO_SUCH_PAGE;
if (pageIndex == 0) {
Graphics2D g2d = (Graphics2D) graphics;
double width = pageFormat.getImageableWidth();
double height = pageFormat.getImageableHeight();
g2d.translate((int) pageFormat.getImageableX(),
(int) pageFormat.getImageableY());
double ticketHeight = height/4d;
for (int index = 0; index < 4; index++) {
ticket.paint(g2d, width, ticketHeight, index + 1);
g2d.translate(0, ticketHeight);
}
result = PAGE_EXISTS;
}
return result;
}
}
И напечатать его, вы будете использовать что-то вроде ...
PrintRequestAttributeSet aset = new HashPrintRequestAttributeSet();
aset.add(MediaSizeName.ISO_A4);
aset.add(new PrinterResolution(300, 300, PrinterResolution.DPI));
aset.add(new MediaPrintableArea(0, 0, 210, 297, MediaPrintableArea.MM));
PrinterJob pj = PrinterJob.getPrinterJob();
pj.setPrintable(new TicketPrintable(new Ticket()));
if (pj.printDialog(aset)) {
try {
pj.print(aset);
} catch (PrinterException ex) {
ex.printStackTrace();
}
}
На экране это выглядит как ...
На бумаге это выглядит (уменьшено для SO)
Теперь, говорит, что, я бы настоятельно рекомендуем вам изучить JasperReports, что делает все это намного проще, ...
Обновлено
Итак, я сидел в пробке и думал про себя: мне интересно, могу ли я использовать масштабирование, чтобы сделать изображение Ticket
, поэтому я подумал, что попробую ...
Главное изображение 72dpi, нижнее изображение масштабируется до 300dpi
double pageWidth = cmsToPixel(21.0f, 300f);
double pageHeight = cmsToPixel(29.7f, 300f);
double imageWidth = cmsToPixel(21.0f, 72f);
double imageHeight = cmsToPixel(29.7f, 72f);
double scaleFactor = ImageUtilities.getScaleFactorToFit(
new Dimension((int) Math.round(imageWidth), (int) Math.round(imageHeight)),
new Dimension((int) Math.round(pageWidth), (int) Math.round(pageHeight)));
int width = (int) Math.round(pageWidth);
int height = (int) Math.round(pageHeight);
BufferedImage img = new BufferedImage(
width,
height,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
g2d.setColor(Color.WHITE);
g2d.fill(new Rectangle2D.Double(0, 0, img.getWidth(), img.getHeight()));
g2d.scale(scaleFactor, scaleFactor);
Ticket ticket = new Ticket();
ticket.paint(g2d, img.getWidth()/scaleFactor, (img.getHeight()/scaleFactor)/4, 1);
g2d.dispose();
try {
ImageIO.write(img, "png", new File("Ticket.png"));
} catch (IOException ex) {
ex.printStackTrace();
}
алгоритмов масштабный коэффициент
public static double getScaleFactorToFit(Dimension original, Dimension toFit) {
double dScale = 1d;
if (original != null && toFit != null) {
double dScaleWidth = getScaleFactor(original.width, toFit.width);
double dScaleHeight = getScaleFactor(original.height, toFit.height);
dScale = Math.min(dScaleHeight, dScaleWidth);
}
return dScale;
}
public static double getScaleFactor(int iMasterSize, int iTargetSize) {
double dScale = 1;
if (iMasterSize > iTargetSize) {
dScale = (double) iTargetSize/(double) iMasterSize;
} else {
dScale = (double) iTargetSize/(double) iMasterSize;
}
return dScale;
}
Примечание: Это действительно будет работать для текста и примитивной графики, вы помещаете изображение в это, и оно не будет работать, изображение будет увеличено и будет выглядеть дерьмовым, как показано в связанном ответе от ранее. В этом случае вам нужно будет создать форму для рендеринга с разрешением 300 точек на дюйм и масштабировать изображение до 72dpi.
Типичный экран шляпы appx. 72 dpi. Типичный принтер может иметь 720 dpi. Поэтому, чтобы использовать разрешение принтера, вам придется значительно увеличить размер изображения (возможно: как можно выше, не вызывая OutOfMemoryError ...) – Marco13
Я сделал это, выполнив следующие действия: size.width * = 5 ; size.height * = 5; Исходное изображение имело разрешение: 594 X 840 и размер 38.7KB, , а новое изображение имеет разрешение 2970 X 4200 и размер 3,67 МБ, но такое же низкое качество .. – Ghita
Вы захватываете изображение @ 72dpi ... масштабирование будет только ухудшать качество. – MadProgrammer