Get webcams names in Xuggler library on Windows - java

When I using Xuggler library on computer with more than one web-camera with Windows I need use "name" of this web-cameras to select device. I means that command "vfwcap 0" select only first web-camera and command "vfwcap 1" or "vfwcap 2" not allowed get access to other web-cameras. If I use:
$ ffmpeg -list_devices true -f dshow -i dummy
I can see the list of "names" this web-cameras. For example: "Logitech HD Webcam C270", "Logitech Webcam C210". If I using this names into the Xuggler library I get en error.
So, I can get access only to the first web-camera on Windows machine using Xuggler.
Can I get list of all devices in Windows from Xuggler and can I use this names into the Xuggler (I just want to use more than one web-camera at the same time)?
Maybe alternative way are exists?

Using this simple java program, you can get list of all webcam's available on your system.
import com.github.sarxos.webcam.Webcam;
public class CameraTest {
public static void main(String[] args) {
List<Webcam> list = Webcam.getWebcams();
for (int i = 0; i < list.size(); i++) {
try {
Webcam cam = list.get(i);
System.out.println("Found this Camera : "+cam.getName());
BufferedImage image = cam.getImage();
} catch (Exception e) {
System.out.println("Exception in cam : " + i);
}
}
}
}
Sample Output :
Found this Camera : TV CARD WDM VIDEO CAPTURE 0
Found this Camera : ManyCam Video Source 1
Found this Camera : DroidCam 2
You will have List of all camera's , So you can use anyone of them as you like.

Related

Get drive letters of USB 3.0 devices (Java under Windows)

