首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

SWT/JFace 列印功能完整實戰:從簡單文字到 StyledText 專業列印

     編輯:Java 相關知識

前言

SWT 提供 org.eclipse.swt.printing 套件實現跨平台列印。核心類別:Printer(列印作業)、PrintDialog(選擇印表機/參數)、GC(繪圖上下文)。本文展示兩個完整範例:基礎文字列印、完整文本編輯器列印(含分頁、頁碼、邊距、字體/顏色同步、非阻塞列印執行緒)。

範例 1:最簡單列印

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

範例 2:專業文本編輯器列印(完整功能)

核心類:PrintMargin(邊距計算)

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

主編輯器類:Main(含列印執行緒)

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

範例 3:StyledText 內建列印(一鍵完成)

// 在 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(); // 或提交給執行緒池
    }
};

關鍵技術點總結

1. 列印流程標準化

  1. PrintDialog.open() → PrinterData
  2. new Printer(data) → printer.startJob(name)
  3. 建立 GC(printer)、設定字體/顏色
  4. printer.startPage() → 繪製內容 → printer.endPage()(重複)
  5. printer.endJob() → 釋放所有資源

2. 邊距計算公式

// 英寸轉像素:margin_inch * DPI - trim
// 右/下邊距:clientArea + trim - margin_inch * DPI - trim

3. 非阻塞列印執行緒

  • 列印可能耗時(大文檔、慢速印表機),必須在背景執行緒執行。
  • UI 執行緒保持響應,可顯示進度對話框、取消按鈕。
  • 列印完成後 Display.asyncExec 更新 UI 狀態。

4. 資源管理鐵律

// 所有建立於 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

5. 分頁邏輯核心

  • 追蹤 x, y 當前繪製位置。
  • 單詞緩衝區 wordBuffer 實現單詞不拆分換行。
  • y + lineHeight > bottomMargin 觸發換頁。
  • 換頁時重置 x、遞增 pageNumber、繪製頁碼。

StyledTextPrintOptions 選項

屬性說明
header頁首文字,支援 <page> 變數
footer頁尾文字,支援 <page>
jobName列印作業名稱
lineStyle行樣式常數

常見問題與解決

問題解決
列印內容截斷/位置錯誤檢查邊距計算、DPI、trim、clientArea 邏輯
中文亂碼/缺字確保印表機端字體支援,或嵌入字體;StyledText 內建較好
UI 假死移至背景執行緒,長文檔分批列印並更新進度
多頁頁碼錯誤pageNumber 遞增邏輯、頁碼繪製座標檢查
資源洩漏嚴格遵循建立/釋放配對,使用 try-finally

總結

SWT 列印核心在於 Printer + GC 繪圖模型:

  • 基礎模式:手動管理 GC、字體、顏色、分頁、邊距,彈性最高。
  • StyledText 模式:StyledText.print() 一鍵生成 Runnable,自動處理樣式、分頁、頁首尾,推薦用於文本編輯器。
  • 非阻塞鐵律:列印必須在背景執行緒,UI 保持響應。
  • 資源釋放:Printer 相關資源建立/釋放嚴格配對。

掌握此模式,即可為任何 SWT 應用添加專業級列印功能。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved