思路分析
靜默打印:在頁面上點擊打印按鈕,不需要選擇預覽,也不需要選擇打印機,直接向打印機發送打印任務。
潤乾現有方法
潤乾報表目前網頁端支持三種打印方式:applet打印、flash打印、PDF打印
- applet打印
支持靜默打印,但是僅僅在IE流程中可以使用applet打印,侷限性太大。而且瀏覽器的配置較複雜,且不能輕易升級jar的版本,如果升級則打印失效。 - flash打印
不支持靜默打印,谷歌瀏覽器將於2020年末停止支持flash。 - PDF打印
打印效果很好,但是依然不支持靜默打印。
新方案
-
思路借鑑:
瀏覽器實現靜默打印的難點在於瀏覽器無法獲取打印機的相關配置,探索階段瞭解了lodop軟件,它是通過插件來實現打印,原理類似在本地安裝一個windows NT服務,然後html頁面將需要打印的內容發送到NT服務,再由服務去調用打印執行打印任務。 -
受到上面的思路啓發,我在想我們是不是也可以做成這樣的:用戶在前臺點擊打印按鈕,我們將需要打印的數據記錄到數據庫打印任務中(字段包括打印機名稱、打印報表名稱、打印參數等),然後再寫一個【客戶端】運行在服務器上,定時查詢打印任務去調用打印機進行打印。這個方案適用於我們當前的項目,項目是一個倉庫軟件位於局域網中。
具體實現
下面展示客戶端的主要代碼
。
- 主要打印代碼
public class RaqPrint {
static final Logger logger = Logger.getLogger(RaqPrint.class);
/**
* TODO 執行單個打印任務
* @param exepath
* @param printTask
* @author kaixin
* @throws Throwable
*/
public static void ChildPrint(String exepath, Map<String, String> printTask) throws Throwable {
// 如果是打印模板不爲空
if (printTask.get("printtemplate") != null && !"".equals(printTask.get("printtemplate"))) {
// 1.設置潤乾報表授權
setRaqLicense();
// 2.計算報表
IReport report = CalculateIReport.getReport(exepath, printTask);
// 3.執行打印任務;查詢該機器是否配置有該打印機
logger.info("打印機名稱:" + printTask.get("printer"));
if (PrintUtil.getPrinterNames().contains(printTask.get("printer"))) {
ReportUtils3.print(report, false, printTask.get("printer"));
// 返回結果
logger.info(printTask.toString()+"打印任務下發成功");
} else {
logger.error("找不到打印機:" + printTask.get("printer") + "。。。。。");
}
}
}
/**
* 內部的授權文件
* @throws Exception
*/
public static void setRaqLicense() throws Exception {
InputStream lis = ClassLoader.getSystemResourceAsStream("reportLicense20190531.xml");
Sequence.readLicense(Sequence.P_RPT, lis);
}
}
注:潤乾報表測試版授權可在
潤乾試用產品授權下載
2. 計算報表
// An highlighted block
/**
* TODO 計算並返回報表
*
* @author kaixin
*
*/
public class CalculateIReport {
static IConnectionFactory connFactory = new ConnFactory(); // 構造數據源工廠類,一般爲自定義類
static final Logger logger = Logger.getLogger(CalculateIReport.class);
/**
* @title: getReport
* @Description: 運算報表並返回報表啊
* @param path 當前運行路徑
* @param reportParams 報表參數以及報表名稱以及打印機等信息
* @throws Exception
* @return
*/
public static IReport getReport(String exepath, Map<String, String> reportParams) throws Exception {
String templateAddress = exepath + "/PrintTemplate/" + reportParams.get("printtemplate") + ".rpx";
//創建報表
ReportDefine rd = (ReportDefine) ReportUtils.read(templateAddress);
System.out.println(templateAddress);
//創建報表運行環境
Context cxt = new Context();
//設置數據源工廠
cxt.setConnectionFactory("ds1", connFactory);
//獲取報表所需要參數名稱
ParamMetaData pmd = rd.getParamMetaData();
//獲取print task 任務給定的參數集合
String paramOrMocrName[] = reportParams.get("templateparams").split(",");
Map<String, String> paramOrMocrNameMap = new HashMap<String, String>();
for (int i = 0; i < paramOrMocrName.length; i++) {
String key = paramOrMocrName[i].split("=")[0];
String value = paramOrMocrName[i].split("=")[1];
paramOrMocrNameMap.put(key, value);
}
logger.info("打印任務id:" + reportParams.get("id") + "任務參數如下"
+ paramOrMocrNameMap);
//給報表參數賦值
if (pmd != null) {
for (int i = 0, count = pmd.getParamCount(); i < count; i++) { // 講究優化的寫法
String param = pmd.getParam(i).getParamName(); // 獲取參數名
cxt.setParamValue(param, paramOrMocrNameMap.get(param)); // 設參數值
}
}
// 構造報表引擎
Engine engine = new Engine(rd, cxt);
// 運算報表
IReport iReport = engine.calc();
return iReport;
}
}
- 執行打印
// An highlighted block
import javax.swing.JFrame;
import com.raqsoft.report.control.PrintFrame;
import com.raqsoft.report.usermodel.IReport;
public class ReportUtils3 {
public static void print(final IReport report, final boolean needSelectPrinter, String printerName)
throws Throwable , Exception{
final PrintFrame frame = new PrintFrame(report, (JFrame) null);
frame.setModal(false);
frame.setLocation(-1000, -1000);
//判斷是否需要指定打印機名稱
if (!needSelectPrinter) {
frame.setPrinterName(printerName);
}
frame.show();
frame.directPrint(needSelectPrinter);
}
}
- 獲取本機打印機列表
import java.util.ArrayList;
import java.util.List;
import javax.print.DocFlavor;
import javax.print.PrintService;
import javax.print.PrintServiceLookup;
import javax.print.attribute.HashPrintRequestAttributeSet;
import javax.print.attribute.PrintRequestAttributeSet;
import org.apache.log4j.Logger;
public class PrintUtil {
static final Logger logger = Logger.getLogger(PrintUtil.class);
/**
* @title: getPrinterNames
* @Description: 獲取打印機列表
* @return List<String>
* @author kaixin
*/
public static List<String> getPrinterNames() throws Exception{
PrintRequestAttributeSet pras = new HashPrintRequestAttributeSet();
DocFlavor flavor = DocFlavor.BYTE_ARRAY.AUTOSENSE;
List<String> printNames = new ArrayList<String>();
//可用的打印機列表(字符串數組)
PrintService printService[] = PrintServiceLookup.lookupPrintServices(flavor, pras);
//當前默認打印機
for (int i = 0; i < printService.length; i++) {
String serviceName = printService[i].getName();
printNames.add(serviceName);
}
return printNames;
}
}
末尾
以上是自己探索的一些拐着彎實現的靜默打印,不適用大多數互聯網場景,只適用於部分特定場景。
如果小夥伴有什麼好的建議,歡迎評論告訴小弟。
後記
爲了防止用戶不慎關閉或者多開,目前在考慮有沒有可能實現成網頁版本,用tomcat的manager去管理
類似:在前臺點擊打印按鈕,然後服務器後臺彈出Jframe(潤乾打印的PrintFrame是實現了Jframe的),然後調用服務器配置的打印機進行打印。
已經實現點擊startup.bat啓動的時候,可以彈出Jframe彈窗。但是通過service.bat把tomcat寫成一個NT服務的時候就不能彈出彈窗了。
看到一種方法,打開服務的屬性,選中【允許服務與桌面交互】,重啓服務即可。不知道是否在win10下可行,有機會試一下。