純Java的解決方案:
成都創(chuàng)新互聯(lián)-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設(shè)、高性價比新市網(wǎng)站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫,直接使用。一站式新市網(wǎng)站制作公司更省心,省錢,快速模板網(wǎng)站建設(shè)找我們,業(yè)務(wù)覆蓋新市地區(qū)。費用合理售后完善,十載實體公司更值得信賴。
我們首先想到的自然就是JDK1.4提供的JPS(Java Printing Service)啦,不過,這東西雖然說支持PDF的Flavor,但是,不管是個人實驗還是網(wǎng)上他人的評論,好像根本就是useless,可能如果說你 的打印機Driver支持PDF的Flavor的話,JPS會檢測到你的driver的這個特性,能夠成功的打印PDF文檔出來,但是,大部分情況下,這 種情形是不成立的,故此JPS死路一條啦!
讓我們看看PDF的老家Adobe那里有沒有什么法寶,我們發(fā)現(xiàn)一個Viewer Bean的組件,說是可以將PDF以Bean組件的方式潛入到Swing中,哇,太爽了,不過慢著,協(xié)議上說不提供任何支持,也不保證不出任何問題,管那 么些,試過再說,一實驗才知道,靠,Exception頻發(fā),而且這個組件較為陳舊,還是扔一邊吧!
還有一個PDFBox,Open Source的,不過對中文支持不好,而且好像開發(fā)進(jìn)度也不是很好,沒有發(fā)布一個正式的版本,基本上不能用于生產(chǎn)環(huán)境;
最后,求助于Commercial的產(chǎn)品吧,實驗了一下ActiveTree的JPrint,感覺不錯,完全可以勝任我們的需求而且恰到好處,不過授權(quán)費 很貴,Email問過之后的答復(fù)是2000USD的最低購買,呵呵,雖然日本人很有錢,但也心疼這個銀子啊,所以最終也得作罷?。ˋctiveTree的 授權(quán)其實挺令我ft的,他其實在2003年的時候是可以免費使用的,但之后就變卦了,呵呵,當(dāng)時記得我還給提過一些bug之類,算了,人家做出這個東西也 不容易)
其他商業(yè)產(chǎn)品也是價格不菲,所以,基本上純Java的solution到這里就否決了,讓我們看Java-Com的解決方案吧!
Java-Com 的解決方案:
在前一條路走不通之后,我痛定思痛,決定轉(zhuǎn)向自己不熟悉的領(lǐng)域,ms的領(lǐng)地,我打算從Java中調(diào)用Com組件,由Com組件來幫助我們實現(xiàn)PDF的打印 工作,不夠這條路也不是一帆風(fēng)順那!
我們知道,Acrobat Reader在發(fā)布的時候會隨同發(fā)布一個支持瀏覽器的com組件用來manipulate他的這個PDF文檔格式,所以,我們想要本地調(diào)用這個隨同發(fā)布的 Com組件來實現(xiàn)PDF打印。雖然Version5,6,7的這個組件格式不一樣(5,6是以ocx的格式發(fā)布,7是以dll的格式發(fā)布),但是,不管那 么些,先從7開始吧!
要調(diào)用com,那么我們需要一個從java到com的Bridge,所以,jacob第一個躍入我的腦海,因為之前就用過嘛!但是麻煩來了,我們并不知道 這個com組件提供了那些調(diào)用接口??!哎,沒辦法,回學(xué)校求教熟悉.net的同學(xué),給好不容易弄出幾個需要的調(diào)用方法(哎,可憐我的周末?。?,星期一就回 來用jacob調(diào)用啦,可是左試右試就是一直拋異常,我那個氣??!難道是jacob的為問題?!我就又找了jcom和jcom2等類似的產(chǎn)品,但jcom 全是日文文檔,沒有辦法,而jcom2估計也是一個德行(我忘了為什么當(dāng)初否決了這個),所以就決定試一試商業(yè)產(chǎn)品吧!
這方面的商業(yè)產(chǎn)品主要有J-Integra,JPanel(好像叫這個名字)以及一個叫JNIWrapper的產(chǎn)品(這個是一個人用用來演示在java中 使用Acrobat5打印PDF的時候提到的)。這些商業(yè)產(chǎn)品好的地方就是他可以根據(jù)某個你要調(diào)用的com組件為你自動生成相應(yīng)的Proxy對象java 代碼,這樣你就可以直接調(diào)用你熟悉的java代碼了。像jacob等開源項目,如果給出一個類似的code generation工具的話,就完全不遜于這些商業(yè)產(chǎn)品啦。鑒于商業(yè)產(chǎn)品的價格,我最終還是否決了這些(日本人其實也聽摳門的)。
這樣,Java-com也對這個問題沒轍了。
不過,最后在我的解決方案中,我還是使用了Jacob,這是后話,暫且不提...
那我們考慮一下,如果PDF打印不行,打印其他格式行不行?!比如圖片,這個JPS可以完全打印,所以,我們找一下有沒有將PDF格式轉(zhuǎn)換為其他格式的工 具吧!
PDF格式轉(zhuǎn)換的解決方案:
在這個領(lǐng)域,主要的就是有GhostScript/GView和ImageMagick,前者可以將PDF格式轉(zhuǎn)換為PostScript格式,但是好像 GhostScript也不能用JPS完全打印出來;而后者是一個將PDF轉(zhuǎn)換為Image的API工具,他的Java實現(xiàn)叫JMagick,但他有一個 跟GhostScript同樣的問題,就是要轉(zhuǎn)換,就必須在本地安裝,然后通過命令行的方式調(diào)用,這個顯然也不是很好,而且集成性很差,還是作罷!
剩下的一個是命令行調(diào)用啦,這是從itext網(wǎng)站找到的,你可以通過在命令行運行AcroRd32 /p /h "path to PDF file"這樣的命令來打印你要打印的PDF文件,當(dāng)然,你可以在PDF文件生成后就將他們依次放入一個批處理文件來執(zhí)行這些打印命令,但是這個方案唯一 的問題就是,每打印一個文件都會啟動一個Acrobat Reader窗口而且必須手動關(guān)閉,這現(xiàn)在不能滿足目前的系統(tǒng)要求。
好了,所有的方案基本上都羅列完了,也沒有找到一個可行的方案:-(
(沒有銀子嘛,不然Activetree的JPrint不錯的說)
這些東西差不多郁悶了我3,4天吧,那幾天簡直就是bored to death.
不過,在郁悶的這幾天的結(jié)尾,卻有一道靈光閃過我的腦海...
能不能說啟動一個打印service,當(dāng)文檔要打印的時候,直接發(fā)送給它就行了那?!而恰好我發(fā)現(xiàn)一段在網(wǎng)頁中加載PDF文檔的Javascript代 碼,而且完全可以使用js來控制PDF的打印,所以,最終的這個方案就浮出水面了 ...
1-使用jacob啟動一個IE進(jìn)程,并隱藏IE窗口;
if(ieAutomation == null)
ieAutomation = new ActiveXComponent("InternetExplorer.Application");
ieAutomation.setProperty("Visible",new Variant(false));
2-PDF前端在生成PDF文件之后發(fā)送生成后的文件到JacobPDFPrinter,JacobPDFPrinter根據(jù)出入的PDF文件的全路徑使 用Velocity模板引擎動態(tài)生成一個包含使用Javascript代碼實現(xiàn)的PDF打印邏輯的HTML文檔(當(dāng)然,使用Velocity生成文檔這部 分邏輯我們單獨抽出到VeloIEPrinterGenerator類中);
3-在HTML生成之后,在JacobPDFPrinter中就可以使用jacob調(diào)用IE的Navigate2,將IE重定向到剛才生成的這個HTML 文件啦,這樣,IE就會在后臺調(diào)用JS代碼將PDF打印到默認(rèn)打印機;
4-打印成功之后,清除臨時動態(tài)生成的HTML文件;
5-當(dāng)主程序退出之前,Quit后臺IE進(jìn)程。
以上就是我能給出的一個solution,并不完美,但it works.
需要注意的幾個問題是:
(1)需要設(shè)置IE的一個高級選項,運行本地腳本運行;
(2)因為Java和Com線程模型的不一致,導(dǎo)致在最終Quit后臺IE進(jìn)程的時候會拋出Com調(diào)用異常,因為對于Win平臺API以及相關(guān)編程模型不 是很熟悉,所以,這個問題需要求助于別人幫忙解決;
(3)IE在執(zhí)行JS打印PDF的時候,同樣會后臺啟動Acrobat的一個進(jìn)程,而這個進(jìn)程我們程序中無法控制其生命周期,所以,主程序退出后,我們沒 有辦法同時kill這個進(jìn)程,好在不管我們運行多少次,這個進(jìn)程在后臺只有一個,所以,性能負(fù)擔(dān)不是很大;
轉(zhuǎn)載
java導(dǎo)出pdf需要用到iText庫,iText是著名的開放源碼的站點sourceforge一個項目,是用于生成PDF文檔的一個java類庫。通過iText不僅可以生成PDF或rtf
的文檔,而且可以將XML、Html文件轉(zhuǎn)化為PDF文件。
iText的安裝非常方便,下載iText.jar文件后,只需要在系統(tǒng)的CLASSPATH中加入iText.jar的路徑,在程序中就可以使用
iText類庫了。
代碼如下:
public class createPdf {
//自己做的一個簡單例子,中間有圖片之類的
//先建立Document對象:相對應(yīng)的 這個版本的jar引入的是com.lowagie.text.Document
Document document = new Document(PageSize.A4, 36.0F, 36.0F, 36.0F, 36.0F);
public void getPDFdemo() throws DocumentException, IOException{
//這個導(dǎo)出用的是 iTextAsian.jar 和iText-2.1.3.jar 屬于比較老的方法。 具體下在地址見:
//首先
//字體的定義:這里用的是自帶的jar里面的字體
BaseFont bfChinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", false);
// 當(dāng)然你也可以用你電腦里面帶的字體庫
//BaseFont bfChinese = BaseFont.createFont("C:/WINDOWS/Fonts/SIMSUN.TTC,1",BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
//定義字體 注意在最新的包里面 顏色是封裝的
Font fontChinese8 = new Font(bfChinese, 10.0F, 0, new Color(59, 54, 54));
//生成pdf的第一個步驟:
//保存本地指定路徑
saveLocal();
document.open();
ByteArrayOutputStream ba = new ByteArrayOutputStream();
// PdfWriter writer = PdfWriter.getInstance(document, ba);
document.open();
//獲取此編譯的文件路徑
String path = this.getClass().getClassLoader().getResource("").getPath();
//獲取根路徑
String filePath = path.substring(1, path.length()-15);
//獲取圖片路徑 找到你需要往pdf上生成的圖片
//這里根據(jù)自己的獲取的路徑寫 只要找到圖片位置就可以
String picPath = filePath +"\\WebContent" +"\\images\\";
//往PDF中添加段落
Paragraph pHeader = new Paragraph();
pHeader.add(new Paragraph(" 你要生成文字寫這里", new Font(bfChinese, 8.0F, 1)));
//pHeader.add(new Paragraph("文字", 字體 可以自己寫 也可以用fontChinese8 之前定義好的 );
document.add(pHeader);//在文檔中加入你寫的內(nèi)容
//獲取圖片
Image img2 = Image.getInstance(picPath +"ccf-stamp-new.png");
//定義圖片在文檔中顯示的絕對位置
img2.scaleAbsolute(137.0F, 140.0F);
img2.setAbsolutePosition(330.0F, 37.0F);
//將圖片添加到文檔中
document.add(img2);
//關(guān)閉文檔
document.close();
/*//設(shè)置文檔保存的文件名
response.setHeader("Content-
disposition", "attachment;filename=\""+ new String(("CCF會員資格確認(rèn)
函.pdf").getBytes("GBK"),"ISO-8859-1") + "\"");
//設(shè)置類型
response.setContentType("application/pdf");
response.setContentLength(ba.size());
ServletOutputStream out = response.getOutputStream();
ba.writeTo(out);
out.flush();*/
}
public static void main(String[]args) throws DocumentException, IOException{
createPdf pdf= new createPdf();
pdf.getPDFdemo();
}
//指定一個文件進(jìn)行保存 這里吧文件保存到D盤的text.pdf
public void saveLocal() throws IOException, DocumentException{
//直接生成PDF 制定生成到D盤test.pdf
File file = new File("D:\\text2.pdf");
file.createNewFile();
PdfWriter.getInstance(document, new FileOutputStream(file));
}
}
具體步驟如下:
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.print.Doc;
import javax.print.DocFlavor;
import javax.print.DocPrintJob;
import javax.print.PrintService;
import javax.print.PrintServiceLookup;
import javax.print.SimpleDoc;
import javax.print.attribute.DocAttributeSet;
import javax.print.attribute.HashDocAttributeSet;
import javax.print.attribute.HashPrintRequestAttributeSet;
import javax.swing.JFileChooser;
public class PrintDemo {
public static void main(String[] args) {
JFileChooser fileChooser = new JFileChooser(); // 創(chuàng)建打印作業(yè)
File file = new File("f:/111.txt"); // 獲取選擇的文件
// 構(gòu)建打印請求屬性集
HashPrintRequestAttributeSet pras = new HashPrintRequestAttributeSet();
// 設(shè)置打印格式,因為未確定類型,所以選擇autosense
DocFlavor flavor = DocFlavor.INPUT_STREAM.AUTOSENSE;
// 定位默認(rèn)的打印服務(wù)
PrintService defaultService = PrintServiceLookup.lookupDefaultPrintService();
InputStream fis = null;
try {
DocPrintJob job = defaultService.createPrintJob(); // 創(chuàng)建打印作業(yè)
fis = new FileInputStream(file); // 構(gòu)造待打印的文件流
DocAttributeSet das = new HashDocAttributeSet();
Doc doc = new SimpleDoc(fis, flavor, das);
job.print(doc, pras);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}