如何实现网站的自动打印



上下文:网站用户必须打印客户端(HTML/CSS)生成的一些页面(报告)。现在,他们需要一个功能,允许他们自动打印每个报告。

浏览器和服务器(Java)都可以访问打印机。

我的选项

1/使浏览器打印报告而无需用户操作(使用window.print())

->我认为这是不可能的,一个窗口会打开,用户必须验证

2/在服务器上启动浏览器,打印报告并关闭浏览器

->看起来相当复杂。有没有一种方法可以在不打开浏览器的情况下从Java生成HTML/CSS/javascript内容?

3/使用Java 生成和打印报告

->工作很容易,但我必须在客户端和服务器端生成报告。。。我想避免这种情况。如果这是唯一的解决方案,我可以在用java生成PDF时使用CSS吗?

其他/还有其他我没有想过的选择吗?

FYI:我们可以强制用户使用特定浏览器的特定版本

我为基于intranet的Web应用程序编写了一个Java servlet,它看起来像是在处理给定的打印机请求。

servlet可能会被清理一些,但本质上它使用了一个基于wkhtmltopdf构建的Java库来解析请求体中的HTML,然后它根据请求选项/方法做三件事之一:

  1. 以pdf格式下载到客户端
  2. 将pdf内容打印到网络打印机之一
  3. 将pdf预渲染到服务器上的文件中,以便稍后打印/下载

我最近也进行了打印,允许装订、双面打印、选择托盘等,请参阅print方法:

