8314070: javax.print: Support IPP output-bin attribute extension

Reviewed-by: psadhukhan, prr
This commit is contained in:
Alexander Scherbatiy 2024-06-04 03:12:40 +00:00
parent d230b30353
commit c7d2a5c1c4
25 changed files with 1479 additions and 3 deletions

View File

@ -44,6 +44,7 @@ import javax.print.attribute.standard.Media;
import javax.print.attribute.standard.MediaPrintableArea;
import javax.print.attribute.standard.MediaSize;
import javax.print.attribute.standard.MediaSizeName;
import javax.print.attribute.standard.OutputBin;
import javax.print.attribute.standard.PageRanges;
import javax.print.attribute.standard.Sides;
import javax.print.attribute.Attribute;
@ -70,6 +71,8 @@ public final class CPrinterJob extends RasterPrinterJob {
private String tray = null;
private String outputBin = null;
// This is the NSPrintInfo for this PrinterJob. Protect multi thread
// access to it. It is used by the pageDialog, jobDialog, and printLoop.
// This way the state of these items is shared across these calls.
@ -191,6 +194,8 @@ public final class CPrinterJob extends RasterPrinterJob {
tray = customTray.getChoiceName();
}
outputBin = getOutputBinValue(attributes.get(OutputBin.class));
PageRanges pageRangesAttr = (PageRanges)attributes.get(PageRanges.class);
if (isSupportedValue(pageRangesAttr, attributes)) {
SunPageSelection rangeSelect = (SunPageSelection)attributes.get(SunPageSelection.class);
@ -658,6 +663,41 @@ public final class CPrinterJob extends RasterPrinterJob {
return tray;
}
private String getOutputBin() {
return outputBin;
}
private void setOutputBin(String outputBinName) {
OutputBin outputBin = toOutputBin(outputBinName);
if (outputBin != null) {
attributes.add(outputBin);
}
}
private OutputBin toOutputBin(String outputBinName) {
PrintService ps = getPrintService();
if (ps == null) {
return null;
}
OutputBin[] supportedBins = (OutputBin[]) ps.getSupportedAttributeValues(OutputBin.class, null, null);
if (supportedBins == null || supportedBins.length == 0) {
return null;
}
for (OutputBin bin : supportedBins) {
if (bin instanceof CustomOutputBin customBin){
if (customBin.getChoiceName().equals(outputBinName)) {
return customBin;
}
}
}
return null;
}
private void setPrinterServiceFromNative(String printerName) {
// This is called from the native side.
PrintService[] services = PrintServiceLookup.lookupPrintServices(DocFlavor.SERVICE_FORMATTED.PAGEABLE, null);

View File

@ -383,6 +383,7 @@ static void nsPrintInfoToJavaPrinterJob(JNIEnv* env, NSPrintInfo* src, jobject d
DECLARE_METHOD(jm_setPrintToFile, sjc_CPrinterJob, "setPrintToFile", "(Z)V");
DECLARE_METHOD(jm_setDestinationFile, sjc_CPrinterJob, "setDestinationFile", "(Ljava/lang/String;)V");
DECLARE_METHOD(jm_setSides, sjc_CPrinterJob, "setSides", "(I)V");
DECLARE_METHOD(jm_setOutputBin, sjc_CPrinterJob, "setOutputBin", "(Ljava/lang/String;)V");
// get the selected printer's name, and set the appropriate PrintService on the Java side
NSString *name = [[src printer] name];
@ -449,6 +450,13 @@ static void nsPrintInfoToJavaPrinterJob(JNIEnv* env, NSPrintInfo* src, jobject d
(*env)->CallVoidMethod(env, dstPrinterJob, jm_setSides, sides); // AWT_THREADING Safe (known object)
CHECK_EXCEPTION();
}
NSString* outputBin = [[src printSettings] objectForKey:@"OutputBin"];
if (outputBin != nil) {
jstring outputBinName = NSStringToJavaString(env, outputBin);
(*env)->CallVoidMethod(env, dstPrinterJob, jm_setOutputBin, outputBinName);
CHECK_EXCEPTION();
}
}
}
@ -468,6 +476,7 @@ static void javaPrinterJobToNSPrintInfo(JNIEnv* env, jobject srcPrinterJob, jobj
DECLARE_METHOD(jm_getPageFormat, sjc_CPrinterJob, "getPageFormatFromAttributes", "()Ljava/awt/print/PageFormat;");
DECLARE_METHOD(jm_getDestinationFile, sjc_CPrinterJob, "getDestinationFile", "()Ljava/lang/String;");
DECLARE_METHOD(jm_getSides, sjc_CPrinterJob, "getSides", "()I");
DECLARE_METHOD(jm_getOutputBin, sjc_CPrinterJob, "getOutputBin", "()Ljava/lang/String;");
NSMutableDictionary* printingDictionary = [dst dictionary];
@ -538,6 +547,15 @@ static void javaPrinterJobToNSPrintInfo(JNIEnv* env, jobject srcPrinterJob, jobj
[dst updateFromPMPrintSettings];
}
}
jobject outputBin = (*env)->CallObjectMethod(env, srcPrinterJob, jm_getOutputBin);
CHECK_EXCEPTION();
if (outputBin != NULL) {
NSString *nsOutputBinStr = JavaStringToNSString(env, outputBin);
if (nsOutputBinStr != nil) {
[[dst printSettings] setObject:nsOutputBinStr forKey:@"OutputBin"];
}
}
}
/*

View File

@ -0,0 +1,201 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, BELLSOFT. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.print.attribute.standard;
import java.io.Serial;
import javax.print.attribute.Attribute;
import javax.print.attribute.DocAttribute;
import javax.print.attribute.EnumSyntax;
import javax.print.attribute.PrintJobAttribute;
import javax.print.attribute.PrintRequestAttribute;
import sun.print.CustomOutputBin;
/**
* Class {@code OutputBin} is a printing attribute class, an enumeration, that
* specifies the output bin for the job.
* <p>
* Class {@code OutputBin} declares keywords for standard output bin kind values.
* <p>
* <b>IPP Compatibility:</b> This attribute is not an IPP 1.1 attribute; it is
* an attribute in the "output-bin" attribute extension
* (<a href="https://ftp.pwg.org/pub/pwg/candidates/cs-ippoutputbin10-20010207-5100.2.pdf">
* PDF</a>) of IPP 1.1. The category name returned by {@code getName()} is the
* IPP attribute name. The enumeration's integer value is the IPP enum value.
* The {@code toString()} method returns the IPP string representation of the
* attribute value.
*/
public sealed class OutputBin extends EnumSyntax implements PrintRequestAttribute, PrintJobAttribute permits CustomOutputBin {
@Serial
private static final long serialVersionUID = -3718893309873137109L;
/**
* The top output bin in the printer.
*/
public static final OutputBin TOP = new OutputBin(0);
/**
* The middle output bin in the printer.
*/
public static final OutputBin MIDDLE = new OutputBin(1);
/**
* The bottom output bin in the printer.
*/
public static final OutputBin BOTTOM = new OutputBin(2);
/**
* The side output bin in the printer.
*/
public static final OutputBin SIDE = new OutputBin(3);
/**
* The left output bin in the printer.
*/
public static final OutputBin LEFT = new OutputBin(4);
/**
* The right output bin in the printer.
*/
public static final OutputBin RIGHT = new OutputBin(5);
/**
* The center output bin in the printer.
*/
public static final OutputBin CENTER = new OutputBin(6);
/**
* The rear output bin in the printer.
*/
public static final OutputBin REAR = new OutputBin(7);
/**
* The face up output bin in the printer.
*/
public static final OutputBin FACE_UP = new OutputBin(8);
/**
* The face down output bin in the printer.
*/
public static final OutputBin FACE_DOWN = new OutputBin(9);
/**
* The large-capacity output bin in the printer.
*/
public static final OutputBin LARGE_CAPACITY = new OutputBin(10);
/**
* Construct a new output bin enumeration value with the given integer
* value.
*
* @param value Integer value
*/
protected OutputBin(int value) {
super(value);
}
/**
* The string table for class {@code OutputBin}.
*/
private static final String[] myStringTable = {
"top",
"middle",
"bottom",
"side",
"left",
"right",
"center",
"rear",
"face-up",
"face-down",
"large-capacity",
};
/**
* The enumeration value table for class {@code OutputBin}.
*/
private static final OutputBin[] myEnumValueTable = {
TOP,
MIDDLE,
BOTTOM,
SIDE,
LEFT,
RIGHT,
CENTER,
REAR,
FACE_UP,
FACE_DOWN,
LARGE_CAPACITY,
};
/**
* Returns the string table for class {@code OutputBin}.
*/
@Override
protected String[] getStringTable() {
return myStringTable.clone();
}
/**
* Returns the enumeration value table for class {@code OutputBin}.
*/
@Override
protected EnumSyntax[] getEnumValueTable() {
return (EnumSyntax[]) myEnumValueTable.clone();
}
/**
* Get the printing attribute class which is to be used as the "category"
* for this printing attribute value.
* <p>
* For class {@code OutputBin} and any vendor-defined subclasses, the category
* is class {@code OutputBin} itself.
*
* @return printing attribute class (category), an instance of class
* {@link Class java.lang.Class}
*/
@Override
public final Class<? extends Attribute> getCategory() {
return OutputBin.class;
}
/**
* Get the name of the category of which this attribute value is an
* instance.
* <p>
* For class {@code OutputBin} and any vendor-defined subclasses, the category
* name is {@code "output-bin"}.
*
* @return attribute category name
*/
@Override
public final String getName() {
return "output-bin";
}
}

View File

@ -335,6 +335,13 @@
* <td>&nbsp;
* <td>&nbsp;
* <tr>
* <th scope="row"><a href="OutputBin.html">OutputBin</a>
* <td>&nbsp;
* <td>X
* <td>X
* <td>&nbsp;
* <td>&nbsp;
* <tr>
* <th scope="row"><a href="DateTimeAtCompleted.html">
* DateTimeAtCompleted</a>
* <td>&nbsp;

View File

@ -0,0 +1,108 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, BELLSOFT. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.print;
import java.io.Serial;
import java.util.ArrayList;
import javax.print.attribute.EnumSyntax;
import javax.print.attribute.standard.Media;
import javax.print.attribute.standard.OutputBin;
public final class CustomOutputBin extends OutputBin {
private static ArrayList<String> customStringTable = new ArrayList<>();
private static ArrayList<CustomOutputBin> customEnumTable = new ArrayList<>();
private String choiceName;
private CustomOutputBin(int x) {
super(x);
}
private static synchronized int nextValue(String name) {
customStringTable.add(name);
return (customStringTable.size()-1);
}
private CustomOutputBin(String name, String choice) {
super(nextValue(name));
choiceName = choice;
customEnumTable.add(this);
}
/**
* Creates a custom output bin
*/
public static synchronized CustomOutputBin createOutputBin(String name, String choice) {
for (CustomOutputBin bin : customEnumTable) {
if (bin.getChoiceName().equals(choice) && bin.getCustomName().equals(name)) {
return bin;
}
}
return new CustomOutputBin(name, choice);
}
private static final long serialVersionUID = 3018751086294120717L;
/**
* Returns the command string for this media tray.
*/
public String getChoiceName() {
return choiceName;
}
/**
* Returns the string table for super class MediaTray.
*/
public OutputBin[] getSuperEnumTable() {
return (OutputBin[])super.getEnumValueTable();
}
/**
* Returns the string table for class CustomOutputBin.
*/
@Override
protected String[] getStringTable() {
String[] nameTable = new String[customStringTable.size()];
return customStringTable.toArray(nameTable);
}
/**
* Returns a custom bin name
*/
public String getCustomName() {
return customStringTable.get(getValue() - getOffset());
}
/**
* Returns the enumeration value table for class CustomOutputBin.
*/
@Override
protected CustomOutputBin[] getEnumValueTable() {
CustomOutputBin[] enumTable = new CustomOutputBin[customEnumTable.size()];
return customEnumTable.toArray(enumTable);
}
}

View File

@ -61,6 +61,7 @@ import javax.print.attribute.standard.Copies;
import javax.print.attribute.standard.Destination;
import javax.print.attribute.standard.DialogTypeSelection;
import javax.print.attribute.standard.JobName;
import javax.print.attribute.standard.OutputBin;
import javax.print.attribute.standard.Sides;
import java.io.BufferedOutputStream;
@ -491,14 +492,19 @@ public class PSPrinterJob extends RasterPrinterJob {
if (attributes == null) {
return; // now always use attributes, so this shouldn't happen.
}
mOptions = "";
Attribute attr = attributes.get(Media.class);
if (attr instanceof CustomMediaTray) {
CustomMediaTray customTray = (CustomMediaTray)attr;
String choice = customTray.getChoiceName();
if (choice != null) {
mOptions = " InputSlot="+ choice;
mOptions += " InputSlot="+ choice;
}
}
String outputBin = getOutputBinValue(outputBinAttr);
if (outputBin != null) {
mOptions += " output-bin=" + outputBin;
}
}
/**
@ -1643,7 +1649,9 @@ public class PSPrinterJob extends RasterPrinterJob {
execCmd[n++] = "-o job-sheets=standard";
}
if ((pFlags & OPTIONS) != 0) {
execCmd[n++] = "-o" + options;
for (String option : options.trim().split(" ")) {
execCmd[n++] = "-o " + option;
}
}
} else {
ncomps+=1; //add 1 arg for lp
@ -1666,7 +1674,9 @@ public class PSPrinterJob extends RasterPrinterJob {
execCmd[n++] = "-o job-sheets=standard";
}
if ((pFlags & OPTIONS) != 0) {
execCmd[n++] = "-o" + options;
for (String option : options.trim().split(" ")) {
execCmd[n++] = "-o " + option;
}
}
}
execCmd[n++] = spoolFile;

View File

@ -81,6 +81,7 @@ import javax.print.attribute.standard.MediaPrintableArea;
import javax.print.attribute.standard.MediaSize;
import javax.print.attribute.standard.MediaSizeName;
import javax.print.attribute.standard.OrientationRequested;
import javax.print.attribute.standard.OutputBin;
import javax.print.attribute.standard.PageRanges;
import javax.print.attribute.standard.PrinterResolution;
import javax.print.attribute.standard.PrinterState;
@ -279,6 +280,7 @@ public abstract class RasterPrinterJob extends PrinterJob {
private PageRanges pageRangesAttr;
protected PrinterResolution printerResAttr;
protected Sides sidesAttr;
protected OutputBin outputBinAttr;
protected String destinationAttr;
protected boolean noJobSheet = false;
protected int mDestType = RasterPrinterJob.FILE;
@ -1228,6 +1230,7 @@ public abstract class RasterPrinterJob extends PrinterJob {
/* reset all values to defaults */
setCollated(false);
sidesAttr = null;
outputBinAttr = null;
printerResAttr = null;
pageRangesAttr = null;
copiesAttr = 0;
@ -1274,6 +1277,11 @@ public abstract class RasterPrinterJob extends PrinterJob {
sidesAttr = Sides.ONE_SIDED;
}
outputBinAttr = (OutputBin)attributes.get(OutputBin.class);
if (!isSupportedValue(outputBinAttr, attributes)) {
outputBinAttr = null;
}
printerResAttr = (PrinterResolution)attributes.get(PrinterResolution.class);
if (service.isAttributeCategorySupported(PrinterResolution.class)) {
if (!isSupportedValue(printerResAttr, attributes)) {
@ -2617,4 +2625,26 @@ public abstract class RasterPrinterJob extends PrinterJob {
parentWindowID = DialogOwnerAccessor.getID(onTop);
}
}
protected String getOutputBinValue(Attribute attr) {
if (attr instanceof CustomOutputBin customOutputBin) {
return customOutputBin.getChoiceName();
} else if (attr instanceof OutputBin) {
PrintService ps = getPrintService();
if (ps == null) {
return null;
}
String name = attr.toString();
OutputBin[] outputBins = (OutputBin[]) ps
.getSupportedAttributeValues(OutputBin.class, null, null);
for (OutputBin outputBin : outputBins) {
String choice = ((CustomOutputBin) outputBin).getChoiceName();
if (name.equalsIgnoreCase(choice) || name.replaceAll("-", "").equalsIgnoreCase(choice)) {
return choice;
}
}
return null;
}
return null;
}
}

View File

@ -69,6 +69,7 @@ import java.awt.event.KeyEvent;
import java.net.URISyntaxException;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import sun.awt.OSInfo;
/**
* A class which implements a cross-platform print dialog.
@ -2300,6 +2301,7 @@ public class ServiceDialog extends JDialog implements ActionListener {
private QualityPanel pnlQuality;
private JobAttributesPanel pnlJobAttributes;
private SidesPanel pnlSides;
private OutputPanel pnlOutput;
public AppearancePanel() {
super();
@ -2330,6 +2332,11 @@ public class ServiceDialog extends JDialog implements ActionListener {
pnlJobAttributes = new JobAttributesPanel();
addToGB(pnlJobAttributes, this, gridbag, c);
if (OSInfo.getOSType() != OSInfo.OSType.WINDOWS) {
c.gridwidth = GridBagConstraints.REMAINDER;
pnlOutput = new OutputPanel();
addToGB(pnlOutput, this, gridbag, c);
}
}
public void updateInfo() {
@ -2337,6 +2344,9 @@ public class ServiceDialog extends JDialog implements ActionListener {
pnlQuality.updateInfo();
pnlSides.updateInfo();
pnlJobAttributes.updateInfo();
if (pnlOutput != null) {
pnlOutput.updateInfo();
}
}
}
@ -2818,8 +2828,106 @@ public class ServiceDialog extends JDialog implements ActionListener {
}
}
@SuppressWarnings("serial") // Superclass is not serializable across versions
private class OutputPanel extends JPanel implements ItemListener {
private final String strTitle = getMsg("border.output");
private JLabel lblOutput;
private JComboBox<Object> cbOutput;
private Vector<OutputBin> outputs = new Vector<>();
public OutputPanel() {
super();
GridBagLayout gridbag = new GridBagLayout();
GridBagConstraints c = new GridBagConstraints();
setLayout(gridbag);
setBorder(BorderFactory.createTitledBorder(strTitle));
cbOutput = new JComboBox<>();
c.fill = GridBagConstraints.BOTH;
c.insets = compInsets;
c.weighty = 1.0;
c.weightx = 0.0;
lblOutput = new JLabel(getMsg("label.outputbins"), JLabel.TRAILING);
lblOutput.setDisplayedMnemonic(getMnemonic("label.outputbins"));
lblOutput.setLabelFor(cbOutput);
addToGB(lblOutput, this, gridbag, c);
c.weightx = 1.0;
c.gridwidth = GridBagConstraints.REMAINDER;
addToGB(cbOutput, this, gridbag, c);
}
public void itemStateChanged(ItemEvent e) {
Object source = e.getSource();
if (e.getStateChange() == ItemEvent.SELECTED) {
if (source == cbOutput) {
int index = cbOutput.getSelectedIndex();
if ((index >= 0) && (index < outputs.size())) {
asCurrent.add(outputs.get(index));
} else if (index == cbOutput.getItemCount() - 1) {
asCurrent.remove(OutputBin.class);
}
}
}
}
public void updateInfo() {
Class<OutputBin> obCategory = OutputBin.class;
cbOutput.removeItemListener(this);
cbOutput.removeAllItems();
outputs.clear();
boolean outputEnabled = false;
if (psCurrent.isAttributeCategorySupported(obCategory)) {
Object values =
psCurrent.getSupportedAttributeValues(obCategory,
docFlavor,
asCurrent);
if (values instanceof OutputBin[]) {
OutputBin[] outputBins = (OutputBin[])values;
for (OutputBin outputBin: outputBins) {
outputs.add(outputBin);
cbOutput.addItem(outputBin.toString());
}
cbOutput.addItem("");
cbOutput.setSelectedIndex(cbOutput.getItemCount() - 1);
OutputBin current = (OutputBin) asCurrent.get(obCategory);
if (current != null) {
for (int i = 0; i < outputs.size(); i++) {
if (current.equals(outputs.get(i))) {
cbOutput.setSelectedIndex(i);
break;
}
}
} else if (outputBins.length == 1) {
cbOutput.setSelectedIndex(0);
}
outputEnabled = outputBins.length > 1;
}
}
cbOutput.setEnabled(outputEnabled);
lblOutput.setEnabled(outputEnabled);
if (outputEnabled) {
cbOutput.addItemListener(this);
}
}
}
/**
* A special widget that groups a JRadioButton with an associated icon,

View File

@ -29,6 +29,7 @@ border.chromaticity=Color Appearance
border.copies=Copies
border.jobattributes=Job Attributes
border.media=Media
border.output=Output
border.orientation=Orientation
border.printrange=Print Range
border.printservice=Print Service
@ -62,6 +63,7 @@ label.pstype=Type:
label.rangeto=To
label.size=Si&ze:
label.source=Sour&ce:
label.outputbins=Out&put trays:
label.status=Status:
label.username=&User Name:
label.millimetres=(mm)

View File

@ -29,6 +29,7 @@ border.chromaticity=Farbdarstellung
border.copies=Kopien
border.jobattributes=Jobattribute
border.media=Medien
border.output=Ausgabe
border.orientation=Ausrichtung
border.printrange=Druckbereich
border.printservice=Druckservice
@ -62,6 +63,7 @@ label.pstype=Typ:
label.rangeto=Bis
label.size=G&röße:
label.source=&Quelle:
label.outputbins=A&usgabefächer:
label.status=Status:
label.username=&Benutzername:
label.millimetres=(mm)

View File

@ -29,6 +29,7 @@ border.chromaticity=Apariencia del Color
border.copies=Copias
border.jobattributes=Atributos del Trabajo
border.media=Soporte
border.output=Salida
border.orientation=Orientación
border.printrange=Rango de Impresión
border.printservice=Servicio de Impresión
@ -62,6 +63,7 @@ label.pstype=Tipo:
label.rangeto=A
label.size=Tama&ño:
label.source=Orig&en:
label.outputbins=Band&ejas de salida:
label.status=Estado:
label.username=&Usuario:
label.millimetres=(mm)

View File

@ -29,6 +29,7 @@ border.chromaticity=Couleur
border.copies=Copies
border.jobattributes=Attributs de tâche
border.media=Support
border.output=Sortir
border.orientation=Orientation
border.printrange=Plage d'impression
border.printservice=Service d'impression
@ -62,6 +63,7 @@ label.pstype=Type :
label.rangeto=A
label.size=Tai&lle :
label.source=Sour&ce :
label.outputbins=Bacs de s&ortie :
label.status=Statut :
label.username=Nom ut&ilisateur :
label.millimetres=(mm)

View File

@ -29,6 +29,7 @@ border.chromaticity=Aspetto colore
border.copies=Copie
border.jobattributes=Attributi job
border.media=Supporti
border.output=Output
border.orientation=Orientamento
border.printrange=Intervallo di stampa
border.printservice=Servizio di stampa
@ -62,6 +63,7 @@ label.pstype=Tipo:
label.rangeto=A
label.size=Di&mensioni:
label.source=O&rigine:
label.outputbins=&Vassoi di uscita:
label.status=Stato:
label.username=Nome &utente:
label.millimetres=(mm)

View File

@ -29,6 +29,7 @@ border.chromaticity=色の表現
border.copies=印刷部数
border.jobattributes=ジョブの属性
border.media=メディア
border.output=出力
border.orientation=用紙の向き
border.printrange=印刷範囲
border.printservice=印刷サービス
@ -62,6 +63,7 @@ label.pstype=タイプ:
label.rangeto=印刷範囲
label.size=サイズ(&Z):
label.source=ソース(&C):
label.outputbins=出力トレイ(&P):
label.status=状態:
label.username=ユーザー名(&U):
label.millimetres=(mm)

View File

@ -29,6 +29,7 @@ border.chromaticity=색상 모양
border.copies=복사
border.jobattributes=작업 속성
border.media=매체
border.output=출력물
border.orientation=방향
border.printrange=인쇄 범위
border.printservice=인쇄 서비스
@ -62,6 +63,7 @@ label.pstype=유형:
label.rangeto=종료
label.size=크기(&Z):
label.source=소스(&C):
label.outputbins=출력 트레이(&P):
label.status=상태:
label.username=사용자 이름(&U):
label.millimetres=(mm)

View File

@ -29,6 +29,7 @@ border.chromaticity=Aparência da Cor
border.copies=Cópias
border.jobattributes=Atributos do Job
border.media=Mídia
border.output=Saída
border.orientation=Orientação
border.printrange=Faixa de Impressão
border.printservice=Serviço de Impressão
@ -62,6 +63,7 @@ label.pstype=Tipo:
label.rangeto=Até
label.size=Ta&manho:
label.source=&Origem:
label.outputbins=Bande&jas de saída:
label.status=Status:
label.username=Nome do &Usuário:
label.millimetres=(mm)

View File

@ -29,6 +29,7 @@ border.chromaticity=Färg
border.copies=Antal exemplar
border.jobattributes=Utskriftsattribut
border.media=Media
border.output=Utmatning
border.orientation=Orientering
border.printrange=Utskriftsintervall
border.printservice=Utskriftstjänst
@ -62,6 +63,7 @@ label.pstype=Typ:
label.rangeto=Till
label.size=Stor&lek:
label.source=&Källa:
label.outputbins=Utma&tningsfack:
label.status=Status:
label.username=A&nvändarnamn:
label.millimetres=(mm)

View File

@ -29,6 +29,7 @@ border.chromaticity=颜色外观
border.copies=份数
border.jobattributes=作业属性
border.media=介质
border.output=出纸
border.orientation=方向
border.printrange=打印区域
border.printservice=打印服务
@ -62,6 +63,7 @@ label.pstype=类型:
label.rangeto=
label.size=大小(&Z):
label.source=来源(&C):
label.outputbins=出纸托盘(&P):
label.status=状态:
label.username=用户名(&U):
label.millimetres=(毫米)

View File

@ -29,6 +29,7 @@ border.chromaticity=色彩外觀
border.copies=份數
border.jobattributes=工作屬性
border.media=媒體
border.output=出紙
border.orientation=方向
border.printrange=列印範圍
border.printservice=列印服務
@ -62,6 +63,7 @@ label.pstype=類型:
label.rangeto=
label.size=大小(&Z):
label.source=來源(&C):
label.outputbins=输出纸盒(&P):
label.status=狀態:
label.username=使用者名稱(&U):
label.millimetres=(mm)

View File

@ -34,11 +34,13 @@ import java.util.HashMap;
import sun.print.IPPPrintService;
import sun.print.CustomMediaSizeName;
import sun.print.CustomMediaTray;
import sun.print.CustomOutputBin;
import javax.print.attribute.standard.Media;
import javax.print.attribute.standard.MediaSizeName;
import javax.print.attribute.standard.MediaSize;
import javax.print.attribute.standard.MediaTray;
import javax.print.attribute.standard.MediaPrintableArea;
import javax.print.attribute.standard.OutputBin;
import javax.print.attribute.standard.PrinterResolution;
import javax.print.attribute.Size2DSyntax;
import javax.print.attribute.Attribute;
@ -60,6 +62,7 @@ public class CUPSPrinter {
// CUPS does not support multi-threading.
private static synchronized native String[] getMedia(String printer);
private static synchronized native float[] getPageSizes(String printer);
private static synchronized native String[] getOutputBins(String printer);
private static synchronized native void
getResolutions(String printer, ArrayList<Integer> resolutionList);
//public static boolean useIPPMedia = false; will be used later
@ -68,10 +71,12 @@ public class CUPSPrinter {
private MediaSizeName[] cupsMediaSNames;
private CustomMediaSizeName[] cupsCustomMediaSNames;
private MediaTray[] cupsMediaTrays;
private OutputBin[] cupsOutputBins;
public int nPageSizes = 0;
public int nTrays = 0;
private String[] media;
private String[] outputBins;
private float[] pageSizes;
int[] resolutionsArray;
private String printer;
@ -144,6 +149,8 @@ public class CUPSPrinter {
for (int i=0; i < resolutionList.size(); i++) {
resolutionsArray[i] = resolutionList.get(i);
}
outputBins = getOutputBins(printer);
}
}
@ -185,6 +192,14 @@ public class CUPSPrinter {
return cupsMediaTrays;
}
/**
* Returns array of OutputBins derived from PPD.
*/
OutputBin[] getOutputBins() {
initMedia();
return cupsOutputBins;
}
/**
* return the raw packed array of supported printer resolutions.
*/
@ -261,6 +276,15 @@ public class CUPSPrinter {
cupsMediaTrays[i] = mt;
}
if (outputBins == null) {
cupsOutputBins = new OutputBin[0];
} else {
int nBins = outputBins.length / 2;
cupsOutputBins = new OutputBin[nBins];
for (int i = 0; i < nBins; i++) {
cupsOutputBins[i] = CustomOutputBin.createOutputBin(outputBins[i*2], outputBins[i*2+1]);
}
}
}
/**

View File

@ -80,6 +80,7 @@ import javax.print.attribute.standard.MediaSizeName;
import javax.print.attribute.standard.MediaTray;
import javax.print.attribute.standard.NumberUp;
import javax.print.attribute.standard.OrientationRequested;
import javax.print.attribute.standard.OutputBin;
import javax.print.attribute.standard.PDLOverrideSupported;
import javax.print.attribute.standard.PageRanges;
import javax.print.attribute.standard.PagesPerMinute;
@ -138,6 +139,7 @@ public class IPPPrintService implements PrintService, SunPrinterJobService {
private DocFlavor[] supportedDocFlavors;
private Class<?>[] supportedCats;
private MediaTray[] mediaTrays;
private OutputBin[] outputBins;
private MediaSizeName[] mediaSizeNames;
private CustomMediaSizeName[] customMediaSizeNames;
private int defaultMediaIndex;
@ -211,6 +213,7 @@ public class IPPPrintService implements PrintService, SunPrinterJobService {
new RequestingUserName("", Locale.getDefault()),
//SheetCollate.UNCOLLATED, //CUPS has no sheet collate?
Sides.ONE_SIDED,
OutputBin.TOP,
};
@ -440,6 +443,7 @@ public class IPPPrintService implements PrintService, SunPrinterJobService {
if ((urlConnection = getIPPConnection(myURL)) == null) {
mediaSizeNames = new MediaSizeName[0];
mediaTrays = new MediaTray[0];
outputBins = new OutputBin[0];
debug_println(debugPrefix+"initAttributes, NULL urlConnection ");
init = true;
return;
@ -460,6 +464,9 @@ public class IPPPrintService implements PrintService, SunPrinterJobService {
cps = new CUPSPrinter(printer);
mediaSizeNames = cps.getMediaSizeNames();
mediaTrays = cps.getMediaTrays();
outputBins = PrintServiceLookupProvider.isMac()
? cps.getOutputBins()
: getSupportedOutputBins();
customMediaSizeNames = cps.getCustomMediaSizeNames();
defaultMediaIndex = cps.getDefaultMediaIndex();
rawResolutions = cps.getRawResolutions();
@ -493,6 +500,11 @@ public class IPPPrintService implements PrintService, SunPrinterJobService {
mediaTrays = new MediaTray[trayList.size()];
mediaTrays = trayList.toArray(mediaTrays);
}
if (outputBins == null) {
outputBins = getSupportedOutputBins();
}
urlConnection.disconnect();
init = true;
@ -827,6 +839,8 @@ public class IPPPrintService implements PrintService, SunPrinterJobService {
new PrinterResolution[supportedRes.length];
System.arraycopy(supportedRes, 0, arr, 0, supportedRes.length);
return arr;
} else if (category == OutputBin.class) {
return Arrays.copyOf(outputBins, outputBins.length);
}
return null;
@ -1053,6 +1067,25 @@ public class IPPPrintService implements PrintService, SunPrinterJobService {
return new Media[0];
}
private OutputBin[] getSupportedOutputBins() {
if ((getAttMap != null) && getAttMap.containsKey("output-bin-supported")) {
AttributeClass attribClass = getAttMap.get("output-bin-supported");
if (attribClass != null) {
String[] values = attribClass.getArrayOfStringValues();
if (values == null || values.length == 0) {
return null;
}
OutputBin[] outputBinNames = new OutputBin[values.length];
for (int i = 0; i < values.length; i++) {
outputBinNames[i] = CustomOutputBin.createOutputBin(values[i], values[i]);
}
return outputBinNames;
}
}
return null;
}
public synchronized Class<?>[] getSupportedAttributeCategories() {
if (supportedCats != null) {
@ -1070,6 +1103,11 @@ public class IPPPrintService implements PrintService, SunPrinterJobService {
(PrintRequestAttribute)printReqAttribDefault[i];
if (getAttMap != null &&
getAttMap.containsKey(pra.getName()+"-supported")) {
if (pra == OutputBin.TOP && (outputBins == null || outputBins.length == 0)) {
continue;
}
catList.add(pra.getCategory());
}
}
@ -1148,6 +1186,11 @@ public class IPPPrintService implements PrintService, SunPrinterJobService {
return true;
}
if (category == OutputBin.class
&& (outputBins == null || outputBins.length == 0)) {
return false;
}
for (int i=0;i<supportedCats.length;i++) {
if (category == supportedCats[i]) {
return true;
@ -1478,6 +1521,18 @@ public class IPPPrintService implements PrintService, SunPrinterJobService {
DialogTypeSelection dst = (DialogTypeSelection)attr;
return attr == DialogTypeSelection.COMMON;
}
} else if (attr.getCategory() == OutputBin.class) {
if (attr instanceof CustomOutputBin) {
return true;
}
String name = attr.toString();
for (OutputBin outputBin : outputBins) {
String choice = ((CustomOutputBin) outputBin).getChoiceName();
if (name.equalsIgnoreCase(choice) || name.replaceAll("-", "").equalsIgnoreCase(choice)) {
return true;
}
}
return false;
}
return true;
}
@ -1646,6 +1701,10 @@ public class IPPPrintService implements PrintService, SunPrinterJobService {
} else {
return new PrinterResolution(300, 300, PrinterResolution.DPI);
}
} else if (category == OutputBin.class) {
if (attribClass != null) {
return CustomOutputBin.createOutputBin(attribClass.getStringValue(), attribClass.getStringValue());
}
}
return null;

View File

@ -415,6 +415,96 @@ Java_sun_print_CUPSPrinter_getMedia(JNIEnv *env,
return nameArray;
}
/*
* Returns list of output bins
*/
JNIEXPORT jobjectArray JNICALL
Java_sun_print_CUPSPrinter_getOutputBins(JNIEnv *env,
jobject printObj,
jstring printer)
{
ppd_file_t *ppd;
ppd_choice_t *choice;
ppd_option_t *outputBin;
const char *name;
const char *filename;
int i, nBins=0;
jstring utf_str;
jclass cls;
jobjectArray nameArray = NULL;
name = (*env)->GetStringUTFChars(env, printer, NULL);
if (name == NULL) {
(*env)->ExceptionClear(env);
JNU_ThrowOutOfMemoryError(env, "Could not create printer name");
return NULL;
}
// NOTE: cupsGetPPD returns a pointer to a filename of a temporary file.
// unlink() must be caled to remove the file when finished using it.
filename = j2d_cupsGetPPD(name);
(*env)->ReleaseStringUTFChars(env, printer, name);
CHECK_NULL_RETURN(filename, NULL);
cls = (*env)->FindClass(env, "java/lang/String");
CHECK_NULL_RETURN(cls, NULL);
if ((ppd = j2d_ppdOpenFile(filename)) == NULL) {
unlink(filename);
DPRINTF("CUPSfuncs::unable to open PPD %s\n", filename);
return NULL;
}
outputBin = j2d_ppdFindOption(ppd, "OutputBin");
if (outputBin != NULL) {
nBins = outputBin->num_choices;
}
if (nBins > 0) {
nameArray = (*env)->NewObjectArray(env, nBins * 2, cls, NULL);
if (nameArray == NULL) {
unlink(filename);
j2d_ppdClose(ppd);
DPRINTF("CUPSfuncs::bad alloc new array\n", "")
if (!(*env)->ExceptionCheck(env)) {
JNU_ThrowOutOfMemoryError(env, "OutOfMemoryError");
}
return NULL;
}
for (i = 0; outputBin!=NULL && i<nBins; i++) {
choice = (outputBin->choices)+i;
utf_str = JNU_NewStringPlatform(env, choice->text);
if (utf_str == NULL) {
unlink(filename);
j2d_ppdClose(ppd);
DPRINTF("CUPSfuncs::bad alloc new string text\n", "")
if (!(*env)->ExceptionCheck(env)) {
JNU_ThrowOutOfMemoryError(env, "OutOfMemoryError");
}
return NULL;
}
(*env)->SetObjectArrayElement(env, nameArray, i*2, utf_str);
(*env)->DeleteLocalRef(env, utf_str);
utf_str = JNU_NewStringPlatform(env, choice->choice);
if (utf_str == NULL) {
unlink(filename);
j2d_ppdClose(ppd);
DPRINTF("CUPSfuncs::bad alloc new string choice\n", "")
if (!(*env)->ExceptionCheck(env)) {
JNU_ThrowOutOfMemoryError(env, "OutOfMemoryError");
}
return NULL;
}
(*env)->SetObjectArrayElement(env, nameArray, i*2+1, utf_str);
(*env)->DeleteLocalRef(env, utf_str);
}
}
j2d_ppdClose(ppd);
unlink(filename);
return nameArray;
}
/*
* Returns list of page sizes and imageable area.

View File

@ -0,0 +1,116 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, BELLSOFT. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.util.Arrays;
import java.util.Set;
import javax.print.PrintService;
import javax.print.PrintServiceLookup;
import javax.print.attribute.Attribute;
import javax.print.attribute.standard.OutputBin;
/*
* @test
* @bug 8314070
* @key printer
* @summary javax.print: Support IPP output-bin attribute extension
*/
public class CheckSupportedOutputBinsTest {
public static void main(String[] args) throws Exception {
PrintService[] services = PrintServiceLookup.lookupPrintServices(null, null);
if (services == null) {
System.out.printf("Skip the test as there are no available PrintServices.%n");
return;
}
System.out.printf("Print services: %d%n", services.length);
for (PrintService service : services) {
checkSupportedOutputBins(service);
}
}
private static void checkSupportedOutputBins(PrintService service) throws Exception {
System.out.printf("Check printService: %s%n", service);
boolean isOutputBinCategorySupported = service.isAttributeCategorySupported(OutputBin.class);
OutputBin defaultOutputBin = (OutputBin) service.getDefaultAttributeValue(OutputBin.class);
Set<Class<?>> supportedAttributeCategories = Set.of(service.getSupportedAttributeCategories());
OutputBin[] supportedOutputBins = (OutputBin[]) service
.getSupportedAttributeValues(OutputBin.class, null, null);
if (!isOutputBinCategorySupported) {
if (supportedAttributeCategories.contains(OutputBin.class)) {
throw new Exception("OutputBin category is not supported" +
" and supported attribute categories contain OutputBin.class.");
}
if (defaultOutputBin != null) {
throw new Exception("OutputBin category is not supported" +
" and the default output bin is not null.");
}
if (supportedOutputBins != null && supportedOutputBins.length > 0) {
throw new Exception("OutputBin category is not supported" +
" and array of supported output bins is not null or its size is not zero.");
}
return;
}
if (!supportedAttributeCategories.contains(OutputBin.class)) {
throw new Exception("OutputBin category is supported" +
" and supported attribute categories do not contain OutputBin.class.");
}
if (defaultOutputBin == null) {
throw new Exception("OutputBin category is supported" +
" and the default output bin is null.");
}
if (supportedOutputBins == null || supportedOutputBins.length == 0) {
throw new Exception("OutputBin category is supported" +
" and PrintService.getSupportedAttributeValues() returns null or an array with zero elements.");
}
if (!service.isAttributeValueSupported(defaultOutputBin, null, null)) {
throw new Exception("OutputBin category is supported" +
" and the default output bin " + defaultOutputBin + " is not supported");
}
for (OutputBin outputBin : supportedOutputBins) {
if (!service.isAttributeValueSupported(outputBin, null, null)) {
throw new Exception("OutputBin category is supported" +
" and the output bin " + outputBin + " from supported attribute values" +
" is not supported");
}
}
}
}

