Files
quality_recticel/old code/tray/src/qz/printer/action/PrintHTML.java
Quality System Admin c96039542d updated control access
2025-10-16 02:36:32 +03:00

415 lines
17 KiB
Java
Executable File

/**
* @author Tres Finocchiaro
*
* Copyright (C) 2016 Tres Finocchiaro, QZ Industries, LLC
*
* LGPL 2.1 This is free software. This software and source code are released under
* the "LGPL 2.1 License". A copy of this license should be distributed with
* this software. http://www.gnu.org/licenses/lgpl-2.1.html
*/
package qz.printer.action;
import com.sun.javafx.print.PrintHelper;
import com.sun.javafx.print.Units;
import javafx.print.*;
import org.apache.commons.io.IOUtils;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import qz.common.Constants;
import qz.printer.PrintOptions;
import qz.printer.PrintOutput;
import qz.printer.action.html.WebApp;
import qz.printer.action.html.WebAppModel;
import qz.utils.PrintingUtilities;
import javax.print.attribute.PrintRequestAttributeSet;
import javax.print.attribute.standard.Copies;
import javax.print.attribute.standard.CopiesSupported;
import javax.print.attribute.standard.Sides;
import javax.swing.*;
import java.awt.*;
import java.awt.print.PageFormat;
import java.awt.print.PrinterException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class PrintHTML extends PrintImage implements PrintProcessor {
private static final Logger log = LogManager.getLogger(PrintHTML.class);
private List<WebAppModel> models;
private JLabel legacyLabel = null;
public PrintHTML() {
super();
models = new ArrayList<>();
}
@Override
public PrintingUtilities.Format getFormat() {
return PrintingUtilities.Format.HTML;
}
@Override
public void parseData(JSONArray printData, PrintOptions options) throws JSONException, UnsupportedOperationException {
try {
PrintOptions.Pixel pxlOpts = options.getPixelOptions();
if (!pxlOpts.isLegacy()) {
WebApp.initialize();
}
for(int i = 0; i < printData.length(); i++) {
JSONObject data = printData.getJSONObject(i);
PrintingUtilities.Flavor flavor = PrintingUtilities.Flavor.parse(data, PrintingUtilities.Flavor.FILE);
String source;
switch(flavor) {
case FILE:
case PLAIN:
// We'll toggle between 'plain' and 'file' when we construct WebAppModel
source = data.getString("data");
break;
default:
source = new String(flavor.read(data.getString("data")), StandardCharsets.UTF_8);
}
double pageZoom = (pxlOpts.getDensity() * pxlOpts.getUnits().as1Inch()) / 72.0;
if (pageZoom <= 1) { pageZoom = 1; }
double pageWidth = 0;
double pageHeight = 0;
double convertFactor = (72.0 / pxlOpts.getUnits().as1Inch());
boolean renderFromHeight = Arrays.asList(PrintOptions.Orientation.LANDSCAPE,
PrintOptions.Orientation.REVERSE_LANDSCAPE).contains(pxlOpts.getOrientation());
if (pxlOpts.getSize() != null) {
if (!renderFromHeight) {
pageWidth = pxlOpts.getSize().getWidth() * convertFactor;
} else {
pageWidth = pxlOpts.getSize().getHeight() * convertFactor;
}
} else if (options.getDefaultOptions().getPageSize() != null) {
if (!renderFromHeight) {
pageWidth = options.getDefaultOptions().getPageSize().getWidth();
} else {
pageWidth = options.getDefaultOptions().getPageSize().getHeight();
}
}
if (pxlOpts.getMargins() != null) {
PrintOptions.Margins margins = pxlOpts.getMargins();
if (!renderFromHeight || pxlOpts.isRasterize()) {
pageWidth -= (margins.left() + margins.right()) * convertFactor;
} else {
pageWidth -= (margins.top() + margins.bottom()) * convertFactor; //due to vector margin matching
}
}
if (!data.isNull("options")) {
JSONObject dataOpt = data.getJSONObject("options");
if (!dataOpt.isNull("pageWidth") && dataOpt.optDouble("pageWidth") > 0) {
pageWidth = dataOpt.optDouble("pageWidth") * convertFactor;
}
if (!dataOpt.isNull("pageHeight") && dataOpt.optDouble("pageHeight") > 0) {
pageHeight = dataOpt.optDouble("pageHeight") * convertFactor;
}
}
models.add(new WebAppModel(source, (flavor != PrintingUtilities.Flavor.FILE), pageWidth, pageHeight, pxlOpts.isScaleContent(), pageZoom));
}
log.debug("Parsed {} html records", models.size());
}
catch(IOException e) {
throw new UnsupportedOperationException("Unable to start JavaFX service", e);
}
catch(NoClassDefFoundError e) {
throw new UnsupportedOperationException("JavaFX libraries not found", e);
}
}
@Override
public void print(PrintOutput output, PrintOptions options) throws PrinterException {
if (options.getPixelOptions().isLegacy()) {
printLegacy(output, options);
} else if (options.getPixelOptions().isRasterize()) {
//grab a snapshot of the pages for PrintImage instead of printing directly
for(WebAppModel model : models) {
try { images.add(WebApp.raster(model)); }
catch(Throwable t) {
if (model.getZoom() > 1 && t instanceof IllegalArgumentException) {
//probably a unrecognized image loader error, try at default zoom
try {
log.warn("Capture failed with increased zoom, attempting with default value");
model.setZoom(1);
images.add(WebApp.raster(model));
}
catch(Throwable tt) {
throw new PrinterException(tt.getMessage());
}
} else {
throw new PrinterException(t.getMessage());
}
}
}
super.print(output, options);
} else {
Printer fxPrinter = null;
for(Printer p : Printer.getAllPrinters()) {
if (p.getName().equals(output.getPrintService().getName())) {
fxPrinter = p;
break;
}
}
if (fxPrinter == null) {
throw new PrinterException("Cannot find printer under the JavaFX libraries");
}
PrinterJob job = PrinterJob.createPrinterJob(fxPrinter);
// apply option settings
PrintOptions.Pixel pxlOpts = options.getPixelOptions();
JobSettings settings = job.getJobSettings();
settings.setJobName(pxlOpts.getJobName(Constants.HTML_PRINT));
settings.setPrintQuality(PrintQuality.HIGH);
// If colortype is default, leave printColor blank. The system's printer settings will be used instead.
if (pxlOpts.getColorType() != PrintOptions.ColorType.DEFAULT) {
settings.setPrintColor(getColor(pxlOpts));
}
if (pxlOpts.getDuplex() == Sides.DUPLEX || pxlOpts.getDuplex() == Sides.TWO_SIDED_LONG_EDGE) {
settings.setPrintSides(PrintSides.DUPLEX);
}
if (pxlOpts.getDuplex() == Sides.TUMBLE || pxlOpts.getDuplex() == Sides.TWO_SIDED_SHORT_EDGE) {
settings.setPrintSides(PrintSides.TUMBLE);
}
if (pxlOpts.getPrinterTray() != null) {
PaperSource tray = findFXTray(fxPrinter.getPrinterAttributes().getSupportedPaperSources(), pxlOpts.getPrinterTray());
if (tray != null) {
settings.setPaperSource(tray);
}
}
if (pxlOpts.getDensity() > 0) {
settings.setPrintResolution(PrintHelper.createPrintResolution((int)pxlOpts.getDensity(), (int)pxlOpts.getDensity()));
}
Paper paper;
if (pxlOpts.getSize() != null && pxlOpts.getSize().getWidth() > 0 && pxlOpts.getSize().getHeight() > 0) {
double convert = 1;
Units units = getUnits(pxlOpts);
if (units == null) {
convert = 10; //need to adjust from cm to mm only for DPCM sizes
units = Units.MM;
}
paper = PrintHelper.createPaper("Custom", pxlOpts.getSize().getWidth() * convert, pxlOpts.getSize().getHeight() * convert, units);
} else {
PrintOptions.Size paperSize = options.getDefaultOptions().getPageSize();
paper = PrintHelper.createPaper("Default", paperSize.getWidth(), paperSize.getHeight(), Units.POINT);
}
PageOrientation orient = fxPrinter.getPrinterAttributes().getDefaultPageOrientation();
if (pxlOpts.getOrientation() != null) {
orient = getOrientation(pxlOpts);
}
try {
PageLayout layout;
PrintOptions.Margins m = pxlOpts.getMargins();
if (m != null) {
//force access to the page layout constructor as the adjusted margins on small sizes are wildly inaccurate
Constructor<PageLayout> plCon = PageLayout.class.getDeclaredConstructor(Paper.class, PageOrientation.class, double.class, double.class, double.class, double.class);
plCon.setAccessible(true);
//margins defined as pnt (1/72nds)
double asPnt = pxlOpts.getUnits().toInches() * 72;
if (orient == PageOrientation.PORTRAIT || orient == PageOrientation.REVERSE_PORTRAIT) {
layout = plCon.newInstance(paper, orient, m.left() * asPnt, m.right() * asPnt, m.top() * asPnt, m.bottom() * asPnt);
} else {
//rotate margins to match raster prints
layout = plCon.newInstance(paper, orient, m.top() * asPnt, m.bottom() * asPnt, m.right() * asPnt, m.left() * asPnt);
}
} else {
//if margins are not provided, use default paper margins
PageLayout valid = fxPrinter.getDefaultPageLayout();
layout = fxPrinter.createPageLayout(paper, orient, valid.getLeftMargin(), valid.getRightMargin(), valid.getTopMargin(), valid.getBottomMargin());
}
//force our layout as the default to avoid default-margin exceptions on small paper sizes
Field field = fxPrinter.getClass().getDeclaredField("defPageLayout");
field.setAccessible(true);
field.set(fxPrinter, layout);
settings.setPageLayout(layout);
}
catch(Exception e) {
log.error("Failed to set custom layout", e);
}
settings.setCopies(pxlOpts.getCopies());
log.trace("{}", settings.toString());
//javaFX lies about this value, so pull from original print service
CopiesSupported cSupport = (CopiesSupported)output.getPrintService()
.getSupportedAttributeValues(Copies.class, output.getPrintService().getSupportedDocFlavors()[0], null);
try {
if (cSupport != null && cSupport.contains(pxlOpts.getCopies())) {
for(WebAppModel model : models) {
WebApp.print(job, model);
}
} else {
settings.setCopies(1); //manually handle copies if they are not supported
for(int i = 0; i < pxlOpts.getCopies(); i++) {
for(WebAppModel model : models) {
WebApp.print(job, model);
}
}
}
}
catch(Throwable t) {
job.cancelJob();
throw new PrinterException(t.getMessage());
}
//send pending prints
job.endJob();
}
}
private void printLegacy(PrintOutput output, PrintOptions options) throws PrinterException {
PrintOptions.Pixel pxlOpts = options.getPixelOptions();
java.awt.print.PrinterJob job = java.awt.print.PrinterJob.getPrinterJob();
job.setPrintService(output.getPrintService());
PageFormat page = job.getPageFormat(null);
PrintRequestAttributeSet attributes = applyDefaultSettings(pxlOpts, page, output.getSupportedMedia());
//setup swing ui
JFrame legacyFrame = new JFrame(pxlOpts.getJobName(Constants.HTML_PRINT));
legacyFrame.setUndecorated(true);
legacyFrame.setLayout(new FlowLayout());
legacyFrame.setExtendedState(Frame.ICONIFIED);
legacyLabel = new JLabel();
legacyLabel.setOpaque(true);
legacyLabel.setBackground(Color.WHITE);
legacyLabel.setBorder(null);
legacyLabel.setDoubleBuffered(false);
legacyFrame.add(legacyLabel);
try {
for(WebAppModel model : models) {
if (model.isPlainText()) {
legacyLabel.setText(cleanHtmlContent(model.getSource()));
} else {
try(InputStream fis = new URL(model.getSource()).openStream()) {
String webPage = cleanHtmlContent(IOUtils.toString(fis, "UTF-8"));
legacyLabel.setText(webPage);
}
}
legacyFrame.pack();
legacyFrame.setVisible(true);
job.setPrintable(this);
printCopies(output, pxlOpts, job, attributes);
}
}
catch(Exception e) {
throw new PrinterException(e.getMessage());
}
finally {
legacyFrame.dispose();
}
}
private String cleanHtmlContent(String html) {
return html.replaceAll("^[\\s\\S]*<(HTML|html)\\b.*?>", "<html>");
}
@Override
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
if (legacyLabel == null) {
return super.print(graphics, pageFormat, pageIndex);
} else {
if (graphics == null) { throw new PrinterException("No graphics specified"); }
if (pageFormat == null) { throw new PrinterException("No page format specified"); }
if (pageIndex + 1 > models.size()) {
return NO_SUCH_PAGE;
}
log.trace("Requested page {} for printing", pageIndex);
Graphics2D graphics2D = (Graphics2D)graphics;
graphics2D.setRenderingHints(buildRenderingHints(dithering, interpolation));
graphics2D.translate(pageFormat.getImageableX(), pageFormat.getImageableY());
graphics2D.scale(pageFormat.getImageableWidth() / pageFormat.getWidth(), pageFormat.getImageableHeight() / pageFormat.getHeight());
legacyLabel.paint(graphics2D);
return PAGE_EXISTS;
}
}
@Override
public void cleanup() {
super.cleanup();
models.clear();
legacyLabel = null;
}
public static Units getUnits(PrintOptions.Pixel opts) {
switch(opts.getUnits()) {
case INCH:
return Units.INCH;
case MM:
return Units.MM;
default:
return null;
}
}
public static PageOrientation getOrientation(PrintOptions.Pixel opts) {
switch(opts.getOrientation()) {
case LANDSCAPE:
return PageOrientation.LANDSCAPE;
case REVERSE_LANDSCAPE:
return PageOrientation.REVERSE_LANDSCAPE;
case REVERSE_PORTRAIT:
return PageOrientation.REVERSE_PORTRAIT;
default:
return PageOrientation.PORTRAIT;
}
}
public static PrintColor getColor(PrintOptions.Pixel opts) {
switch(opts.getColorType()) {
case COLOR:
return PrintColor.COLOR;
default:
return PrintColor.MONOCHROME;
}
}
}