Printing a bufferedImage

I am trying to create the screenshot capability.
Our application can run on multiple monitors and when
we click on the print screen button on one monitor. Then only that monitor
should send the screen to printed (to a physical printer) with all the pop ups,headers,footers visible.

I have found how to create a screenshot but it creates a BufferedImage but to print you need a component.

In Jython is there a way to convert a BufferedImage to a component?

Just to help me understand what you’re doing, would you mind posting the code you used to generate the screenshot? Also, what version of Ignition are you running?

The reason I ask is, if you’re using Jython to get the BufferedImage, then there might be an easier way. I haven’t fully tested this myself, but I have done similar things, and so I think you can just use the parent desktop using the following code (or something similar):

#name should be the name of the desktop you're trying to capture
name = system.gui.getCurrentDesktop() 

#the goal here is to get the highest level container, and print everything in it
masterContainer = system.gui.desktop(name).getOpenedWindows()[0].parent

#Here you have a few options in Ignition's system.print:
if YouWantToSaveAnImageAndPrintItManually:
        system.print.printToImage(masterContainer)
elif YouWantToPrintTheImageDirectly:
        job = system.print.createPrintJob(masterContainer)
        job.print()

If you’re using a version of ignition prior to 7.9.2, then you won’t be able to use system.gui.getCurrentDesktop. An easy workaround for this is to pass the desktop handle into the window as a string parameter and then reference that to get the desktop.

I hope this helps!

2 Likes

Hey Zac,

I am going to try out your suggestion.
Below is my current code.
I am using version 7.9.3 of ignition.

from java.awt.image import BufferedImage
from java.awt import Rectangle
from java.awt import Robot 
from java.awt import Graphics #this is not being used 
from java.awt import Component #this is not being used 

#Obtain custom property from Header Monitor Number
monNum = str(event.source.parent.monNum)

#Gets the Monitor size from the Client Tags (x,y,width,height)
monX = system.tag.read('[Client]GraphicsInfo/Display'+monNum+'/monitorX')
monY = system.tag.read('[Client]GraphicsInfo/Display'+monNum+'/monitorY')
monW = system.tag.read('[Client]GraphicsInfo/Display'+monNum+'/monitorW')
monH = system.tag.read('[Client]GraphicsInfo/Display'+monNum+'/monitorH')

#Message Box for testing 
system.gui.messageBox("Monitor Num:" + monNum + "\n mon X: %d \n mon Y: %d \n mon H: %d \n mon W: %d \n" %(monX.value,monY.value,monW.value,monH.value))

#creates Rectangle (x,y,width,height)
rec = Rectangle(monX.value,monY.value,monW.value,monH.value)

bot = Robot()
#returns a Buffered Image of the rectangle
img = bot.createScreenCapture(rec)

#stuck at this point
#need to convert BufferedImage to Component 

#This is a project script 
project.Print.printComponent(img)
# prints component with inverted colours (used with screens that are mostly black)
#def printComponent(comp):
#	job = system.print.createPrintJob(comp)
#	job.print()

When I run your code I get all three of my monitors as seen in the image below.

I only want to get only one of the monitors.

Hey there, sorry again for the delayed response. Been pretty busy on my end. Did you get this fixed?

So, off the top of my head, the solution to the issue where you’re getting too much info in your screenshot would be to fine tune the definition of “masterContainer” (referencing my code example from the previous comment).

So, for example, you might do:

# used to be:
# masterContainer = system.gui.desktop(name).getOpenedWindows()[0].parent
masterContainer = system.gui.desktop(name).getOpenedWindows()[0]

Also, if you have the handle of the desktop you want handy, maybe call it directly instead of using “getCurrentDesktop”. The goal is to narrow the scope of the screencap to be only the container you want to capture; this might require some trial and error to figure out how the java/ignition containers are structured in your project.

Hi Zac

I did mange to figure it out. I mostly used the java library instead of ignition because I couldn’t get the screen capture to work quite right.

from java.awt import Robot, Rectangle
from javax.imageio import ImageIO
from java.io import File, ByteArrayOutputStream
from javax.print import Doc, DocFlavor, DocPrintJob, PrintService, PrintServiceLookup, SimpleDoc, ServiceUI 
from javax.print.attribute import HashPrintRequestAttributeSet, HashDocAttributeSet
from javax.print.attribute.standard import Copies, OrientationRequested

#Obtain custom property from header Monitor Number
monNum = str(event.source.parent.monNum)

#Obtain the X cordinate offset
xOffset = system.gui.getParentWindow(event).parent.getTopLevelAncestor().x
#Obtain the Y cordinate offset
yOffset = system.gui.getParentWindow(event).parent.getTopLevelAncestor().y

#Gets the monitor size from the Client Tags (x,y,width,height)
monX = system.tag.read('[Client]GraphicsInfo/Display'+monNum+'/monitorX')
monY = system.tag.read('[Client]GraphicsInfo/Display'+monNum+'/monitorY')
monW = system.tag.read('[Client]GraphicsInfo/Display'+monNum+'/monitorW')
monH = system.tag.read('[Client]GraphicsInfo/Display'+monNum+'/monitorH')

#Message Box for testing 
#system.gui.messageBox("Monitor Num:" + monNum + "\n mon X: %d \n mon Y: %d \n mon H: %d \n mon W: %d \n x Offset: %d \n y Offset: %d \n" %(monX.value,monY.value,monW.value,monH.value,xOffset,yOffset))

#creates a rectangle (x,y,width,height)
rec = Rectangle((monX.value+xOffset),(monY.value+yOffset),monW.value,monH.value)

#Creates a screen Capture and returns a BufferedImage
bot = Robot()
bimage = bot.createScreenCapture(rec)

#Converts Screen Capture to Byte Array so we can print it.
baos=ByteArrayOutputStream()                     
ImageIO.write(bimage, "png", baos)
baos.flush
byteArray=baos.toByteArray()
baos.close

#Set Default Print job Attributes
DocAttributeSet = HashDocAttributeSet()
printJobAttributeSet = HashPrintRequestAttributeSet()
printJobAttributeSet.add(Copies(1))
printJobAttributeSet.add(OrientationRequested.LANDSCAPE)

#Gets a list avaliable printers
services = PrintServiceLookup.lookupPrintServices( DocFlavor.BYTE_ARRAY.PNG, None )

#Creates Print Setting Dialogue Box 
#(graphics, x, y , list of printers, default printer, flavor, attributes)
service = ServiceUI.printDialog(None,(monX.value+xOffset)+ 150 ,(monY.value +yOffset) + 150 , services, PrintServiceLookup.lookupDefaultPrintService() , None, printJobAttributeSet)

#Do nothing if the Print Dialogue is Cancelled 
if (service != None):

	#Create document
	doc=SimpleDoc(byteArray, DocFlavor.BYTE_ARRAY.PNG, DocAttributeSet)
	
	#Prints that document
	job=service.createPrintJob()
	job.print(doc,printJobAttributeSet)
3 Likes

Great! Glad you got it working. Also, thanks for the code. I might mess around with this a bit, to see if I can get some fancier screenshot work in my own projects.