View File

@ -0,0 +1,368 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, BELLSOFT. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.print.PrintService;
import javax.print.attribute.Attribute;
import javax.print.attribute.HashPrintRequestAttributeSet;
import javax.print.attribute.PrintRequestAttributeSet;
import javax.print.attribute.standard.DialogTypeSelection;
import javax.print.attribute.standard.MediaSizeName;
import javax.print.attribute.standard.OutputBin;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dialog;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Window;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/*
* @test
* @bug 8314070
* @key printer
* @requires (os.family == "linux" | os.family == "mac")
* @summary javax.print: Support IPP output-bin attribute extension
* @run main/manual OutputBinAttributePrintDialogTest COMMON
* @run main/manual OutputBinAttributePrintDialogTest NATIVE
*/
public class OutputBinAttributePrintDialogTest {
private static final long TIMEOUT = 10 * 60_000;
private static volatile boolean testPassed = true;
private static volatile boolean testSkipped = false;
private static volatile boolean testFinished = false;
private static volatile boolean printJobCanceled = false;
private static volatile boolean timeout = false;
private static volatile boolean isNativeDialog;
private static volatile int testCount;
private static volatile int testTotalCount;
public static void main(String[] args) throws Exception {
if (args.length < 1) {
throw new RuntimeException("COMMON or NATIVE print dialog type argument is not provided!");
}
final DialogTypeSelection dialogTypeSelection = getDialogTypeSelection(args[0]);
isNativeDialog = (dialogTypeSelection == DialogTypeSelection.NATIVE);
if (dialogTypeSelection == DialogTypeSelection.NATIVE) {
String os = System.getProperty("os.name").toLowerCase();
if (os.startsWith("linux")) {
System.out.println("Skip the native print dialog type test on Linux as it is the same as the common.");
return;
}
}
final OutputBin[] supportedOutputBins = getSupportedOutputBinttributes();
if (supportedOutputBins == null) {
return;
}
if (supportedOutputBins.length < 2) {
System.out.println("Skip the test as the number of supported output bins is less than 2.");
return;
}
SwingUtilities.invokeLater(() -> {
testTotalCount = supportedOutputBins.length;
for (OutputBin outputBin : supportedOutputBins) {
if (testSkipped) {
break;
}
testPrint(dialogTypeSelection, outputBin, supportedOutputBins);
}
testFinished = true;
});
long time = System.currentTimeMillis() + TIMEOUT;
while (System.currentTimeMillis() < time) {
if (!testPassed || testFinished) {
break;
}
Thread.sleep(500);
}
timeout = true;
closeDialogs();
if (testSkipped) {
System.out.printf("Test is skipped!%n");
return;
}
if (!testPassed) {
throw new Exception("Test failed!");
}
if (testCount != testTotalCount) {
throw new Exception(
"Timeout: " + testCount + " tests passed out from " + testTotalCount);
}
}
private static void print(DialogTypeSelection dialogTypeSelection, OutputBin outputBin) throws PrinterException {
PrintRequestAttributeSet attr = new HashPrintRequestAttributeSet();
attr.add(MediaSizeName.ISO_A4);
attr.add(dialogTypeSelection);
for (Attribute attribute : attr.toArray()) {
System.out.printf("Used print request attribute: %s%n", attribute);
}
PrinterJob job = PrinterJob.getPrinterJob();
job.setJobName("Print to " + outputBin + " output bin through " + dialogTypeSelection + " print dialog");
job.setPrintable(new OutputBinAttributePrintable(outputBin));
if (job.printDialog(attr)) {
job.print();
} else if (isNativeDialog) {
printJobCanceled = true;
} else {
throw new RuntimeException(dialogTypeSelection + " print dialog for " + outputBin + " is canceled!");
}
}
private static class OutputBinAttributePrintable implements Printable {
private final OutputBin outputBinAttr;
public OutputBinAttributePrintable(OutputBin outputBinAttr) {
this.outputBinAttr = outputBinAttr;
}
@Override
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
if (pageIndex != 0) {
return NO_SUCH_PAGE;
}
int x = (int) (pageFormat.getImageableX() + pageFormat.getImageableWidth() / 10);
int y = (int) (pageFormat.getImageableY() + pageFormat.getImageableHeight() / 5);
Graphics2D g = (Graphics2D) graphics;
g.setColor(Color.BLACK);
g.drawString(getPageText(outputBinAttr), x, y);
return PAGE_EXISTS;
}
}
private static String getPageText(OutputBin outputBin) {
return String.format("Output bin: %s", outputBin);
}
private static OutputBin[] getSupportedOutputBinttributes() {
PrinterJob printerJob = PrinterJob.getPrinterJob();
PrintService service = printerJob.getPrintService();
if (service == null) {
System.out.printf("No print service found.");
return null;
}
if (!service.isAttributeCategorySupported(OutputBin.class)) {
System.out.printf("Skipping the test as OutputBin category is not supported for this printer.");
return null;
}
Object obj = service.getSupportedAttributeValues(OutputBin.class, null, null);
if (obj instanceof OutputBin[]) {
return (OutputBin[]) obj;
}
throw new RuntimeException("OutputBin category is supported but no supported attribute values are returned.");
}
private static void pass() {
testCount++;
}
private static void skip() {
testSkipped = true;
}
private static void fail(OutputBin outputBin) {
System.out.printf("Failed test: %s%n", getPageText(outputBin));
testPassed = false;
}
private static void runPrint(DialogTypeSelection dialogTypeSelection, OutputBin outputBin) {
try {
print(dialogTypeSelection, outputBin);
} catch (PrinterException e) {
e.printStackTrace();
fail(outputBin);
closeDialogs();
}
}
private static void testPrint(DialogTypeSelection dialogTypeSelection, OutputBin outputBin, OutputBin[] supportedOutputBins) {
System.out.printf("Test dialog: %s%n", dialogTypeSelection);
String[] instructions = {
"Up to " + testTotalCount + " tests will run and it will test all output bins:",
Arrays.toString(supportedOutputBins),
"supported by the printer.",
"",
"The test is " + (testCount + 1) + " from " + testTotalCount + ".",
"",
"On-screen inspection is not possible for this printing-specific",
"test therefore its only output is a page printed to the printer",
outputBin + " output bin.",
"",
"To be able to run this test it is required to have a default",
"printer configured in your user environment.",
"",
" - Press 'Start Test' button.",
" The " + dialogTypeSelection + " print dialog should appear.",
String.join("\n", getPrintDialogInstructions(dialogTypeSelection, outputBin)),
"",
"Visual inspection of the printed pages is needed.",
"",
"A passing test will print the page with the text: '" + getPageText(outputBin) + "'",
"to the corresponding printer " + outputBin + " ouput bin.",
"",
"The test fails if the page is not printed in to the corresponding output bin.",
};
String title = String.format("Print %s dialog with %s output bin test: %d from %d",
dialogTypeSelection, outputBin, testCount + 1, testTotalCount);
final JDialog dialog = new JDialog((Frame) null, title, Dialog.ModalityType.DOCUMENT_MODAL);
JTextArea textArea = new JTextArea(String.join("\n", instructions));
textArea.setEditable(false);
final JButton testButton = new JButton("Start Test");
final JButton skipButton = new JButton("Skip Test");
final JButton passButton = new JButton("PASS");
skipButton.setEnabled(false);
passButton.setEnabled(false);
passButton.addActionListener((e) -> {
pass();
dialog.dispose();
});
skipButton.addActionListener((e) -> {
skip();
dialog.dispose();
});
final JButton failButton = new JButton("FAIL");
failButton.setEnabled(false);
failButton.addActionListener((e) -> {
fail(outputBin);
dialog.dispose();
});
testButton.addActionListener((e) -> {
testButton.setEnabled(false);
runPrint(dialogTypeSelection, outputBin);
skipButton.setEnabled(true);
passButton.setEnabled(true);
failButton.setEnabled(true);
});
JPanel mainPanel = new JPanel(new BorderLayout());
mainPanel.add(textArea, BorderLayout.CENTER);
JPanel buttonPanel = new JPanel(new FlowLayout());
buttonPanel.add(testButton);
if (isNativeDialog) {
buttonPanel.add(skipButton);
}
buttonPanel.add(passButton);
buttonPanel.add(failButton);
mainPanel.add(buttonPanel, BorderLayout.SOUTH);
dialog.add(mainPanel);
dialog.pack();
dialog.setVisible(true);
dialog.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.out.println("Dialog closing");
fail(outputBin);
}
});
}
private static void closeDialogs() {
for (Window w : Dialog.getWindows()) {
w.dispose();
}
}
private static DialogTypeSelection getDialogTypeSelection(String dialogTypeSelection) {
switch (dialogTypeSelection) {
case "COMMON":
return DialogTypeSelection.COMMON;
case "NATIVE":
return DialogTypeSelection.NATIVE;
default:
throw new RuntimeException("Unknown dialog type selection: " + dialogTypeSelection);
}
}
private static String[] getPrintDialogInstructions(DialogTypeSelection dialogTypeSelection, OutputBin outputBin) {
if (dialogTypeSelection == DialogTypeSelection.COMMON) {
return new String[]{
" - Select 'Appearance' tab.",
" - Select '" + outputBin + "' output tray from 'Output trays' combo box.",
" - Press 'Print' button."
};
} else if (dialogTypeSelection == DialogTypeSelection.NATIVE) {
return new String[]{
" - Press 'Show Details' buttons if the details are hidded.",
" - Check that the native print dialog contains 'Finishing Options' in the drop-down list.",
" - If there is no 'Finishing Options' in the drop-down list then",
" - Press 'Cancel' button on the print dialog.",
" - Press 'Skip Test' button on the test dialog.",
" otherwise",
" - Select 'Finishing Options' from the drop-down list.",
" - Select '" + outputBin + "' Output Bin.",
" - Press 'Print' button."
};
}
throw new RuntimeException("Unknown dialog type selection: " + dialogTypeSelection);
}
}

