SWT 提供 org.eclipse.swt.printing 套件實現跨平台列印。核心類別:Printer(列印作業)、PrintDialog(選擇印表機/參數)、GC(繪圖上下文)。本文展示兩個完整範例:基礎文字列印、完整文本編輯器列印(含分頁、頁碼、邊距、字體/顏色同步、非阻塞列印執行緒)。
public class SimplePrint {
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
shell.pack(); shell.open();
PrintDialog dialog = new PrintDialog(shell);
PrinterData data = dialog.open(); // 顯示系統列印對話框
if (data != null) {
Printer printer = new Printer(data);
if (printer.startJob("Text")) {
GC gc = new GC(printer);
if (printer.startPage()) {
gc.drawString("Eclipse", 200, 200); // 繪製文字
printer.endPage();
}
gc.dispose();
printer.endJob();
}
printer.dispose();
System.out.println("Print job done.");
}
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) display.sleep();
}
display.dispose();
}
}
public class PrintMargin {
public int left, right, top, bottom;
private PrintMargin(int l, int r, int t, int b) {
left = l; right = r; top = t; bottom = b;
}
// 統一邊距(英寸)
static PrintMargin getPrintMargin(Printer printer, double margin) {
return getPrintMargin(printer, margin, margin, margin, margin);
}
// 四邊獨立邊距(英寸)
static PrintMargin getPrintMargin(Printer printer, double mL, double mR, double mT, double mB) {
Rectangle clientArea = printer.getClientArea(); // 可列印區域
Rectangle trim = printer.computeTrim(0, 0, 0, 0); // 邊框修飾
Point dpi = printer.getDPI(); // 點/英寸
int leftMargin = (int)(mL * dpi.x) - trim.x;
int rightMargin = clientArea.width + trim.width - (int)(mR * dpi.x) - trim.x;
int topMargin = (int)(mT * dpi.y) - trim.y;
int bottomMargin = clientArea.height + trim.height - (int)(mB * dpi.y) - trim.y;
return new PrintMargin(leftMargin, rightMargin, topMargin, bottomMargin);
}
}
public class Main {
// ... 字體、顏色、選單、檔案開啟等初始化代碼 ...
// 選單項:列印
MenuItem printItem = new MenuItem(fileMenu, SWT.PUSH);
printItem.setText("&Print...\tCtrl+P");
printItem.setAccelerator(SWT.CTRL | 'P');
printItem.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
menuPrint();
}
});
void menuPrint() {
PrintDialog dialog = new PrintDialog(shell, SWT.NONE);
PrinterData data = dialog.open();
if (data == null) return;
if (data.printToFile) data.fileName = "print.out";
textToPrint = text.getText(); // 獲取編輯器文字
printer = new Printer(data);
// 關鍵:在背景執行緒列印,避免阻塞 UI
Thread printingThread = new Thread("Printing") {
public void run() {
print(printer);
printer.dispose();
}
};
printingThread.start();
}
void print(Printer printer) {
if (!printer.startJob("Text")) return;
// 1. 計算邊距(1 英寸)
Rectangle clientArea = printer.getClientArea();
Rectangle trim = printer.computeTrim(0, 0, 0, 0);
Point dpi = printer.getDPI();
PrintMargin margins = PrintMargin.getPrintMargin(printer, 1.0);
// 2. 準備 GC、字體、顏色
GC gc = new GC(printer);
FontData fontData = font.getFontData()[0];
Font printerFont = new Font(printer, fontData.getName(), fontData.getHeight(), fontData.getStyle());
gc.setFont(printerFont);
Color printerFg = new Color(printer, foregroundColor.getRGB());
Color printerBg = new Color(printer, backgroundColor.getRGB());
gc.setForeground(printerFg);
gc.setBackground(printerBg);
int tabWidth = gc.stringExtent(" ").x; // 4 空格
int lineHeight = gc.getFontMetrics().getHeight();
// 3. 執行列印邏輯
printText(gc, printer, margins, lineHeight, tabWidth);
// 4. 清理
printer.endJob();
printerFont.dispose();
printerFg.dispose();
printerBg.dispose();
gc.dispose();
}
void printText(GC gc, Printer printer, PrintMargin margins, int lineHeight, int tabWidth) {
printer.startPage();
int x = margins.left;
int y = margins.top;
int pageNumber = 1;
StringBuffer wordBuffer = new StringBuffer();
for (int index = 0; index < textToPrint.length(); ) {
char c = textToPrint.charAt(index++);
if (c == '\r' || c == '\n') {
// 處理換行
if (c == '\r' && index < textToPrint.length() && textToPrint.charAt(index) == '\n') index++;
printWordBuffer(gc, wordBuffer, x, y, margins, tabWidth);
wordBuffer.setLength(0);
newLine(gc, printer, margins, lineHeight, x, y, pageNumber);
} else if (c == '\t') {
// Tab 鍵
if (gc.textExtent(wordBuffer.toString() + " ").x > margins.right - margins.left) {
printWordBuffer(gc, wordBuffer, x, y, margins, tabWidth);
wordBuffer.setLength(0);
}
wordBuffer.append(' ');
// 看一個字決定是否換行
if (index < textToPrint.length() && !Character.isWhitespace(textToPrint.charAt(index))) {
String word = readWord(textToPrint, index);
if (gc.textExtent(wordBuffer + word).x > margins.right - margins.left) {
printWordBuffer(gc, wordBuffer, x, y, margins, tabWidth);
wordBuffer.setLength(0);
}
}
} else {
wordBuffer.append(c);
if (Character.isWhitespace(c)) {
printWordBuffer(gc, wordBuffer, x, y, margins, tabWidth);
wordBuffer.setLength(0);
}
}
}
if (wordBuffer.length() > 0) printWordBuffer(gc, wordBuffer, x, y, margins, tabWidth);
if (y + lineHeight <= margins.bottom) printer.endPage();
}
void printWordBuffer(GC gc, StringBuffer buffer, int[] x, int y, PrintMargin margins, int tabWidth) {
if (buffer.length() == 0) return;
String word = buffer.toString();
int wordWidth = gc.textExtent(word).x;
if (x[0] + wordWidth > margins.right) {
newLine(gc, printer, margins, lineHeight, x, y, pageNumber);
}
gc.drawString(word, x[0], y, true);
x[0] += wordWidth;
}
void newLine(GC gc, Printer printer, PrintMargin margins, int lineHeight, int[] x, int[] y, int[] pageNumber) {
x[0] = margins.left;
y[0] += lineHeight;
if (y[0] + lineHeight > margins.bottom) {
printer.endPage();
if (index < textToPrint.length()) {
y[0] = margins.top;
pageNumber[0]++;
printer.startPage();
// 繪製頁碼
String pageStr = "- " + pageNumber[0] + " -";
int pageX = (margins.right - margins.left - gc.textExtent(pageStr).x) / 2 + margins.left;
gc.drawString(pageStr, pageX, margins.bottom + gc.textExtent(pageStr).y);
}
}
}
String readWord(String text, int offset) {
StringBuffer sb = new StringBuffer();
while (offset < text.length() && !Character.isWhitespace(text.charAt(offset))) {
sb.append(text.charAt(offset++));
}
return sb.toString();
}
}
// 在 Action 中
Action printStyled = new Action("Print (StyledText)") {
public void run() {
StyledTextPrintOptions options = new StyledTextPrintOptions();
options.header = "SWT Application";
options.footer = "Page ";
options.jobName = "StyledText Print";
// 返回 Runnable,可同步或非同步執行
Runnable printJob = styledText.print(new Printer(), options);
printJob.run(); // 或提交給執行緒池
}
};
PrintDialog.open() → PrinterDatanew Printer(data) → printer.startJob(name)GC(printer)、設定字體/顏色printer.startPage() → 繪製內容 → printer.endPage()(重複)printer.endJob() → 釋放所有資源// 英寸轉像素:margin_inch * DPI - trim
// 右/下邊距:clientArea + trim - margin_inch * DPI - trim
Display.asyncExec 更新 UI 狀態。// 所有建立於 Printer 的資源,必須在 endJob 前釋放
Font printerFont = new Font(printer, ...);
Color printerColor = new Color(printer, ...);
GC gc = new GC(printer);
// ...
gc.dispose();
printerFont.dispose();
printerColor.dispose();
printer.dispose(); // 最後釋放 Printer
x, y 當前繪製位置。wordBuffer 實現單詞不拆分換行。y + lineHeight > bottomMargin 觸發換頁。x、遞增 pageNumber、繪製頁碼。| 屬性 | 說明 |
|---|---|
| header | 頁首文字,支援 <page> 變數 |
| footer | 頁尾文字,支援 <page> |
| jobName | 列印作業名稱 |
| lineStyle | 行樣式常數 |
| 問題 | 解決 |
|---|---|
| 列印內容截斷/位置錯誤 | 檢查邊距計算、DPI、trim、clientArea 邏輯 |
| 中文亂碼/缺字 | 確保印表機端字體支援,或嵌入字體;StyledText 內建較好 |
| UI 假死 | 移至背景執行緒,長文檔分批列印並更新進度 |
| 多頁頁碼錯誤 | pageNumber 遞增邏輯、頁碼繪製座標檢查 |
| 資源洩漏 | 嚴格遵循建立/釋放配對,使用 try-finally |
SWT 列印核心在於 Printer + GC 繪圖模型:
StyledText.print() 一鍵生成 Runnable,自動處理樣式、分頁、頁首尾,推薦用於文本編輯器。掌握此模式,即可為任何 SWT 應用添加專業級列印功能。