My Java program needs to get a list of the drive letters of connected USB devices, but only those that support USB 3.0 (both the device and the USB port it is plugged into, so that it works with high speed).
Currently I try to use WMI through a PowerShell command my Java programm executes.
I already found this: Powershell: Grab USB Drive letter. But it would also list USB 2.0 devices.
Regarding version detection I found this: How to check the version of the available USB ports? - The PowerShell command I tried is Get-WmiObject Win32_USBHub. This brings up several problems. First: It lists far more stuff than only USB drives (I think also all the USB hubs of my PC). Second: Even though there is a field USBVersion for all items in the list it is always empty.
Update
The essence of my research over the last days is, that there are 2 realms of information I need to connect.
Drives / Logical Drives
Drive Letter
BusType (is equal to "USB" for my matter)
USB devices
Vendor ID and Product ID (VID&PID)
bcdUSB (value within the usb device descriptor, indicating USB Version)
For a given drive letter I need to find the bcdUSB value. But I haven't found a way to get the drive corresponding to a USB device.
What I tried so far
WMI over PowerShell
Relevant commands I found are
Get-Disk // Get BusType
gwmi Win32_LogicalDisk // Get drive letter
// Those make the connection between disk and logical disk
gwmi Win32_LogicalDiskToPartition
gwmi Win32_LogicalDiskToPartition
Even though I get the BusType I couldn't make a connection to bcdUSB
usb4java (Link)
I only get information from the USB device realm here. I can load devices and see ther VID&PID and the bcdUSB value, but no way to map this to drives and drive letters.
lsusb via Cygwin
According to this post the linux command is easier to handle than WMI. So I tried to use it under Windows. But I like usb4java I only got VID&PID + bcdUSB, not the mount point (drive letter).
Searching the Windows Registry
I did a few string searchs in the Windows registry. No success.
Reading Windows Event log
I thought about ovserving Windows events to detect what Drive and what USB device connect at the same time. I didn't even find events when plugging in a USB stick.
Maybe this is what you are looking for:
Find Windows Drive Letter of a removable disk from USB VID/PID
At least someone marked the answer as working... :-)
Since the suggested Link solves this problem for C# not Java and leaves out one step, I'll post my final code here.
Summary
In Java
Use USB4Java to find all connected USB devices with bcdUSB=0x0300
Get Vendor ID and Product ID (VID&PID) for that devices
Via Powershell (with jPowerShell)
Get PnPEntity for given VID&PID
Get related USB Controller
Find associator of that USB Controller that is associated with a disk drive
Get that Disk drive
Get related disk partition
Get related logical disk -> LogicalDisk.DeviceID = Drive Letter
Code
Java class:
class UsbDetector {
private PowerShell shell;
#PostConstruct
private void init() {
shell = com.profesorfalken.jpowershell.PowerShell.openSession();
}
#OnDestroy
private void onShutdownHook() {
shell.close();
}
/**
* Get drive letters of USB 3.0 devices.
*/
public List<String> getDriveLettersForUsb3Devices() throws IOException, UsbException {
List<UsbDevice> devicesUSB3 = getAllUsb3Devices();
ImmutableList.Builder<String> driveLetterList = ImmutableList.builder();
for (UsbDevice device : devicesUSB3) {
String vidAndPid = getVidAndPid(device);
String powerShellScript = buildScript(vidAndPid);
String driveLetter = executeOnPowerShell(powerShellScript);
driveLetterList.add(driveLetter);
}
return driveLetterList.build();
}
private String executeOnPowerShell(String powerShellScript) {
InputStream psScriptStream = new ByteArrayInputStream(powerShellScript.getBytes());
BufferedReader psScriptReader = new BufferedReader(new InputStreamReader(psScriptStream));
PowerShellResponse response = shell.executeScript(psScriptReader);
return response.getCommandOutput();
}
private String buildScript(String vidAndPid) throws IOException {
InputStream psScriptStream =
getClass().getClassLoader().getResourceAsStream("GetUsbDrives.ps1");
String psScript = IOUtil.toString(psScriptStream);
psScript = String.format("$input=\"%s\"", vidAndPid) + "\n" + psScript;
return psScript;
}
/**
* The Vendor ID and Product ID are necessary to find the device via WMI.
*/
private String getVidAndPid(UsbDevice device) {
short vendorId = device.getUsbDeviceDescriptor().idVendor();
short productId = device.getUsbDeviceDescriptor().idProduct();
String vendorIdHexString = String.format("%04x", vendorId).toUpperCase();
String productIdHexString = String.format("%04x", productId).toUpperCase();
String vidAndPid = String.format("VID_%s&PID_%s", vendorIdHexString, productIdHexString);
return vidAndPid;
}
/**
* From all Usb devices find those with USB 3.0. The value bcdUsb is a hexadecimal coded number
* telling us the USB version.
*/
private List<UsbDevice> getAllUsb3Devices() throws UsbException {
List<UsbDevice> devicesUSB3 = Lists.newArrayList();
UsbServices services = new org.usb4java.javax.Services();
UsbHub hub = services.getRootUsbHub();
List<UsbDevice> devices = getAllUsbDevices(hub);
for (UsbDevice device : devices) {
UsbDeviceDescriptor descriptor = device.getUsbDeviceDescriptor();
short bcdUsb = descriptor.bcdUSB();
String bcdDecoded = DescriptorUtils.decodeBCD(bcdUsb);
if (Objects.equal(bcdDecoded, "3.00")) {
devicesUSB3.add(device);
}
}
return devicesUSB3;
}
/**
* UsbHubs can either mount UsbDevices or further UsbHubs. This method searches through the tree
* of UsbHubs for UsbDevices and returns them as list.
*/
private List<UsbDevice> getAllUsbDevices(UsbHub hub) {
List<UsbDevice> devices = Lists.newArrayList();
List<UsbDevice> attachedDevices = hub.getAttachedUsbDevices();
for (UsbDevice device : attachedDevices) {
if (device instanceof UsbHub) {
List<UsbDevice> subdevices = getAllUsbDevices((UsbHub) device);
devices.addAll(subdevices);
} else {
devices.add(device);
}
}
return devices;
}
}
PowerShell script:
# $input = "VID_XXXX&PID_XXXX (this line is added in Java Code)
# For given VID and PID of a USB device we search for
# the corresponding logical disk to get the drive letter.
# The chain of objects is:
# PnPEntity (PnP = Plug and Play)
# -> USBController
# -> Some associator of USBController that has a related disk drive
# -> diskDrive
# -> diskPartition
# -> logicalDisk
# Find PnPEntity for given VID and PID
$usbPnPEntity = (gwmi Win32_PnPEntity | where DeviceID -match $input)
# Get USB Controller related to PnP Entity
$usbController = $usbPnPEntity.getRelated("Win32_USBController")
$usbControllerID = $usbController.DeviceID
# Find objects associated with the USB Controller
$query = "ASSOCIATORS OF {Win32_USBController.DeviceID='$usbControllerID'}"
$associators = ([wmisearcher]$query).get()
# Search through associators
foreach ($associator in $associators) {
# Find associator that is related to a disk Drive
$assoDeviceID = $associator.DeviceID
$diskDrive = (gwmi win32_diskdrive | where PNPDeviceID -eq $assoDeviceID)
if($diskDrive){
# Get logical Disk related to the disk drive
$logicalDisk = $diskDrive.getRelated("Win32_DiskPartition").getRelated("Win32_LogicalDisk")
# Print device ID which is the drive letter (e.g. "C:")
$logicalDisk.DeviceID
break
}
}
Maven dependencies:
<dependency>
<groupId>org.usb4java</groupId>
<artifactId>usb4java-javax</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>com.profesorfalken</groupId>
<artifactId>jPowerShell</artifactId>
<version>3.1.1</version>
</dependency>

