/*******************************************************************************
* Copyright (c) 2016 Weasis Team and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Nicolas Roduit - initial API and implementation
*******************************************************************************/
package org.weasis.core.ui.util;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import javax.print.DocFlavor;
import javax.print.DocPrintJob;
import javax.print.PrintService;
import javax.print.ServiceUIFactory;
import javax.print.attribute.Attribute;
import javax.print.attribute.AttributeSet;
import javax.print.attribute.PrintServiceAttribute;
import javax.print.attribute.PrintServiceAttributeSet;
import javax.print.attribute.standard.PrinterIsAcceptingJobs;
import javax.print.event.PrintServiceAttributeListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.weasis.core.api.service.AuditLog;
/**
* ForcedAcceptPrintService is a PrintService wrapper class that allows printing to be attempted even if Java thinks the
* 'Printer is not accepting job'. It is recommended to use this after prompting the user, so they will see that the
* printer is reporting as offline and can choose to "force it" to try printing anyway.
* <p/>
* This hack gets around annoying 'Printer is not accepting job' errors in Java 5/6 that don't occur in Java 1.4. This
* was enough of a problem for our 1000+ users that it was the sole reason we could not move the product up from Java
* 1.4. Hence, the hack was invented as we had the problem with users whose printers were clearly online and they could
* print from any non-Java application.
* <p/>
* Turns out this hack is also useful for printing to the latest inkjet printers that have chips in the cartridges and
* stay in a 'No ink' status once empty. This is presumably to cause an inconvenience for cartridge refillers, but we're
* the ones who get the support calls from users that printing is not working, so it's an inconvenience to everyone
* except the printer manufacturer.
* <p/>
*/
public class ForcedAcceptPrintService implements PrintService {
private static final Logger LOGGER = LoggerFactory.getLogger(ForcedAcceptPrintService.class);
private final PrinterJob thePrintJob;
private final PrintService delegate;
/**
* Private constructor as this only works as a one-shot per print attempt. Use the static method above to hack a
* PrintJob, then tell it to print. The hack is gone by the time printing occurs and this instance will be garbage
* collected due to having no other references once the PrintJob is back to its original state.
*
* @param printJob
* the print job to affect
*/
private ForcedAcceptPrintService(PrinterJob printJob) {
this.thePrintJob = printJob;
this.delegate = printJob.getPrintService();
try {
thePrintJob.setPrintService(this);
// replace the private PrintService field on the PrintJob instance with a reference
// to our replacement PrintService so that we can intercept calls to getAttributes().
// it is expected that the first thing the PrintJob will do is check it's PrintService's
// PrinterIsAcceptingJobs attribute, at which point we'll force it to think it is accepting
// jobs and restore the PrintService to the original instance to get out of the way.
// The only real requirement is that the PrintJob does not cast the PrintService
// to it's expected type until after it has checked the PrinterIsAcceptingJobs
// attribute.
} catch (PrinterException e) {
AuditLog.logError(LOGGER, e, "Set Print Service"); //$NON-NLS-1$
}
}
/**
* Tweak the PrintJob to think this class is it's PrintService, long enough to override the PrinterIsAcceptingJobs
* attribute. If it doesn't work out or the printer really is offline then it's no worse than if this hack was not
* used.
*
* @param printJob
* the print job to affect
*/
public static void setupPrintJob(PrinterJob printJob) {
new ForcedAcceptPrintService(printJob);
}
/** Restore the PrintJob's PrintService to what it was originally. */
private void restoreServiceReference() {
try {
thePrintJob.setPrintService(delegate);
} catch (PrinterException e) {
AuditLog.logError(LOGGER, e, "Restore Print Service"); //$NON-NLS-1$
}
}
/**
*
* getAttribute is the one PrintService method we want to intercept to override the
*
* PrinterIsAcceptingJobs attribute.
*/
@Override
public <T extends PrintServiceAttribute> T getAttribute(Class<T> category) {
if (category.equals(PrinterIsAcceptingJobs.class)) {
// once we've overridden the return value for the PrinterIsAcceptingJobs attribute we're done.
// put the PrintJob's PrintService back to what it was.
restoreServiceReference();
return (T) PrinterIsAcceptingJobs.ACCEPTING_JOBS;
}
return delegate.getAttribute(category);
}
@Override
public DocPrintJob createPrintJob() {
return delegate.createPrintJob();
}
@Override
public void addPrintServiceAttributeListener(PrintServiceAttributeListener listener) {
delegate.addPrintServiceAttributeListener(listener);
}
@Override
public PrintServiceAttributeSet getAttributes() {
return delegate.getAttributes();
}
@Override
public Object getDefaultAttributeValue(Class<? extends Attribute> category) {
return delegate.getDefaultAttributeValue(category);
}
@Override
public String getName() {
return delegate.getName();
}
@Override
public ServiceUIFactory getServiceUIFactory() {
return delegate.getServiceUIFactory();
}
@Override
public Class<?>[] getSupportedAttributeCategories() {
return delegate.getSupportedAttributeCategories();
}
@Override
public Object getSupportedAttributeValues(Class<? extends Attribute> category, DocFlavor flavor,
AttributeSet attributes) {
return delegate.getSupportedAttributeValues(category, flavor, attributes);
}
@Override
public DocFlavor[] getSupportedDocFlavors() {
return delegate.getSupportedDocFlavors();
}
@Override
public AttributeSet getUnsupportedAttributes(DocFlavor flavor, AttributeSet attributes) {
return delegate.getUnsupportedAttributes(flavor, attributes);
}
@Override
public boolean isAttributeCategorySupported(Class<? extends Attribute> category) {
return delegate.isAttributeCategorySupported(category);
}
@Override
public boolean isAttributeValueSupported(Attribute attrval, DocFlavor flavor, AttributeSet attributes) {
return delegate.isAttributeValueSupported(attrval, flavor, attributes);
}
@Override
public boolean isDocFlavorSupported(DocFlavor flavor) {
return delegate.isDocFlavorSupported(flavor);
}
@Override
public void removePrintServiceAttributeListener(PrintServiceAttributeListener listener) {
delegate.removePrintServiceAttributeListener(listener);
}
}