1. 程式人生 > >Java:使用Java呼叫印表機進行列印

Java:使用Java呼叫印表機進行列印

一、Java的列印簡介

在我們的實際工作中,經常需要實現列印功能。但由於歷史原因,Java 提供的列印功能一直都比較弱。實際上最初的 jdk 根本不支援列印,直到 jdk1.1 才引入了很輕量的列印支援。實際上,SUN 公司也一直致力於 Java 列印功能的完善,而 Java2 平臺則終於有了一個健壯的列印模式的開端, jdk1.4 則提供了一套完整的"Java 列印服務 API" (Java Print Service API),它對已有的列印功能是積極的補充。

本次調研的列印物件主要是JPG,PDF和Word這三種常見檔案格式。

二、Java列印實現

2.1 JPG圖片檔案格式列印實現

列印JPG圖片格式的檔案,本次採用的Java原生的列印方式。

jdk1.4之後對列印功能有了很好的支援。Java 的列印 API 主要存在於 java.awt.print 包中。而 jdk1.4 新增的類則主要存在於 javax.print 包及其相應的子包 javax.print.event 和 javax.print.attribute 中。其中 javax.print 包中主要包含列印服務的相關類,而 javax.print.event 則包含列印事件的相關定義,javax.print.attribute 則包括列印服務的可用屬性列表等。可以很好的解決列印JPG圖片格式的需求。

優點:jdk的原生支援的列印功能,可直接使用,支援設定各項列印引數。

缺點:侷限性較大,只能列印一些圖片和文字格式的檔案。

具體實現如下:

public static void main(String[] argv) throws Exception {
        File file = new File("E:\\a.jpg");
        String printerName = "HP MFP M436 PCL6";//印表機名包含字串
        PDFPrint(file,printerName);
    }