Java gstreamer linking two textoverlay elements is not working

I have a rtsp player application written in java and built on top of gstreamer 1.When I try to display a text on top of the playing video with only one textoverlay element in the pipeline it is working fine.But I need to display different texts on all the corners of the window.
The first think I though was chaining overlay elements which actually worked from command line with gst-launch-1.0 as follows;
gst-launch-1.0 -v rtspsrc location=rtsp://10.0.5.41:8554 ! rtpjitterbuffer ! rtph264depay ! vaapiparse_h264 ! vaapidecodebin ! textoverlay text = "live video" halignment=left ! textoverlay text="action camera 1" ! xvimagesink
When I try to construct the same pipeline with java as follows;
this.sourceElement = ElementFactory.make(RTSP_SOURCE, RTSP_SOURCE);
final Element videoQueue = ElementFactory.make(QUEUE, QUEUE);
final Element videoDepay = ElementFactory.make(RTP_H264_DEPAY, RTP_H264_DEPAY);
final Element videoParser = ElementFactory.make(VAAPI_H264_PARSE, VAAPI_H264_PARSE);
final Element videoDecoder = ElementFactory.make(VAAPI_DECODE, VAAPI_DECODE);
videoTypeOverlay = ElementFactory.make(TEXT_OVERLAY, TEXT_OVERLAY);
videoSourceOverlay = ElementFactory.make(TEXT_OVERLAY, TEXT_OVERLAY);
sinkElement = ElementFactory.make(XV_IMAGE_SINK, XV_IMAGE_SINK);
pipe.addMany(sourceElement, videoQueue, videoDepay, videoParser, videoDecoder, videoSourceOverlay, videoTypeOverlay, sinkElement);
Element.linkMany(videoQueue, videoDepay, videoParser, videoDecoder, videoSourceOverlay, videoTypeOverlay, sinkElement);
sourceElement.connect((Element.PAD_ADDED) (element, pad) -> {
if (pad.isLinked()) {
return;
}
Caps caps = pad.getCaps();
if (caps.size() > 0) {
String mediaType = caps.getStructure(0).getString("media");
if ("video".equalsIgnoreCase(mediaType)) {
pad.link(videoQueue.getStaticPad("sink"));
}
}
});
#Override
public void play(PlaySettings playSettings) {
videoTimeOverlay.set("text", text);
videoTimeOverlay.set("valignment", valign);
videoTimeOverlay.set("halignment", halign);
...
}
I get a blank canvas.
The only way to make application at least working is to remove one of the overlays from linkMany and addMany lines as follows;
pipe.addMany(sourceElement, videoQueue, videoDepay, videoParser, videoDecoder, videoSourceOverlay, sinkElement);
Element.linkMany(videoQueue, videoDepay, videoParser, videoDecoder, videoSourceOverlay, sinkElement);
So how can I put more than one static overlay strings on video canvas?
I solved this problem by creating a Bin element and the two overlay elements are wrapped in it.
The method below is a working code example for creating Bin container element with ghost pads.
private Bin createTextOverlayBin() {
Bin textOverlayBin = new Bin();
videoTypeOverlay = ElementFactory.make(TEXT_OVERLAY, "video-type-overlay");
Optional<Pad> textOverlaySinkPad = playIdOverlay.getSinkPads().stream().filter(pad -> pad.getName().equals("video_sink")).findAny();
Optional<Pad> textOverlaySrcPad = videoTypeOverlay.getSrcPads().stream().filter(pad -> pad.getName().equals("src")).findAny();
if (textOverlaySinkPad.isPresent() && textOverlaySrcPad.isPresent()) {
textOverlayBin.add(playIdOverlay);
textOverlayBin.add(videoTypeOverlay);
GhostPad ghostSinkPad = new GhostPad("sink", textOverlaySinkPad.get());
textOverlayBin.addPad(ghostSinkPad);
GhostPad ghostSrcPad = new GhostPad("src", textOverlaySrcPad.get());
textOverlayBin.addPad(ghostSrcPad);
playIdOverlay.link(videoTypeOverlay);
} else {
LOGGER.error("Video text overlay element creation is failed!");
}
return textOverlayBin;
}

