private static class PrinterFonts extends HashMap<FontData, Font> {
private Printer printer;
/**
* Konstruktor
*
* @param printer –
* Drucker
*/
PrinterFonts(Printer printer) {
super();
this.printer = printer;
}
/**
* Liefert einen Druckerfont
*
* @param data –
* Fontdaten, für welche ein Font benötigt wird
* @return – Font
*/
Font getFont(FontData data) {
Font font = get(data);
if (font == null) {
font = new Font(printer, data);
put(data, font);
}
return font;
}
/**
* Entsorgt alle Fonts
*/
void dispose() {
for (Font font : values())
font.dispose();
}
}
@SuppressWarnings("serial")
/**
* Repositorium für Farben
*/
private static class PrinterColors extends HashMap<RGB, Color> {
private Printer printer;
/**
* Konstruktor
*
* @param printer -
* Drucker
*/
PrinterColors(Printer printer) {
super();
this.printer = printer;
}
/**
* Liefert eine Druckerfarbe
*
* @param rgb -
* RGB-Wert
* @return - Druckerfarbe
*/
Color getColor(RGB rgb) {
Color color = get(rgb);
if (color == null) {
color = new Color(printer, rgb);
put(rgb, color);
}
return color;
}
/**
* Entsorgt alle Farben
*/
void dispose() {
for (Color color : values())
color.dispose();
}
}
private PrinterFonts printerFonts;
private PrinterColors printerColors;
/**
* Druckt ein Control-Element
*
* @param control –
* das zu druckende Element
* @param printer –
* der Drucker
* @param marginX –
* Abstand von der linken Kante in Zoll
* @param marginY –
* Abstand von der Oberkante in Zoll
* @param borders –
* true, wenn Umrahmungen gewünscht sind
* @param fill –
* true, wenn Flächen ausgefüllt werden sollen
*/
public void print(Control control, Printer printer, float marginX,
float marginY, boolean borders, boolean fill) {
// Grafikkontext und Repositorien anfertigen
GC gc = new GC(printer);
printerFonts = new PrinterFonts(printer);
printerColors = new PrinterColors(printer);
// Druckerauflösung ermitteln
Point dpi = printer.getDPI();
// drucken
print(control, gc, dpi, (int) (dpi.x * marginX),
(int) (dpi.y * marginY), borders, fill);
// Aufräumen
printerColors.dispose();
printerFonts.dispose();
gc.dispose();
}
/**
* Druckt ein Widget mitsamt seiner Kindelemente
*
* @param w –
* das zu druckende Widget
* @param gc –
* der grafische Kontext
* @param dpi –
* Druckerauflösung
* @param offX –
* X-Offset in Pixel
* @param offY –
* Y-Offset in Pixel
* @param borders –
* true, wenn Umrahmungen gewünscht sind
* @param fill –
* true, wenn Flächen ausgefüllt werden sollen
*/
private void print(Widget w, GC gc, Point dpi, int offX, int offY,
boolean borders, boolean fill) {
// Das Widget ausdrucken
Rectangle printBounds = null;
if (w instanceof Control)
printBounds = printControl((Control) w, gc, dpi, offX, offY,
borders, fill);
else if (w instanceof TreeItem) {
TreeItem item = (TreeItem) w;
printBounds = printItem(item, gc, dpi, offX, offY, fill);
} else if (w instanceof TableItem) {
TableItem item = (TableItem) w;
printItem(item, gc, dpi, offX, offY, fill);
}
// Die Kindelemente ausdrucken
if (printBounds != null) {
if (w instanceof TreeItem) {
TreeItem item = (TreeItem) w;
for (TreeItem child : item.getItems()) {
print(child, gc, dpi, printBounds.x, printBounds.y,
borders, fill);
}
} else {
Rectangle oldClip = gc.getClipping();
gc.setClipping(printBounds.x, printBounds.y, printBounds.width,
printBounds.height);
if (w instanceof Tree) {
gc.setClipping(printBounds.x, printBounds.y,
printBounds.width, printBounds.height);
Tree tree = (Tree) w;
for (TreeItem child : tree.getItems()) {
print(child, gc, dpi, printBounds.x, printBounds.y,
borders, fill);
}
} else if (w instanceof Table) {
Table table = (Table) w;
for (TableItem child : table.getItems()) {
print(child, gc, dpi, printBounds.x, printBounds.y,
borders, fill);
}
} else if (w instanceof Composite) {
gc.setClipping(printBounds.x, printBounds.y,
printBounds.width, printBounds.height);
Composite composite = (Composite) w;
for (Control child : composite.getChildren()) {
print(child, gc, dpi, printBounds.x, printBounds.y,
borders, fill);
}
}
gc.setClipping(oldClip);
}
}
}
/**
* Druckt ein TableItem
*
* @param item –
* das zu druckende TableItem
* @param gc –
* der grafische Kontext
* @param dpi –
* Druckerauflösung
* @param offX –
* X-Offset in Pixel
* @param offY –
* Y-Offset in Pixel
* @param fill –
* true, wenn Flächen ausgefüllt werden sollen
*/
private void printItem(TableItem item, GC gc, Point dpi, int offX,
int offY, boolean fill) {
int columns = Math.max(1, item.getParent().getColumnCount());
for (int i = 0; i < columns; i++) {
// Umrandung und Ausfüllen
Rectangle printBounds = renderArea(item.getDisplay(), gc, offX,
offY, dpi, item.getBounds(i), null, (fill) ? item
.getBackground() : null);
// Text
printText(gc, item.getText(), printBounds, item.getFont(), item
.getForeground(), false);
}
}
/**
* Druckt ein TreeItem
*
* @param item –
* das zu druckende TreeItem
* @param gc –
* der grafische Kontext
* @param dpi –
* Druckerauflösung
* @param offX –
* X-Offset in Pixel
* @param offY –
* Y-Offset in Pixel
* @param fill –
* true, wenn Flächen ausgefüllt werden sollen
* @return -Druckbereich des Widgets
*/
private Rectangle printItem(TreeItem item, GC gc, Point dpi, int offX,
int offY, boolean fill) {
int columns = Math.max(1, item.getParent().getColumnCount());
Rectangle printBounds = null;
for (int i = 0; i < columns; i++) {
// Umrandung und Ausfüllen
Rectangle bounds = renderArea(item.getDisplay(), gc, offX, offY,
dpi, item.getBounds(), null, (fill) ? item.getBackground()
: null);
if (printBounds == null)
printBounds = bounds;
// Text
printText(gc, item.getText(), bounds, item.getFont(), item
.getForeground(), false);
}
return printBounds;
}
/**
* Druckt ein Control-Element
*
* @param control –
* das zu druckende Control-Element
* @param gc –
* der grafische Kontext
* @param dpi –
* Druckerauflösung
* @param offX –
* X-Offset in Pixel
* @param offY –
* Y-Offset in Pixel
* @param borders –
* true, wenn Umrahmungen gewünscht sind
* @param fill –
* true, wenn Flächen ausgefüllt werden sollen
* @return -Druckbereich des Widgets
*/
private Rectangle printControl(Control control, GC gc, Point dpi, int offX,
int offY, boolean borders, boolean fill) {
if (!control.isVisible())
return null;
// Umrandung und Ausfüllen
Display display = control.getDisplay();
Rectangle printBounds = renderArea(display, gc, offX, offY, dpi,
control.getBounds(),
(borders && hasBorder(control)) ? getColors().getColor(
FormColors.BORDER) : display
.getSystemColor(SWT.COLOR_WHITE), (fill) ? control
.getBackground() : null);
// Text – ExpandableComposites replizieren den Text in einem
// Kindelement; wir verhindern den doppelten Ausdruck
if (!(control instanceof ExpandableComposite)) {
// Per Java-Reflection
Class<? extends Control> clazz = control.getClass();
try {
Method m = clazz.getMethod("getText", (Class[]) null);
String text = (String) m.invoke(control, (Object[]) null);
printText(gc, text, printBounds, control.getFont(), control
.getForeground(), (control.getStyle() & SWT.WRAP) != 0);
} catch (Exception e) {
}
}
return printBounds;
}
/**
* Druckt den Widget-Text
*
* @param gc –
* der grafische Kontext
* @param text –
* Text
* @param bounds –
* Druckbereich
* @param font –
* Display-Font
* @param color –
* Display-Farbe
* @param wrap –
* true, wenn Text umgebrochen werden soll
*/
private void printText(GC gc, String text, Rectangle bounds, Font font,
Color color, boolean wrap) {
Rectangle oldClip = gc.getClipping();
gc.setClipping(bounds);
text = text.trim();
if (text != null && text.length() > 0) {
gc.setForeground(printerColors.getColor(color.getRGB()));
gc.setFont(printerFonts.getFont(font.getFontData()[0]));
Point extent = gc.textExtent(text);
if (extent.x <= bounds.width || !wrap)
gc.drawText(text, bounds.x, bounds.y, true);
else {
int ty = 0;
while (true) {
int wordBreak = -1;
boolean whiteSpace = false;
for (int i = 0; i < text.length(); i++) {
char c = text.charAt(i);
if (Character.isWhitespace(c)) {
if (!whiteSpace) {
wordBreak = i;
whiteSpace = true;
}
if (c == '\n')
break;
} else
whiteSpace = false;
if (gc.textExtent(text.substring(0, i)).x > bounds.width) {
if (wordBreak < 0)
wordBreak = i;
break;
}
}
if (wordBreak < 0) {
gc.drawText(text, bounds.x, bounds.y + ty, true);
break;
}
gc.drawText(text.substring(0, wordBreak), bounds.x,
bounds.y + ty, true);
text = text.substring(wordBreak).trim();
ty += gc.getFontMetrics().getHeight();
if (ty > bounds.height)
break;
}
}
}
gc.setClipping(oldClip);
}
/**
* Umrahmungen und Füllungen
*
* @param display –
* die Display-Instanz
* @param gc –
* der grafische Kontext
* @param offX –
* X-Offset in Pixeln
* @param offY –
* Y-Offset in Pixeln
* @param dpi –
* Druckerauflösung
* @param bounds –
* zu füllender/umrahmender Bereich
* @param borderColor –
* Rahmenfarbe oder null
* @param fillColor –
* Füllfarbe oder null
* @return – der Druckbereich des Widgets
*/
private Rectangle renderArea(Display display, GC gc, int offX, int offY,
Point dpi, Rectangle bounds, Color borderColor, Color fillColor) {
// Bildschirmauflösung ermitteln
Point screenDPI = display.getDPI();
// Positionen umrechnen
int x = offX + bounds.x * dpi.x / screenDPI.x;
int y = offY + bounds.y * dpi.y / screenDPI.y;
int width = bounds.width * dpi.x / screenDPI.x;
int height = bounds.height * dpi.y / screenDPI.y;
// Bereich füllen
if (fillColor != null) {
gc.setBackground(printerColors.getColor(fillColor.getRGB()));
gc.fillRectangle(x, y, width, height);
}
// Bereich umrahmen
if (borderColor != null) {
gc.setForeground(printerColors.getColor(borderColor.getRGB()));
int pixX = dpi.x / screenDPI.x;
int pixY = dpi.y / screenDPI.y;
gc.drawRectangle(x - pixX, y - pixY, width + pixX, height + pixY);
}
// Anfangsposition zurückgeben
return new Rectangle(x, y, width, height);
}
/**
* Ermittelt, ob eine Umrahmung gezeichnet werden soll
*
* @param c –
* das Control-Element
* @return – true, wenn das Element eine Umrahmung hat
*/
public boolean hasBorder(Control c) {
boolean inactiveBorder = false;
boolean textBorder = false;
if (c.getEnabled() == false && !(c instanceof CCombo))
return false;
if ((c.getStyle() & SWT.BORDER) != 0)
return true;
if (c instanceof Hyperlink)
return false;
Object flag = c.getData(KEY_DRAW_BORDER);
if (flag != null) {
if (flag.equals(Boolean.FALSE))
return false;
if (flag.equals(TREE_BORDER))
inactiveBorder = true;
else if (flag.equals(TEXT_BORDER))
textBorder = true;
}
if (getBorderStyle() == SWT.BORDER) {
if (!inactiveBorder && !textBorder)
return false;
if (c instanceof Text || c instanceof Table || c instanceof Tree)
return false;
}
if (!inactiveBorder
&& (c instanceof Text || c instanceof CCombo || textBorder))
return true;
if (inactiveBorder || c instanceof Table || c instanceof Tree)
return true;
return false;
}