import java.awt.print.PrinterJob;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.print.DocFlavor;
import javax.print.PrintService;
import javax.print.attribute.standard.Media;
import javax.print.attribute.standard.MediaTray;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.woo.htmltopdf.HtmlToPdf;
import io.woo.htmltopdf.HtmlToPdfException;
import io.woo.htmltopdf.HtmlToPdfObject;
import io.woo.htmltopdf.PdfPageSize;
public class PDFServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private static final Logger myLog = LoggerFactory.getLogger(PDFServlet.class);
private static final String PDF_CONTENT = "application/pdf";
private static final String JSON_CONTENT = "application/json; charset=UTF-8";
private static final String DOWNLOAD_FILE = "attachment;filename=%s.pdf";
private static final String REQUEST_STATUS = "[{"success":%s}]";
private static final String ERROR_MSG = "Error with %s request: ";
private Map<String, File> myRenderMap = new HashMap<String, File>();
private File myRenderDir;
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
ServletContext servletContext = this.getServletContext();
String appRoot = servletContext.getRealPath("/");
myRenderDir = new File(appRoot + "render");
if (myRenderDir.exists()) 
{
try 
{
FileUtils.cleanDirectory(myRenderDir);
} 
catch (IOException e) 
{
myLog.error("Unable to clean render directory.", e);
}
}
else
{
myRenderDir.mkdirs();
}
myLog.debug("Initialized PDFServlet");
}
// downloads to client (or prints) a pre-rendered pdf
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException  {       
try
{
String resourceId = request.getParameter("resourceId");
String printer = request.getParameter("printer");
String tray = request.getParameter("tray");
String duplex = request.getParameter("duplex");
String staple = request.getParameter("staple");
if (resourceId == null || resourceId.equals("*") || resourceId.equals("resources"))
{
myLog.debug("Retrieving resources list for GET request: "
+ request.getRequestURI()
+ (request.getQueryString() != null ? "?" + request.getQueryString() : ""));
response.setContentType(JSON_CONTENT);
response.setStatus(200);
try (PrintWriter out = response.getWriter())
{
out.println(getResources());
}
}
else if (resourceId.equalsIgnoreCase("printers"))
{
myLog.debug("Retrieving printers list for GET request: "
+ request.getRequestURI()
+ (request.getQueryString() != null ? "?" + request.getQueryString() : ""));
response.setContentType(JSON_CONTENT);
response.setStatus(200);
try (PrintWriter out = response.getWriter())
{
out.println(getPrinters());
}
}
else if (myRenderMap.containsKey(resourceId))
{
myLog.debug("Retrieving pdf resource for GET request: "
+ request.getRequestURI()
+ (request.getQueryString() != null ? "?" + request.getQueryString() : ""));
while (myRenderMap.containsKey(resourceId) && myRenderMap.get(resourceId) == null)
{
Thread.sleep(100);
}
File file = myRenderMap.get(resourceId);
// print
if (printer != null)
{
myLog.debug("Printing existing resource: " + resourceId);
byte[] document = Files.readAllBytes(file.toPath());
boolean printed = print(document, printer, resourceId, tray, duplex, staple);
response.setContentType(JSON_CONTENT);
response.setStatus(printed ? 200 : 500);
try (PrintWriter out = response.getWriter())
{
out.println(String.format(REQUEST_STATUS, String.valueOf(printed)));
}
}
// download
else
{
myLog.debug("Downloading existing resource: " + resourceId);
try (InputStream in = FileUtils.openInputStream(file)) 
{
response.setContentType(PDF_CONTENT);
response.setHeader("Content-Disposition", String.format(DOWNLOAD_FILE, resourceId));
ServletOutputStream out = response.getOutputStream();
IOUtils.copy(in, out);
} 
catch (HtmlToPdfException e) 
{
myLog.error("HTML to PDF conversion error.", e);
}
}
}
else
{
myLog.debug("Resource does not exist for GET request: "
+ request.getRequestURI()
+ (request.getQueryString() != null ? "?" + request.getQueryString() : ""));
response.setContentType(JSON_CONTENT);
response.setStatus(500);
try (PrintWriter out = response.getWriter())
{
out.println(String.format(REQUEST_STATUS, "false"));
}
}
}
catch (Exception e)
{
String msg = String.format(ERROR_MSG, "GET") 
+ request.getRequestURI() 
+ (request.getQueryString() != null ? "?" + request.getQueryString() : "");
myLog.error(msg, e);
throw new ServletException(msg, e);
}
}
// downloads to client (or prints) a pdf that is created from the body content
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException  {
myLog.debug("Retrieving pdf resource for POST request: "
+ request.getRequestURI()
+ (request.getQueryString() != null ? "?" + request.getQueryString() : ""));
try
{
String resourceId = request.getParameter("resourceId");
String size = request.getParameter("size");
String printer = request.getParameter("printer");
String tray = request.getParameter("tray");
String duplex = request.getParameter("duplex");
String staple = request.getParameter("staple");
if (resourceId == null)
{
resourceId = request.getContextPath().substring(1);
}
// resource is currently being rendered
if (myRenderMap.containsKey(resourceId) && myRenderMap.get(resourceId) == null)
{
myLog.debug("Waiting for rendering: " + resourceId);
while (myRenderMap.containsKey(resourceId) && myRenderMap.get(resourceId) == null)
{
Thread.sleep(100);
}
}
// resource is not already rendered
if (!myRenderMap.containsKey(resourceId))
{
myLog.debug("Newly rendering resource: " + resourceId);
String[] content = IOUtils.toString(request.getReader()).split("\|", -1);   
HtmlToPdf html = create(content);
html.documentTitle(resourceId);
if (size != null)
{
html.pageSize(PdfPageSize.valueOf(size));
}
else
{
html.pageSize(PdfPageSize.Letter);
}
// print
if (printer != null)
{
myLog.debug("Printing new resource: " + resourceId);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
IOUtils.copy(html.convert(), baos);
byte[] document = baos.toByteArray();
boolean printed = print(document, printer, resourceId, tray, duplex, staple);
response.setContentType(JSON_CONTENT);
response.setStatus(printed ? 200 : 500);
try (PrintWriter out = response.getWriter())
{
out.println(String.format(REQUEST_STATUS, String.valueOf(printed)));
}
}
// download
else
{
myLog.debug("Downloading new resource: " + resourceId);
try (InputStream in = html.convert()) 
{
response.setContentType(PDF_CONTENT);
response.setHeader("Content-Disposition", String.format(DOWNLOAD_FILE, resourceId));
ServletOutputStream out = response.getOutputStream();
IOUtils.copy(in, out);
} 
catch (HtmlToPdfException e) 
{
myLog.error("HTML to PDF conversion error.", e);
}
}
}
// resource is already rendered (use file)
else
{
myLog.debug("Resource already exists: " + resourceId);
File file = myRenderMap.get(resourceId);
// print
if (printer != null)
{
myLog.debug("Printing existing resource: " + resourceId);
byte[] document = Files.readAllBytes(file.toPath());
boolean printed = print(document, printer, resourceId, tray, duplex, staple);
response.setContentType(JSON_CONTENT);
response.setStatus(printed ? 200 : 500);
try (PrintWriter out = response.getWriter())
{
out.println(String.format(REQUEST_STATUS, String.valueOf(printed)));
}
}
// download
else
{
myLog.debug("Downloading existing resource: " + resourceId);
try (InputStream in = FileUtils.openInputStream(file)) 
{
response.setContentType(PDF_CONTENT);
response.setHeader("Content-Disposition", String.format(DOWNLOAD_FILE, resourceId));
ServletOutputStream out = response.getOutputStream();
IOUtils.copy(in, out);
} 
catch (Exception e) 
{
myLog.error("Unable to download existing resource.", e);
}
}
}
}
catch (Exception e)
{
String msg = String.format(ERROR_MSG, "POST") 
+ request.getRequestURI() 
+ (request.getQueryString() != null ? "?" + request.getQueryString() : "");
myLog.error(msg, e);
throw new ServletException(msg, e);
}
}
// pre-renders a pdf that is created from the body content
@Override
public void doPut(HttpServletRequest request, HttpServletResponse response)
throws ServletException  {       
myLog.debug("Rendering pdf resource for PUT request: "
+ request.getRequestURI()
+ (request.getQueryString() != null ? "?" + request.getQueryString() : ""));
try
{
String resourceId = request.getParameter("resourceId");
String size = request.getParameter("size");
boolean created = false;
if (resourceId == null)
{
resourceId = request.getContextPath().substring(1);
}
if (!myRenderMap.containsKey(resourceId))
{
myRenderMap.put(resourceId, null);
String filePath = myRenderDir.getAbsolutePath() + "/" + resourceId + ".pdf";
String[] content = IOUtils.toString(request.getReader()).split("\|", -1);  
HtmlToPdf html = create(content);
html.documentTitle(resourceId);
if (size != null)
{
html.pageSize(PdfPageSize.valueOf(size));
}
else
{
html.pageSize(PdfPageSize.Letter);
}
created = html.convert(filePath);
if (created)
{
myRenderMap.put(resourceId, new File(filePath));
}
else
{
myRenderMap.remove(resourceId);
}
}
else
{
while (myRenderMap.get(resourceId) == null && myRenderMap.containsKey(resourceId))
{
Thread.sleep(100);
}
created = myRenderMap.containsKey(resourceId);
}
response.setContentType(JSON_CONTENT);
response.setStatus(created ? 200 : 500);
try (PrintWriter out = response.getWriter())
{
out.println(String.format(REQUEST_STATUS, String.valueOf(created)));
}
}
catch (Exception e)
{
String msg = String.format(ERROR_MSG, "PUT") 
+ request.getRequestURI() 
+ (request.getQueryString() != null ? "?" + request.getQueryString() : "");
myLog.error(msg, e);
throw new ServletException(msg, e);
}
}
// removes pre-rendered pdf from server files
@Override
public void doDelete(HttpServletRequest request, HttpServletResponse response)
throws ServletException  {    
myLog.debug("Removing pdf resource for DELETE request: "
+ request.getRequestURI()
+ (request.getQueryString() != null ? "?" + request.getQueryString() : ""));
try
{
String resourceId = request.getParameter("resourceId");
List<String> toRemove = new ArrayList<String>();
long now = Calendar.getInstance().getTimeInMillis();
for (String key : myRenderMap.keySet())
{
myLog.debug("Checking: " + key);
File file = myRenderMap.get(key);
if (file == null)
{
Thread.sleep(10000); // wait 10 seconds to see if it renders
}
if (file == null || // the file doesn't actually exist
now - file.lastModified() > (1000 * 60 * 30) || // delete anything older than 30 minutes
key.equals(resourceId)) // requested to delete
{
toRemove.add(key);
}
}
myLog.debug("Removing: " + Arrays.toString(toRemove.toArray()));
for (String key : toRemove)
{
myRenderMap.get(key).delete();
myRenderMap.remove(key);
}
response.setContentType(JSON_CONTENT);
response.setStatus(200);
try (PrintWriter out = response.getWriter())
{
out.println(String.format(REQUEST_STATUS, "true"));
}
}
catch (Exception e)
{
String msg = String.format(ERROR_MSG, "DELETE") 
+ request.getRequestURI() 
+ (request.getQueryString() != null ? "?" + request.getQueryString() : "");
myLog.error(msg, e);
throw new ServletException(msg, e);
}
}