Bukkit ProtocolLib Nametags

I am trying to change the name above a players entity. I have successfully done this but it has a side effect of changeing the players skin to the default. How can I change the player's nametag without resetting their skin.
Plugin Librarys Used
ProtocolLib
PacketWrapper
Code used to change name
public void changeNameOnHead(Player player, String name) {
PlayerInfoData pid = new
PlayerInfoData(WrappedGameProfile.fromPlayer(player), 1,
EnumWrappers.NativeGameMode.SURVIVAL,
WrappedChatComponent.fromText(player.getName()));
WrapperPlayServerPlayerInfo wpspi = new WrapperPlayServerPlayerInfo();
wpspi.setAction(EnumWrappers.PlayerInfoAction.REMOVE_PLAYER);
wpspi.setData(Collections.singletonList(pid));
for (Player p: Bukkit.getOnlinePlayers()) {
if (p.equals(player)) {
continue;
}
p.hidePlayer(player);
wpspi.sendPacket(p);
}
ProtocolLibrary.getProtocolManager().addPacketListener(
new PacketAdapter(RoleplayEngine.Instance,
PacketType.Play.Server.PLAYER_INFO) {
#Override
public void onPacketSending(PacketEvent event) {
if (event.getPacket().getPlayerInfoAction().read(0) != EnumWrappers.PlayerInfoAction.ADD_PLAYER) {
return;
}
PlayerInfoData pid =
event.getPacket().getPlayerInfoDataLists().read(0).get(0);
if (pid.getProfile().getUUID() !=
player.getUniqueId()) return;
PlayerInfoData newPid = new PlayerInfoData(
pid.getProfile().withName(name),
pid.getPing(),
pid.getGameMode(),
WrappedChatComponent.fromText(name)
);
event.getPacket().getPlayerInfoDataLists().write(0,
Collections.singletonList(newPid));
}
}
);
for (Player p: Bukkit.getOnlinePlayers()) {
if (p.equals(player)) {
continue;
}
p.showPlayer(player);
}
}
You can try to use this library available in github to change the player's name and skin.
Quick example of usage:
PlayerDisplayModifier p = new PublicDisplayModifier(plugin);
p.changeDisplay(myPlayer, "SkinPlayer", "NewName");
This works if your server is 1.8 and lower, not sure if it works on higher versions.
If your server version is higher than 1.8, you can try using NickNamerIntegratedApi, a plugin that also has an API for devs. It's open sourced, so you can probably dig for the piece of code that makes the nick change possible
Lastly, you can also try to use md-5's iTAG, and a good fork of it by ataranlen

