Overview
system.print.createPrintJob() wraps a single Swing component into one print job — calling it in a loop spawns one dialog per window. To produce a single multi-page PDF (or print job) covering an entire Ignition Vision project, you must either (a) assemble a java.awt.print.Book of Printable pages from each root container, or (b) rasterize each window into a BufferedImage and stitch the images into a PDF using a library such as Apache PDFBox bundled in your Ignition gateway.
time.sleep() inside a Vision button script. The EDT (Event Dispatch Thread) is blocked, so windows never finish rendering before you capture them. Open all windows in one pass, then use system.util.invokeLater() to defer the capture/print call until the EDT is free to paint.Prerequisites
| Requirement | Detail |
|---|---|
| Ignition version | 8.1.x (Vision module); Jython 2.7 scripting engine |
| Script trigger | Button actionPerformed or mouseClicked event on a Vision client |
| Window permissions | All target windows must be accessible to the logged-in user; parameter-required windows need default or dummy parameters to prevent red overlays |
| PDF output | Method A: OS print-to-PDF dialog (no gateway dependency). Method B: Apache PDFBox JAR on gateway classpath (place in /usr/local/ignition/user-lib/pylib/) |
| Ignition docs | system.gui.getWindowNames · system.nav.openWindow · system.print |
Method A — Java AWT Book: Single Print Dialog, Multiple Pages
This approach uses java.awt.print.Book (implements Pageable) to collect one Printable per window, then submits the whole book in a single PrinterJob. Selecting "Print to PDF" or "Microsoft Print to PDF" in the OS dialog writes all pages to one file.
# Button actionPerformed — Ignition Vision 8.1
# Jython 2.7
from java.awt.print import PrinterJob, Book, PageFormat, Printable
from java.awt import RenderingHints
import math
# ── 1. Define which windows to capture ──────────────────────────────────────
# Option A: all windows in the project
targetWindows = system.gui.getWindowNames()
# Option B: explicit subset (recommended for production)
# targetWindows = ['Overview', 'Process/Reactor', 'Alarms/ActiveAlarms']
# ── 2. Open every window (no sleep — EDT must stay free) ─────────────────────
openedRoots = []
for name in targetWindows:
try:
win = system.nav.openWindow(name) # opens if not already open
openedRoots.append(win.getRootContainer())
except Exception as e:
system.util.getLogger('PDFExport').warn('Skipping %s: %s' % (name, str(e)))
# ── 3. Build a Printable wrapper for each root container ─────────────────────
class RootPrintable(Printable):
def __init__(self, component):
self.component = component
def print(self, g2d, pf, pageIndex):
if pageIndex > 0:
return Printable.NO_SUCH_PAGE
g2d.translate(pf.getImageableX(), pf.getImageableY())
iw = pf.getImageableWidth()
ih = pf.getImageableHeight()
scaleX = iw / float(self.component.getWidth())
scaleY = ih / float(self.component.getHeight())
scale = min(scaleX, scaleY)
g2d.scale(scale, scale)
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON)
self.component.paint(g2d)
return Printable.PAGE_EXISTS
# ── 4. Defer actual print call until EDT finishes painting all windows ─────────
def submitPrintJob():
pj = PrinterJob.getPrinterJob()
book = Book()
pf = pj.defaultPage()
pf.setOrientation(PageFormat.LANDSCAPE)
for rc in openedRoots:
book.append(RootPrintable(rc), pf)
pj.setPageable(book)
pj.setJobName('IgnitionProjectSnapshot')
if pj.printDialog(): # shows ONE dialog for all pages
pj.print()
system.util.invokeLater(submitPrintJob)
system.util.invokeLater() queues submitPrintJob to run after all pending paint events clear. This guarantees root containers have rendered before component.paint(g2d) is called. On slow clients add a second invokeLater nesting for extra margin.Method B — BufferedImage Rasterization to Disk
Use this when you need PNG snapshots for archiving or want to drive a Python PDF library rather than the OS print dialog. Each window is painted into a BufferedImage and saved to a timestamped folder accessible from the gateway file system or a network share.
from java.awt.image import BufferedImage
from java.awt import RenderingHints
from javax.imageio import ImageIO
from java.io import File
import system, os
# Destination directory (gateway-accessible path via system.file or UNC)
OUT_DIR = 'C:/IgnitionSnapshots'
ts = system.date.format(system.date.now(), 'yyyyMMdd_HHmmss')
outPath = os.path.join(OUT_DIR, ts)
os.makedirs(outPath)
targetWindows = ['Overview', 'Process/Boiler', 'Alarms/ActiveAlarms']
def captureWindows():
for name in targetWindows:
try:
win = system.nav.openWindow(name)
rc = win.getRootContainer()
w, h = rc.getWidth(), rc.getHeight()
img = BufferedImage(w, h, BufferedImage.TYPE_INT_RGB)
g = img.createGraphics()
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON)
rc.paint(g)
g.dispose()
safeName = name.replace('/', '_').replace(' ', '_')
ImageIO.write(img, 'PNG', File('%s/%s.png' % (outPath, safeName)))
except Exception as e:
system.util.getLogger('SnapExport').error('%s failed: %s' % (name, str(e)))
system.util.invokeLater(captureWindows)
system.gui.messageBox('Snapshots saved to:\n' + outPath)
Window Selection and Parameter Handling
Windows with required navigation parameters throw exceptions on openWindow(name) if no parameters are passed. Use system.nav.openWindow(name, params) where params is a PyDictionary. Maintain a configuration dictionary at the project script level:
# Project script: shared.pdf.windowConfig
WINDOW_MAP = {
'Overview': {},
'Process/Reactor': {'unitID': 1, 'mode': 'view'},
'Process/Boiler': {'unitID': 2, 'mode': 'view'},
'Alarms/ActiveAlarms': {},
'Reports/ShiftSummary': {'shift': 'Day'},
}
# In your button script, replace the openWindow loop:
for name, params in shared.pdf.windowConfig.WINDOW_MAP.items():
win = system.nav.openWindow(name, params)
openedRoots.append(win.getRootContainer())
| Scenario | Recommended Approach | Key API |
|---|---|---|
| All project windows → single OS-dialog PDF | Method A (Book/Pageable) | java.awt.print.Book |
| Subset of windows → PNG archive | Method B (BufferedImage) | javax.imageio.ImageIO |
| One window → quick print | system.print.createPrintJob(rc) |
Ignition built-in |
| Scheduled quarterly report | Method B + gateway timer script |
system.util.invokeLater inside client event |
Verification
- Add a
system.util.getLogger('PDFExport').info('Opened: ' + name)inside the open loop. Check Gateway → Diagnostics → Logs filtered onPDFExportto confirm each window opened without exceptions. - For Method A: after the print dialog appears, verify the page count in the dialog matches
len(targetWindows). A mismatch indicates a window failed to open and was skipped. - For Method B: navigate to
OUT_DIRand confirm one PNG per window name, file size > 50 KB (near-zero size = paint called before component rendered — add a secondinvokeLaternesting). - Verify no "red overlay" placeholders appear in captured images — this means a required window parameter was missing. Update
WINDOW_MAPwith correct defaults. - Test with a non-admin user account to confirm all target windows are within the user's role permissions before scheduling.
FAQ
Why does my captured image show a blank or red window?
The root container painted before the Vision window finished rendering. Wrap your capture logic in a nested system.util.invokeLater() call so the EDT processes all pending paint events first. Red overlays indicate a missing required navigation parameter — pass default values via system.nav.openWindow(name, {'param': value}).
How do I get one PDF file instead of one print dialog per window?
Replace looped system.print.createPrintJob() calls with a java.awt.print.Book containing one Printable per window, then submit it via a single PrinterJob.printDialog() call. The OS "Print to PDF" driver writes all pages to one file.
Can I run this from a gateway timer script instead of a client button?
No. system.nav.openWindow(), getRootContainer(), and AWT painting only work on the Vision client EDT. Schedule the export by firing a client message handler via system.util.sendMessage() from a gateway timer, then execute the print logic inside the client handler.
What Ignition scripting function returns all window names in the project?
system.gui.getWindowNames() returns a list of strings for every window in the open Vision client project. To limit scope, replace this with a hardcoded Python list: targetWindows = ['Overview', 'Alarms/Active'].
How do I set page orientation and margins for each page in the Book?
Create a PageFormat via PrinterJob.defaultPage(), call pf.setOrientation(PageFormat.LANDSCAPE), then set custom margins by creating a java.awt.print.Paper object, calling paper.setImageableArea(x, y, w, h) in points (72 pt = 1 inch), and applying with pf.setPaper(paper) before passing pf to book.append().