private HtmlToPdf create(String[] content)
{
HtmlToPdf html = HtmlToPdf.create();
for (String document : content)
{
if (document.startsWith("<") && document.endsWith(">"))
{
html.object(HtmlToPdfObject.forHtml(document));
}
else
{
html.object(HtmlToPdfObject.forUrl(document));
}
}
return html;
}
private boolean print(byte[] document, String printerIpAddress, String title, String tray, String duplex, String staple)
{
boolean success = false;
List<byte[]> commands = new ArrayList<byte[]>();
if (title != null)
{
commands.add(("@PJL SET JOBNAME=" + title + "n").getBytes());
}
if (tray != null)
{
commands.add(("@PJL SET MEDIASOURCE=" + tray + "n").getBytes());
}
if (staple != null)
{ 
commands.add(("@PJL SET STAPLEOPTION=ONEn").getBytes());
}
if (duplex != null)
{
commands.add(("@PJL SET DUPLEX=ONn").getBytes());
if (duplex.equalsIgnoreCase("short"))
{
commands.add(("@PJL SET BINDING=SHORTEDGEn").getBytes());
}
else if (duplex.equalsIgnoreCase("long"))
{
commands.add(("@PJL SET BINDING=LONGEDGEn").getBytes());
}
}
try (Socket socket = new Socket(printerIpAddress, 9100))
{
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
out.write(27);
out.write("%-12345X@PJLn".getBytes());
for (byte[] command : commands)
{
out.write(command);
}
out.write("@PJL ENTER LANGUAGE=PDFn".getBytes());
out.write(document);
out.write(27);
out.write("%-12345X".getBytes());
out.flush();
out.close();
success = true;
}
catch (Exception e)
{
System.out.println(e);
}
return success;
}
private String getResources()
{
List<String> toRemove = new ArrayList<String>();
StringBuilder json = new StringBuilder("[");
long now = Calendar.getInstance().getTimeInMillis();
for (String key : myRenderMap.keySet())
{
File file = myRenderMap.get(key);
if (file != null)
{
if (now - file.lastModified() > (1000 * 60 * 30)) // delete anything older than 30 minutes
{
toRemove.add(key);
}
else
{
json.append("{"resourceId":"");
json.append(key);
json.append(""},");
}
}
}
if (json.length() > 1)
{
json.setLength(json.length() - 1);
}
json.append("]");
for (String key : toRemove)
{
myRenderMap.get(key).delete();
myRenderMap.remove(key);
}
return json.toString();
}
private String getPrinters()
{
StringBuilder json = new StringBuilder("[");
PrintService[] services = PrinterJob.lookupPrintServices();
DocFlavor flavor = DocFlavor.SERVICE_FORMATTED.PRINTABLE;
for (PrintService service : services)
{
json.append("{"printer":"");
json.append(service.getName());
json.append("","trays":[");
Object o = service.getSupportedAttributeValues(Media.class, flavor, null);
if (o != null && o.getClass().isArray())
{
for (Media media : (Media[]) o)
{
if (media instanceof MediaTray)
{
json.append(""");
json.append(media.toString());
json.append("",");
}
}
if (json.charAt(json.length() - 1) == ',')
{
json.setLength(json.length() - 1);
}
}
json.append("]},");
}
if (json.length() > 1)
{
json.setLength(json.length() - 1);
}
json.append("]");
return json.toString();
}
}

最新更新