Finding if printer is online and ready to print

The following 4 questions didn't help, therefore this isn't a duplicate:
ONE, TWO, THREE, FOUR
I need to find a way to discover if the Printer that my system reports is available to print or not.
Printer page:
In the picture, the printer "THERMAL" is available to print, but "HPRT PPTII-A(USB)" isn't available to print. The System shows me that, by making the non-available printer shaded
Using the following code, I'm able to find all the printers in the system
public static List<String> getAvailablePrinters() {
DocFlavor flavor = DocFlavor.SERVICE_FORMATTED.PRINTABLE;
PrintRequestAttributeSet aset = new HashPrintRequestAttributeSet();
PrintService[] services = PrintServiceLookup.lookupPrintServices(flavor, aset);
ArrayList<String> names = new ArrayList<String>();
for (PrintService p : services) {
Attribute at = p.getAttribute(PrinterIsAcceptingJobs.class);
if (at == PrinterIsAcceptingJobs.ACCEPTING_JOBS) {
names.add(p.getName());
}
}
return names;
}
output:
[HPRT PPTII-A(USB), THERMAL]
The problem is: This code shows all the printers that the system have ever installed.
What I need: This list should contain only the really available printers to print. In this example, it should only show "THERMAL", and not show "HPRT PPTII-A(USB)"
How can this be achieved?
If it is okay that the solution is Windows-specific, try WMI4Java. Here is my situation:
As you can see, my default printer "Kyocera Mita FS-1010" is inactive (greyed out) because I simply switched it off.
Now add this to your Maven POM:
<dependency>
<groupId>com.profesorfalken</groupId>
<artifactId>WMI4Java</artifactId>
<version>1.4.2</version>
</dependency>
Then it is as easy as this to list all printers with their respective status:
package de.scrum_master.app;
import com.profesorfalken.wmi4java.WMI4Java;
import com.profesorfalken.wmi4java.WMIClass;
import java.util.Arrays;
public class Printer {
public static void main(String[] args) {
System.out.println(
WMI4Java
.get()
.properties(Arrays.asList("Name", "WorkOffline"))
.getRawWMIObjectOutput(WMIClass.WIN32_PRINTER)
);
}
}
The console log looks as follows:
Name : WEB.DE Club SmartFax
WorkOffline : False
Name : Send To OneNote 2016
WorkOffline : False
Name : Microsoft XPS Document Writer
WorkOffline : False
Name : Microsoft Print to PDF
WorkOffline : False
Name : Kyocera Mita FS-1010 KX
WorkOffline : True
Name : FreePDF
WorkOffline : False
Name : FinePrint
WorkOffline : False
Name : Fax
WorkOffline : False
Please note that WorkOffline is True for the Kyocera printer. Probably this is what you wanted to find out.
And now a little modification in order to filter the printers list so as to only show active printers:
WMI4Java
.get()
.properties(Arrays.asList("Name", "WorkOffline"))
.filters(Arrays.asList("$_.WorkOffline -eq 0"))
.getRawWMIObjectOutput(WMIClass.WIN32_PRINTER)
Update: I was asked how to get a list of active printer names. Well, this is not so easy due to a shortcoming in WMI4Java for which I have just filed a pull request. It causes us to parse and filter the raw WMI output, but the code is still pretty straightforward:
package de.scrum_master.app;
import com.profesorfalken.wmi4java.WMI4Java;
import com.profesorfalken.wmi4java.WMIClass;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class Printer {
public static void main(String[] args) {
String rawOutput = WMI4Java
.get()
.properties(Arrays.asList("Name", "WorkOffline"))
.filters(Arrays.asList("$_.WorkOffline -eq 0"))
.getRawWMIObjectOutput(WMIClass.WIN32_PRINTER);
List<String> printers = Arrays.stream(rawOutput.split("(\r?\n)"))
.filter(line -> line.startsWith("Name"))
.map(line -> line.replaceFirst(".* : ", ""))
.sorted()
.collect(Collectors.toList());
System.out.println(printers);
}
}
The console output looks like this:
[Fax, FinePrint, FreePDF, Microsoft Print to PDF, Microsoft XPS Document Writer, Send To OneNote 2016, WEB.DE Club SmartFax]
UPDATE:
Instead of querying WMI "win32_printer" object I would recommend using Powershell directly like this, its much cleaner API :
Get-Printer | where PrinterStatus -like 'Normal' | fl
To see all the printers and statuses:
Get-Printer | fl Name, PrinterStatus
To see all the attributes:
Get-Printer | fl
You can still use ProcessBuilder in Java as described below.
Before update:
Windows solution, query WMI "win32_printer" object:
public static void main(String[] args) {
// select printer that have state = 0 and status = 3, which indicates that printer can print
ProcessBuilder builder = new ProcessBuilder("powershell.exe", "get-wmiobject -class win32_printer | Select-Object Name, PrinterState, PrinterStatus | where {$_.PrinterState -eq 0 -And $_.PrinterStatus -eq 3}");
String fullStatus = null;
Process reg;
builder.redirectErrorStream(true);
try {
reg = builder.start();
fullStatus = getStringFromInputStream(reg.getInputStream());
reg.destroy();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
System.out.print(fullStatus);
}
For converting InputStream to String look here: comprehensive StackOverflow answer, or you can simply use:
public static String getStringFromInputStream(InputStream is) {
ByteArrayOutputStream result = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length;
try {
while ((length = is.read(buffer)) != -1) {
result.write(buffer, 0, length);
}
} catch (IOException e1) {
e1.printStackTrace();
}
// StandardCharsets.UTF_8.name() > JDK 7
String finalResult = "";
try {
finalResult = result.toString("UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return finalResult;
}
Output:
Name PrinterState PrinterStatus
---- ------------ -------------
Foxit Reader PDF Printer 0 3
Send to OneNote 2010 0 3
Microsoft XPS Document Writer 0 3
Microsoft Print to PDF 0 3
Fax 0 3
\\192.168.50.192\POS_PRINTER 0 3
As you can see, you now have all the printers that are in working state in the string.
You can use your existing method (getAvailablePrinters()) and e.g. add something like this:
ArrayList<String> workingPrinter = new ArrayList<String>();
System.out.println("Working printers:");
for(String printer : getAvailablePrinters()){
if(fullStatus.contains("\n" + printer + " ")){ // add a newline character before the printer name and space after so that it catches exact name
workingPrinter.add(printer);
System.out.println(printer);
}
}
And now you will have a nice list of working printers.
Console output:
Working printers:
Send to OneNote 2010
Foxit Reader PDF Printer
Microsoft XPS Document Writer
Microsoft Print to PDF
Fax
\\192.168.50.192\POS_PRINTER
Of course you have to be careful with the names with this approach - e.g. if "POS_PRINTER" is in all printers but not in working printers list, it could still get added to the workingPrinters list, if there is a working printer named "POS_PRINTER 1" as that name contains "\nPOS_PRINTER " string...

Printing with Attributes(Tray Control, Duplex, etc...) using javax.print library

I've been trying for some time to determine a way to use the standard Java Print library to print files - specifically, PDF documents - with certain attributes - specifically, to certain trays or using duplex.
There exists plenty of documentation on how this should be done, and indeed, I've researched and tried these methods. The typical way is something like this:
public static void main (String [] args) {
try {
PrintService[] pservices = PrintServiceLookup.lookupPrintServices(null, null);
//Acquire Printer
PrintService printer = null;
for (PrintService serv: pservices) {
System.out.println(serv.toString());
if (serv.getName().equals("PRINTER_NAME_BLAH")) {
printer = serv;
}
}
if (printer != null) {
System.out.println("Found!");
//Open File
FileInputStream fis = new FileInputStream("FILENAME_BLAH_BLAH.pdf");
//Create Doc out of file, autosense filetype
Doc pdfDoc = new SimpleDoc(fis, DocFlavor.INPUT_STREAM.AUTOSENSE, null);
//Create job for printer
DocPrintJob printJob = printer.createPrintJob();
//Create AttributeSet
PrintRequestAttributeSet pset = new HashPrintRequestAttributeSet();
//Add MediaTray to AttributeSet
pset.add(MediaTray.TOP);
//Add Duplex Option to AttributeSet
pset.add(Sides.DUPLEX);
//Print using Doc and Attributes
printJob.print(pdfDoc, pset);
//Close File
fis.close();
}
}
catch (Throwable t) {
t.printStackTrace();
}
}
In short, you do the following
Find the Printer
Create a PrinterJob
Create an AttributeSet
Add Attributes to the AttributeSet, such as Tray and Duplex
Call print on the printer job using the AttributeSet
The problem here is that, despite being the documented way of doing this, as well as what I've found from several tutorials, this method... doesn't work. Now keep in mind, I know that doesn't sound very descript, but hear me out. I don't say that lightly...
The official documentation for PrinterJob actually mentions that the AttributeSet is ignored in the default implementation. Source code seen here shows this to be true - the attributes are passed in and ignored entirely.
So apparently, you need some sort of extended version of the class, which is possibly based on the specific printers and their capabilities? I attempted to write some test code that would tell me such capabilities - we have a large variety of printers set up at the office, large or small, simple or full of bells and whistles - not to mention several drivers on my computer just for pseudo-printer drivers that just create documents and simulate printers without going to any sort of hardware. The test code is as follows:
public static void main (String [] args) {
PrintService[] pservices = PrintServiceLookup.lookupPrintServices(null, null);
for (PrintService serv: pservices) {
System.out.println(serv.toString());
printFunctionality(serv, "Trays", MediaTray.class);
printFunctionality(serv, "Copies", Copies.class);
printFunctionality(serv, "Print Quality", PrintQuality.class);
printFunctionality(serv, "Color", ColorSupported.class);
printFunctionality(serv, "Media Size", MediaSize.class);
printFunctionality(serv, "Accepting Jobs", PrinterIsAcceptingJobs.class);
}
}
private static void printFunctionality(PrintService serv, String attrName, Class<? extends Attribute> attr) {
boolean isSupported = serv.isAttributeCategorySupported(attr);
System.out.println(" " + attrName + ": " + (isSupported ? "Y" : "N"));
}
The results I found were that every printer, without exception, returned that "copies" were supported, and all other attributes were not. Furthermore, every printer's capabilities were identical, regardless of how implausible that would seem.
The inevitable question is multi-layered: How does one send in attributes in a way that they are registered? Additionally, how does one properly detect the capabilities of a printer? Indeed, is the PrinterJob class actually extended in a usable way at all, or are the Attributes always ignored?
Examples I've found throughout The Internet seem to suggest to me that the answer to the latter question is "No, they are always ignored", which seems ridiculous to me (but increasingly more believable as I sift through hundreds of pages). Is this code that Sun simply set up but never got working to a completed state? If so, are there any alternatives?
The problem is that the the Java print API is a bridge between worlds. Printer manufacturers don't release drivers for the JVM. They release drivers for Windows, Macintosh, and maybe someone has a a driver for a given printer that works on one or more *nix platforms.
Along you come with some Java code running inside a JVM on some host system. When you start querying the printer features, you aren't talking to the printers -- you are talking to a bridge class in java.awt.print that hook into the JVM, which hooks to the host operating system, which hooks into whatever particular driver was installed for a given printer. So there are several places where this can fall apart... The particular JVM you are on may or may not fully implement the API for querying printer features, let alone passing those parameters along for a given job.
A few suggestions:
look into the javax.print classes as an alternative to
java.awt.print -- I've had more luck printing from there.
try using alternative print drivers for your printers -- you can define
multiple named connections to a given printer, each with a different
driver. If you've got a manufacturer provided driver, try a more generic driver, if you've got a generic driver, try to install a more specific one.
run your code under alternate JVM implementations for your platform
So, we inevitably found a way to print to different trays and with different settings, but not directly. We found it impossible to send attributes via the printJob.print method, and that much hasn't changed. However, we were able to set the name of the print job, then intercept the print job with a low-level Perl script, parse the name, and set the tray and duplex settings there. It's an extreme hack, but it works. It still remains true that Java Printer Attributes do not work, and you will need to find another way if you want to set them.
We had similar requirement to print PDF's and wanted to send some pages to Specific tray and also wanted the document to be stapled.
We used Java code + ghost script combination
First convert PDF to ghost script and then add PJL (Print job language) commands to ghost script file to select trays and staple the documents.
Then send that edited ghost script file to printer.
Here is complete example written in Java
http://reddymails.blogspot.com/2014/07/how-to-print-documents-using-java-how.html
-Ram
Here's what it looks like in javafx Tray's may vary and it will also print out all trays that are available just change the tray name
private void printImage(Node node) {
PrinterJob job = PrinterJob.createPrinterJob();
if (job != null) {
JobSettings js = job.getJobSettings();
PaperSource papersource = js.getPaperSource();
System.out.println("PaperSource=" + papersource);
PrinterAttributes pa = printer.getPrinterAttributes();
Set<PaperSource> s = pa.getSupportedPaperSources();
System.out.println("# of papersources=" + s.size());
if (s != null) {
for (PaperSource newPaperSource : s) {
System.out.println("newpapersource= " + newPaperSource);
//Here is where you would put the tray name that is appropriate
//in the contains section
if(newPaperSource.toString().contains("Tray 2"))
js.setPaperSource(newPaperSource);
}
}
job.getJobSettings().setJobName("Whatever");
ObjectProperty<PaperSource> sources = job.getJobSettings().paperSourceProperty();
System.out.println(sources.toString());
boolean success = job.printPage(node);
if (success) {
System.out.println("PRINTING FINISHED");
job.endJob();
//Stage mainStage = (Stage) root.getScene().getWindow();
//mainStage.close();
}
}
}
Here's My output:
PaperSource=Paper source : Automatic
# of papersources=6
newpapersource= Paper source :
newpapersource= Paper source : Manual Feed in Tray 1
newpapersource= Paper source : Printer auto select
newpapersource= Paper source : Tray 1
newpapersource= Paper source : Tray 2
newpapersource= Paper source : Form-Source
ObjectProperty [bean: Collation = UNCOLLATED
Copies = 1
Sides = ONE_SIDED
JobName = Whatever
Page ranges = null
Print color = COLOR
Print quality = NORMAL
Print resolution = Feed res=600dpi. Cross Feed res=600dpi.
Paper source = Paper source : Tray 2
Page layout = Paper=Paper: Letter size=8.5x11.0 INCH Orient=PORTRAIT leftMargin=54.0 rightMargin=54.0 topMargin=54.0 bottomMargin=54.0, name: paperSource, value: Paper source : Tray 2]
PRINTING FINISHED
I've found the trick for the printer trays is to iterate over the Media.class using getSupportedAttributeValues(...), match the human-readable name, and select that particular value. Tested on Windows, MacOS with several tray configurations.
String tray = "1";
// Handle human-readable names, see PRINTER_TRAY_ALIASES usage below for context. Adjust as needed.
List<String> PRINTER_TRAY_ALIASES = Arrays.asList("", "Tray ", "Paper Cassette ");
// Get default printer
PrintService printService = PrintServiceLookup.lookupDefaultPrintService();
// Attributes to be provided at print time
PrintRequestAttributeSet pset = new HashPrintRequestAttributeSet();
Media[] supported = printService.getSupportedAttributeValues(Media.class, null, null);
for(Media m : supported) {
for(String pta : PRINTER_TRAY_ALIASES) {
// Matches "1", "Tray 1", or "Paper Cassette 1"
if (m.toString().trim().equalsIgnoreCase(pta + tray)) {
attributes.add(m);
break;
}
}
}
// Print, etc
// printJob.print(pdfDoc, pset);

Categories