// 傳入檔案和印表機名稱
public static void JPGPrint(File file,String printerName) throws PrintException {
	if (file == null) {
		System.err.println("缺少列印檔案");
	}
	InputStream fis = null;
	try {
		// 設定列印格式,如果未確定型別,可選擇autosense
		DocFlavor flavor = DocFlavor.INPUT_STREAM.JPEG;
		// 設定列印引數
		PrintRequestAttributeSet aset = new HashPrintRequestAttributeSet();
		aset.add(new Copies(1)); //份數
		//aset.add(MediaSize.ISO.A4); //紙張
		// aset.add(Finishings.STAPLE);//裝訂
		aset.add(Sides.DUPLEX);//單雙面
		// 定位列印服務
		PrintService printService = null;
		if (printerName != null) {
			//獲得本臺電腦連線的所有印表機
			PrintService[] printServices = PrinterJob.lookupPrintServices();
			if(printServices == null || printServices.length == 0) {
				System.out.print("列印失敗,未找到可用印表機,請檢查。");
				return ;
			}
			//匹配指定印表機
			for (int i = 0;i < printServices.length; i++) {
				System.out.println(printServices[i].getName());
				if (printServices[i].getName().contains(printerName)) {
					printService = printServices[i];
					break;
				}
			}
			if(printService==null){
				System.out.print("列印失敗,未找到名稱為" + printerName + "的印表機,請檢查。");
				return ;
			}
		}
		fis = new FileInputStream(file); // 構造待列印的檔案流
		Doc doc = new SimpleDoc(fis, flavor, null);
		DocPrintJob job = printService.createPrintJob(); // 建立列印作業
		job.print(doc, aset);
	} catch (FileNotFoundException e1) {
		e1.printStackTrace();
	} finally {
 // 關閉列印的檔案流
		if (fis != null) {
			try {
				fis.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}
}

2.2 PDF檔案格式列印實現

在經過網上的查詢及對比,我選擇了使用Apache PDFbox來實現進行PDF檔案格式的列印。

Apache PDFbox是一個開源的、基於Java的、支援PDF文件生成的工具庫,它可以用於建立新的PDF文件,修改現有的PDF文件,還可以從PDF文件中提取所需的內容。Apache PDFBox還包含了數個命令列工具。在此,我們只研究列印功能。

優點:功能強大,開源軟體,較完美的解決了PDF格式檔案的一系列處理,使用方便。

缺點:

具體實現如下:

①直接匯入maven依賴:

<dependency>
	<groupId>org.apache.pdfbox</groupId>
	<artifactId>pdfbox</artifactId>
	<version>2.0.6</version>
</dependency>

②程式碼呼叫實現

public static void main(String[] args) throws Exception {
        String pdfFile = "E:\\a.pdf";//檔案路徑
        File file = new File(pdfFile);
        String printerName = "HP MFP M436 PCL6";//印表機名包含字串
        print(file,printerName);
    }
public static void PDFprint(File file ,String printerName) throws Exception {
        PDDocument document = null;
        try {
            document = PDDocument.load(file);
            PrinterJob printJob = PrinterJob.getPrinterJob();
            printJob.setJobName(file.getName());
            if (printerName != null) {
                // 查詢並設定印表機
                //獲得本臺電腦連線的所有印表機
                PrintService[] printServices = PrinterJob.lookupPrintServices();                			 if(printServices == null || printServices.length == 0) {
                    System.out.print("列印失敗,未找到可用印表機,請檢查。");
                    return ;
                }
                PrintService printService = null;
                //匹配指定印表機
                for (int i = 0;i < printServices.length; i++) {
                    System.out.println(printServices[i].getName());
                    if (printServices[i].getName().contains(printerName)) {
                        printService = printServices[i];
                        break;
                    }
                }
                if(printService!=null){
                    printJob.setPrintService(printService);
                }else{
                    System.out.print("列印失敗,未找到名稱為" + printerName + "的印表機,請檢查。");
                    return ;
                }
            }
            //設定紙張及縮放
            PDFPrintable pdfPrintable = new PDFPrintable(document, Scaling.ACTUAL_SIZE);
            //設定多頁列印
            Book book = new Book();
            PageFormat pageFormat = new PageFormat();
            //設定列印方向
            pageFormat.setOrientation(PageFormat.PORTRAIT);//縱向
            pageFormat.setPaper(getPaper());//設定紙張
            book.append(pdfPrintable, pageFormat, document.getNumberOfPages());
            printJob.setPageable(book);
            printJob.setCopies(1);//設定列印份數
            //新增列印屬性
            HashPrintRequestAttributeSet pars = new HashPrintRequestAttributeSet();
            pars.add(Sides.DUPLEX); //設定單雙頁
            printJob.print(pars);
        }finally {
            if (document != null) {
                try {
                    document.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
public static Paper getPaper() {
        Paper paper = new Paper();
        // 預設為A4紙張,對應畫素寬和高分別為 595, 842
        int width = 595;
        int height = 842;
        // 設定邊距,單位是畫素,10mm邊距,對應 28px
        int marginLeft = 10;
        int marginRight = 0;
        int marginTop = 10;
        int marginBottom = 0;
        paper.setSize(width, height);
        // 下面一行程式碼,解決了列印內容為空的問題
        paper.setImageableArea(marginLeft, marginRight, width - (marginLeft + marginRight), height - (marginTop + marginBottom));
        return paper;
    }

2.3 Word檔案格式列印實現

列印word這裡共使用了2種方法,一種是直接使用jacob進行列印,這種方法列印word我暫時沒有找到設定列印引數的相關方式,(但是列印Excle好像設定列印引數沒問題,在PrintOut操作裡設定,引數具體可參考https://msdn.microsoft.com/zh-cn/vba/excel-vba/articles/worksheets-printout-method-excel),第二種是將word先轉成pdf檔案,然後進行列印。

2.3.1 Word檔案採用jacob外掛進行列印實現。

Jacob是一個 Java到微軟的com介面的橋樑。使用Jacob允許任何JVM訪問com物件,從而使Java應用程式能夠呼叫com物件。如果你要對  Word、Excel 進行處理,Jacob是一個好的選擇。

優點:可以很好的處理word文件的相關操作。

缺點:必須手動引入dll檔案,暫時未找到方法設定列印相關引數,只能暫時實現靜默列印。

具體實現如下:

①下載jacob.zip ,對應(86/64)的dll檔案放在%Java_Home%jre/bin目錄下。

②匯入jacob.jar到工程中

 在工程中建立lib資料夾儲存jacob.jar:reseources—lib—jacob.jar

 

  新增Maven依賴:

         <!--新增本地的jacob.jar包-->

        <!--新增本地的jacob.jar包-->
		<dependency>
			<groupId>com.jacob</groupId>
			<artifactId>jacob</artifactId>
			<version>1.19</version>
			<scope>system</scope>
		<systemPath>${basedir}/src/main/resources/lib/jacob.jar</systemPath>
		</dependency>

具體程式碼實現:

public static void main(String[] args) {
        String filePath = "E:\\a.docx";//檔案路徑
        String printerName = "HP MFP M436 PCL6";//印表機名包含字串
        printWord(filePath,printerName);
    }

    public static void printWord(String filePath, String printerName){
//        初始化執行緒
        ComThread.InitSTA();
        ActiveXComponent word = new ActiveXComponent("Word.Application");
       //設定印表機名稱
        word.setProperty("ActivePrinter", new Variant(printerName));
        // 這裡Visible是控制文件開啟後是可見還是不可見,若是靜默列印,那麼第三個引數就設為false就好了
        Dispatch.put(word, "Visible", new Variant(false));
        // 獲取文件屬性
        Dispatch document = word.getProperty("Documents").toDispatch();
        // 開啟啟用文擋
        Dispatch doc=Dispatch.call(document, "Open", filePath).toDispatch();
        //Dispatch doc = Dispatch.invoke(document, "Open", Dispatch.Method,
              //  new Object[] { filePath }, new int[1]).toDispatch();
        try{
            Dispatch.callN(doc, "PrintOut");
            System.out.println("列印成功!");
        }catch (Exception e){
            e.printStackTrace();
            System.out.println("列印失敗");
        }finally {
            try {
                if (doc != null) {
                    Dispatch.call(doc, "Close", new Variant(0));//word文件關閉
                }
            } catch (Exception e2) {
                e2.printStackTrace();
            }
            //退出
            word.invoke("Quit", new Variant[0]);
            //釋放資源
            ComThread.Release();
            ComThread.quitMainSTA();
        }
        }

2.3.2 先將word轉化為pdf檔案,然後列印pdf(lz使用)

優點:可設定列印引數等操作

缺點:有橫向頁的word轉化時可能會出現錯誤,同時也要引入jacob相關依賴和檔案

具體實現步驟如下:

①因為轉化也是使用jacob外掛,所以也需要根據第一種方法一樣引入jacob相關依賴和檔案

②列印pdf檔案時,使用的是上面講述的pdfbox外掛,所以也需要引入pdfbox的依賴

        <dependency>
            <groupId>org.apache.pdfbox</groupId>
            <artifactId>pdfbox</artifactId>
            <version>2.0.6</version>
        </dependency>

③程式碼具體實現

首先將word檔案轉化為pdf檔案

//word轉化pdf,傳入轉換前的檔案路徑(例:"E:\\a.docx")和轉換後的檔案路徑(例:"E:\\a.pdf")
    public static void wordToPDF(String sFilePath,String toFilePath) {
        System.out.println("啟動 Word...");
        long start = System.currentTimeMillis();
        ActiveXComponent app = null;
        Dispatch doc = null;
        try {
            app = new ActiveXComponent("Word.Application");
            app.setProperty("Visible", new Variant(false));
            Dispatch docs = app.getProperty("Documents").toDispatch();
            doc = Dispatch.call(docs, "Open", sfilePath).toDispatch();
            System.out.println("開啟文件:" + sfilePath);
            System.out.println("轉換文件到 PDF:" + toFilePath);
            File tofile = new File(toFilePath);
            if (tofile.exists()) {
                tofile.delete();
            }
            Dispatch.call(doc, "SaveAs", toFilePath, // FileName
                    17);//17是pdf格式
            long end = System.currentTimeMillis();
            System.out.println("轉換完成..用時:" + (end - start) + "ms.");

        } catch (Exception e) {
            System.out.println("========Error:文件轉換失敗:" + e.getMessage());
        } finally {
            Dispatch.call(doc, "Close", false);
            System.out.println("關閉文件");
            if (app != null)
                app.invoke("Quit", new Variant[]{});
        }
        // 如果沒有這句話,winword.exe程序將不會關閉
        ComThread.Release();
    }

然後列印pdf檔案

//這裡傳入的檔案為word轉化生成的pdf檔案
public static void PDFprint(File file ,String printerName) throws Exception {
        PDDocument document = null;
        try {
            document = PDDocument.load(file);
            PrinterJob printJob = PrinterJob.getPrinterJob();
            printJob.setJobName(file.getName());
            if (printerName != null) {
                // 查詢並設定印表機
                //獲得本臺電腦連線的所有印表機
                PrintService[] printServices = PrinterJob.lookupPrintServices();                			 if(printServices == null || printServices.length == 0) {
                    System.out.print("列印失敗,未找到可用印表機,請檢查。");
                    return ;
                }
                PrintService printService = null;
                //匹配指定印表機
                for (int i = 0;i < printServices.length; i++) {
                    System.out.println(printServices[i].getName());
                    if (printServices[i].getName().contains(printerName)) {
                        printService = printServices[i];
                        break;
                    }
                }
                if(printService!=null){
                    printJob.setPrintService(printService);
                }else{
                    System.out.print("列印失敗,未找到名稱為" + printerName + "的印表機,請檢查。");
                    return ;
                }
            }
            //設定紙張及縮放
            PDFPrintable pdfPrintable = new PDFPrintable(document, Scaling.ACTUAL_SIZE);
            //設定多頁列印
            Book book = new Book();
            PageFormat pageFormat = new PageFormat();
            //設定列印方向
            pageFormat.setOrientation(PageFormat.PORTRAIT);//縱向
            pageFormat.setPaper(getPaper());//設定紙張
            book.append(pdfPrintable, pageFormat, document.getNumberOfPages());
            printJob.setPageable(book);
            printJob.setCopies(1);//設定列印份數
            //新增列印屬性
            HashPrintRequestAttributeSet pars = new HashPrintRequestAttributeSet();
            pars.add(Sides.DUPLEX); //設定單雙頁
            printJob.print(pars);
        }finally {
            if (document != null) {
                try {
                    document.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
public static Paper getPaper() {
        Paper paper = new Paper();
        // 預設為A4紙張,對應畫素寬和高分別為 595, 842
        int width = 595;
        int height = 842;
        // 設定邊距,單位是畫素,10mm邊距,對應 28px
        int marginLeft = 10;
        int marginRight = 0;
        int marginTop = 10;
        int marginBottom = 0;
        paper.setSize(width, height);
        // 下面一行程式碼,解決了列印內容為空的問題
        paper.setImageableArea(marginLeft, marginRight, width - (marginLeft + marginRight), height - (marginTop + marginBottom));
        return paper;
    }

最後別忘了將生成的pdf檔案刪除

                File file=new File(toFilePath);
                if(file.exists()&&file.isFile())
                    file.delete();

三、總結

至此,本次實現的JPG、PDF和Word三種檔案格式的列印已經全部實現,分別採用了原生列印和PDFbox外掛和jacob外掛進行實現。記錄一下,以防忘記,留待後續使用。