View File

@ -0,0 +1,275 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, BELLSOFT. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.print.PrintService;
import javax.print.attribute.Attribute;
import javax.print.attribute.HashPrintRequestAttributeSet;
import javax.print.attribute.PrintRequestAttributeSet;
import javax.print.attribute.standard.MediaSizeName;
import javax.print.attribute.standard.OutputBin;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dialog;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Window;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/*
* @test
* @bug 8314070
* @key printer
* @requires (os.family == "linux" | os.family == "mac")
* @summary javax.print: Support IPP output-bin attribute extension
* @run main/manual OutputBinAttributeTest
*/
public class OutputBinAttributeTest {
private static final long TIMEOUT = 10 * 60_000;
private static volatile boolean testPassed = true;
private static volatile boolean testFinished = false;
private static volatile boolean timeout = false;
private static volatile int testCount;
private static volatile int testTotalCount;
public static void main(String[] args) throws Exception {
SwingUtilities.invokeLater(() -> {
Set<OutputBin> supportedOutputBins = getSupportedOutputBinttributes();
if (supportedOutputBins != null) {
if (supportedOutputBins.size() > 1) {
testTotalCount = supportedOutputBins.size();
for (OutputBin outputBin : supportedOutputBins) {
testPrint(outputBin, supportedOutputBins);
}
} else {
System.out.println("Skip the test as the number of supported output bins is less than 2.");
}
}
testFinished = true;
});
long time = System.currentTimeMillis() + TIMEOUT;
while (System.currentTimeMillis() < time) {
if (!testPassed || testFinished) {
break;
}
Thread.sleep(500);
}
timeout = true;
closeDialogs();
if (!testPassed) {
throw new Exception("Test failed!");
}
if (testCount != testTotalCount) {
throw new Exception(
"Timeout: " + testCount + " tests passed out from " + testTotalCount);
}
}
private static void print(OutputBin outputBin) throws PrinterException {
PrintRequestAttributeSet attr = new HashPrintRequestAttributeSet();
attr.add(MediaSizeName.ISO_A4);
attr.add(outputBin);
for (Attribute attribute : attr.toArray()) {
System.out.printf("Used print request attribute: %s%n", attribute);
}
PrinterJob job = PrinterJob.getPrinterJob();
job.setJobName("Print to " + outputBin + " output bin");
job.setPrintable(new OutputBinAttributePrintable(outputBin));
job.print(attr);
}
private static class OutputBinAttributePrintable implements Printable {
private final OutputBin outputBinAttr;
public OutputBinAttributePrintable(OutputBin outputBinAttr) {
this.outputBinAttr = outputBinAttr;
}
@Override
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
if (pageIndex != 0) {
return NO_SUCH_PAGE;
}
int x = (int) (pageFormat.getImageableX() + pageFormat.getImageableWidth() / 10);
int y = (int) (pageFormat.getImageableY() + pageFormat.getImageableHeight() / 5);
Graphics2D g = (Graphics2D) graphics;
g.setColor(Color.BLACK);
g.drawString(getPageText(outputBinAttr), x, y);
return PAGE_EXISTS;
}
}
private static String getPageText(OutputBin outputBin) {
return String.format("Output bin: %s", outputBin);
}
private static Set<OutputBin> getSupportedOutputBinttributes() {
Set<OutputBin> supportedOutputBins = new HashSet<>();
PrinterJob printerJob = PrinterJob.getPrinterJob();
PrintService service = printerJob.getPrintService();
if (service == null) {
System.out.printf("No print service found.");
return null;
}
if (!service.isAttributeCategorySupported(OutputBin.class)) {
System.out.printf("Skipping the test as OutputBin category is not supported for this printer.");
return null;
}
Object obj = service.getSupportedAttributeValues(OutputBin.class, null, null);
if (obj instanceof OutputBin[]) {
Collections.addAll(supportedOutputBins, (OutputBin[]) obj);
return supportedOutputBins;
}
throw new RuntimeException("OutputBin category is supported but no supported attribute values are returned.");
}
private static void pass() {
testCount++;
}
private static void fail(OutputBin outputBin) {
System.out.printf("Failed test: %s%n", getPageText(outputBin));
testPassed = false;
}
private static void runPrint(OutputBin outputBin) {
try {
print(outputBin);
} catch (PrinterException e) {
e.printStackTrace();
fail(outputBin);
}
}
private static void testPrint(OutputBin outputBin, Set<OutputBin> supportedOutputBins) {
String[] instructions = {
"Up to " + testTotalCount + " tests will run and it will test all output bins:",
supportedOutputBins.toString(),
"supported by the printer.",
"",
"The test is " + (testCount + 1) + " from " + testTotalCount + ".",
"",
"On-screen inspection is not possible for this printing-specific",
"test therefore its only output is a page printed to the printer",
outputBin + " output bin.",
"To be able to run this test it is required to have a default",
"printer configured in your user environment.",
"",
"Visual inspection of the printed pages is needed.",
"",
"A passing test will print the page with the text: '" + getPageText(outputBin) + "'",
"to the corresponding printer " + outputBin + " ouput bin.",
"",
"The test fails if the page is not printed in to the corresponding output bin.",
};
String title = String.format("Print %s output bin test: %d from %d",
outputBin, testCount + 1, testTotalCount);
final JDialog dialog = new JDialog((Frame) null, title, Dialog.ModalityType.DOCUMENT_MODAL);
JTextArea textArea = new JTextArea(String.join("\n", instructions));
textArea.setEditable(false);
final JButton testButton = new JButton("Start Test");
final JButton passButton = new JButton("PASS");
passButton.setEnabled(false);
passButton.addActionListener((e) -> {
pass();
dialog.dispose();
});
final JButton failButton = new JButton("FAIL");
failButton.setEnabled(false);
failButton.addActionListener((e) -> {
fail(outputBin);
dialog.dispose();
});
testButton.addActionListener((e) -> {
testButton.setEnabled(false);
runPrint(outputBin);
passButton.setEnabled(true);
failButton.setEnabled(true);
});
JPanel mainPanel = new JPanel(new BorderLayout());
mainPanel.add(textArea, BorderLayout.CENTER);
JPanel buttonPanel = new JPanel(new FlowLayout());
buttonPanel.add(testButton);
buttonPanel.add(passButton);
buttonPanel.add(failButton);
mainPanel.add(buttonPanel, BorderLayout.SOUTH);
dialog.add(mainPanel);
dialog.pack();
dialog.setVisible(true);
dialog.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.out.println("Dialog closing");
fail(outputBin);
}
});
}
private static void closeDialogs() {
for (Window w : Dialog.getWindows()) {
w.dispose();
}
}
}