In recent years, Ex Libris — the Library systems supplier of the Alma Library Management System — have made significant improvements to the Alma printing stack. Alma is a web-based, cloud-hosted solution, that until recently relied upon customers' email for printing. Print jobs would be sent by email to a nominated customer Inbox representing the print queue. It would then be up to the customer to process and print the incoming emails, and while this works, this method of printing required some effort on the part of the customer to implement a solution, and such solutions (particularly in our case) were fragile and prone to failure.
A few years back, Ex Libris introduced two new methods of printing Alma printouts in addition to email. So-called "Quick printing", where the Alma web app passes the print data to the browser, and the browser receives a signal to invoke the browser print dialogue. This is useful for occasional ad hoc printing, and is assumed to have been made possible because of the availability of new printing APIs in modern web browsers. However, this is not useful for printing scenarios where a workflow is involved, because multiple clicks would be required, which would soon add up to an increase in time spent by the operator processing the items. This is where the second improvement made in recent years comes to the fore. The HTML print content can now be fetched via a new Alma Printing API, and Ex Libris's Alma Print Daemon provides a client solution for customers to leverage this improvement. However, the Alma Print Daemon appears to have some downsides:
- being an Electron app it is very large; the installer is over 100MB!
- it is built on an old version of
node.js
, and I have tried & failed to repackage withnode.js
16, due to a problem with the dependencynode-native-printer
- it only supports standard paper sizes. We want to be able to print to
Roll Paper 80 x 297 mm
on an Epson TM series POS printer, and attempts to do this with the daemon results in very small printouts
This repository is an attempt to address the above downsides, although in its current state it has a few downsides of its own, which are explained later on.
The Powershell script contained in this repository polls the Alma print queue for new jobs. If it finds any it will send the job to the specified printer and mark the job as Printed
.
The intention is that the script runs in the background, with its window minimised to the taskbar by default. If something goes wrong then the taskbar icon can be clicked to show the above window.
There are tasks that need to be done in the Alma web UI Configuration area, so that the Alma printer is available as an online queue, which are explained here. For the purposes of running this script you will need to get an API key to use.
Once you have an API key, you'll want to make it available for the script to use:
Set-Location <script-dir>
. .\FetchAlmaPrint.ps1;Invoke-Setup
After running the above commands, you will be prompted to enter your API key, which you can paste in. This is then stored in an XML file for future use by the script.
The second thing you'll want to do is determine the Alma print queue to monitor:
. .\FetchAlmaPrint.ps1;Fetch-Printers
This will list all of the Alma printers. You'll want to note the Printer ID for the queue you're interested in printing from.
(note that you only need to dot source the script if it's no longer in memory, i.e. you closed the Powershell window between invocations)
Thirdly, and lastly, you'll want to run the Fetch-Jobs
function, with the parameters set according to the requirements, e.g.:
. .\FetchAlmaPrint.ps1;Fetch-Jobs -printerId "848838010001381" -localPrinterName "EPSON TM-T88III Receipt" -checkInterval 20
In a production environment, it is the Fetch-Jobs
function that will run in the background, polling for and printing new print jobs as they are generated at the back-end.
The script works by running a loop to check the queue every X
seconds. The number of seconds can be changed with the -checkInterval
parameter of the Fetch-Jobs
function. By default, this is 30
seconds. The communications with Alma's API is achieved with Powershell's Invoke-RestMethod
.
The HTML content is saved to a file stored in the tmp_printouts
subdirectory. Each file contains the content for one printout, and the filename will contain the letterId
(the ID assigned by Alma to the printout). If something related to printing goes wrong, but the "fetch" from Alma succeeded, it would then be possible to manually print the content from the stored file, e.g. by opening this file in a web browser and Ctrl + P.
Rendering and printing the HTML is achieved using an Internet Explorer COM object. This is probably the weakest area of the script; there is currently no way to internally specify which printer to print to, so the script relies upon setting the printer specified by -localPrinterName
as the Windows default printer when there is a job to print, restoring the default printer afterwards. The script also does something quite similar for the Page Setup
settings (margins, etc), reading in the existing settings, changing them when there are jobs to print, and restoring the original settings when done. Though these settings are not printer-specific, typically we'll want different values according to the printer we want to print to.
By default, the script will only fetch printouts that have the Alma printout status Pending
. Once the printout has been printed, the script attempts to change the status to Printed
. There is a Fetch-Jobs
function parameter -printoutsWithStatus
that can be added, which can be used to fetch printouts with other statuses (Printed
, Pending
, Canceled
, ALL
). For example, -printoutsWithStatus 'Canceled'
What follows is the setup required to start Fetch-Jobs
automatically upon logon, and a simple way for non-technical staff to re-launch the script if something untoward happens and the running instance needs to be restarted.
In a so-called production environment, it is recommended that two identical script shortcut (.lnk
) files be created. These should be put in the shell:startup
(C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp
) and shell:desktop
(C:\Users\Public\Desktop
) directories. The shortcut in shell:startup
ensures the script runs automatically upon logon, and the shortcut in shell:desktop
provides an option to manually invoke the script, if the Powershell window was accidentally closed. The .lnk
files will ensure that the script will run minimised, so as to run discretely in the background, lessening the chance of an operator accidentally closing the window.
The script DeployShortcuts.ps1
is available in the setup-scripts
subdirectory, to make it easier to create and deploy the .lnk
shortcut files. It is highly recommended to create the shortcuts using this script, because of File Explorer's 260 character limit in respect of viewing and editing shortcut properties' Target
field. This limit will very likely be exceeded when including additional parameters, and in some environments with e.g. very long printer names, which need to be included as parameter values.
- Open an elevated Powershell window
- Type
Set-Location 'C:\fmsys-alma-printing-api\setup-scripts'
(or the path to where the repo is, plus\setup-scripts
) - Copy the following, editing the parameter values with
<>
placeholder characters, and adding any additional required parameters:
.\DeployShortcuts.ps1 -ShortcutFilename 'Alma Slip Printing' -ShortcutArguments "-NoLogo -NoProfile -Command `"& { Start-Sleep 30;. .\FetchAlmaPrint.ps1;Fetch-Jobs -checkInterval 15 -printerId '<printerId>' -localPrinterName '<printerName>' }`""
- Paste the resulting line into your Powershell window and press
CR
to run the script
The script also allows one to inspect the properties of an existing .lnk
shortcut file:
- Open a Powershell window
- Copy the following, editing the
-ListOnlyFilePath
parameter value if you want inspect a different shortcut:
.\DeployShortcuts.ps1 -ListOnlyFilePath "C:\Users\Public\Desktop\Alma Printing.lnk"
See also UoY-specific notes for a list of UoY -ShortcutArguments
.
These are included here "just in case".
To create the shortcuts manually, take the following steps:
-
Assuming this repository is in
C:\fmsys-alma-printing-api
, create a new shortcut in this directory:- In File explorer, right-click in the whitespace, and from the menu left-click
New
>Shortcut
- In the resulting wizard, enter
powershell
for the location of the item. Click theNext
button - In the next screen, give the shortcut a suitable name like
Alma Slip Printing
. Click theFinish
button
- In File explorer, right-click in the whitespace, and from the menu left-click
-
Now make some modifications to your new shortcut file:
- Right-click the shortcut file, and from the menu left-click
Properties
- In the
Shortcut
tab of the resulting dialogue, edit the following fields:
- Right-click the shortcut file, and from the menu left-click
Target:
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -NoLogo -NoProfile -Command "& { Start-Sleep 30;. .\FetchAlmaPrint.ps1;Fetch-Jobs -checkInterval 15 -printerId '<printerId>' -localPrinterName '<printerName>' }"
Start in: C:\fmsys-alma-printing-api
Run: Minimized
- Click the
OK
button to save these changes. - In the Windows
Run
app, entershell:common startup
. Explorer will open the startup directory. - Move your shortcut from
C:\fmsys-alma-printing-api
to this directory (admin rights required) - In the Windows
Run
app, entershell:common desktop
. Explorer will open the desktop directory - From
shell:common startup
, copy your shortcut to theshell:common desktop
directory (admin rights required) - Lastly, log off the PC and on again to check that the script is starting normally.
- Note the
Start-Sleep 30;
at the start of the-Command
parameter. It was noted that if the script starts too quickly after logging in, then you might see errors. This ensures a delay before starting the queue checking. - If you need to reduce the
font-size
for a particular printer, you can do this by adjusting the relevant XSL template in Alma. For example, we use the following to reduce thefont-size
when printed to our Epson POS receipt printers:
<xsl:if test="notification_data/receivers/receiver/printer/code = 'sorter' or notification_data/receivers/receiver/printer/code = 'kings'">
@media print {
tr {font-size: 70%;}
}
</xsl:if>
The printer code
s, referenced in the conditional statement, are returned when invoking the Fetch-Printers
function.
A problem was identified with the readability of the barcodes when printed using the script. The key points about this problem are:
- the printed barcodes appeared to be missing their right guard bar, which was causing the barcodes to be unreadable when scanned
- when the HTML content is rendered on screen in Internet Explorer, from the files stored in
tmp_printouts
, the guard bar is not missing - the Opticon branded scanners in use are incapable of reading the "faulty" printed barcodes, however, they can be read using ones' smartphone
- the problem is not present when the HTML content is printed from an alternative web browser such as Google Chrome
- the problem was narrowed down to it being Internet Explorer related, and there was a very similar sounding problem described at this now-defunct link, which pinpointed the XPS subsystem (that IE uses for printing) as the root cause
- after experimentation, it was found that the barcodes could be converted from
PNG
toJPG
format in order to resolve the readability problem
To provide a solution for this problem, a new base64Png2Jpg
function was added which converts the base64 PNG data to base64 JPG. This can be used by adding the Fetch-Jobs
function switch parameter -jpgBarcode
.
Alternatively, another solution is to modify the XSL template associated with the printout so that — instead of including the barcode as an image — the barcode is included as text formatted with an installed barcode font such as this one. Example XSL snippet:
<p style="font-family: IDAutomationHC39M; font-size: medium;">(<xsl:value-of select="notification_data/phys_item_display/barcode"/>)</p>
Because the script relies upon Internet Explorer for rendering & printing the HTML, it is likely you'll see the following first-run box:
To prevent this box from reappearing, a setup script is provided. Perform the following one-time step in an elevated Powershell window:
Set-Location 'C:\fmsys-alma-printing-api\setup-scripts'
.\DisableFirstRunIE.ps1
The tmp_printouts
directory will accumulate many HTML files over time. It's probably a useful contingency having the HTML files persisted to disk in case re-prints are required, or if there is a problem printing the document. However, over time these files may consume a significant amount of disk space, so it's recommended to delete or recycle the older ones while keeping the more recent ones. To help automate this housekeeping process, a Task Scheduler XML template and a Powershell script is provided to recycle files older than 30 days. This can be adjusted according to local needs; just edit the XML before importing, or modify the task once imported. See maintenance-scripts\fmsys-alma-printing-api - clean tmp_printouts directory.xml
and maintenance-scripts\recycleFiles.ps1
. Note that maintenance-scripts\silent.vbs
exists to run the Powershell script completely invisibly; powershell.exe
has -WindowStyle 'Hidden'
but this is only processed after the Powershell window has appeared.
In relation to creating shortcuts using .\setup-scripts\DeployShortcuts.ps1
, here is a list of UoY -ShortcutArguments
parameter values to conveniently copy/paste:
Interlending receiving
"-NoLogo -NoProfile -NoExit -Command `"& { Start-Sleep 30;. .\FetchAlmaPrint.ps1;Fetch-Jobs -checkInterval 15 -printerId '19195349880001381' -localPrinterName 'EPSON TM-T88III Receipt' }`""
JBM Holds processing
"-NoLogo -NoProfile -NoExit -Command `"& { Start-Sleep 30;. .\FetchAlmaPrint.ps1;Fetch-Jobs -checkInterval 15 -printerId '848838010001381' -localPrinterName 'EPSON TM-T88III Receipt' }`""
JBM Customer Services & Off-site Store Slips
"-NoLogo -NoProfile -NoExit -Command `"& { Start-Sleep 30;. .\FetchAlmaPrint.ps1;Fetch-Jobs -checkInterval 15 -printerId '18009202260001381','19594501280001381','31949435460001381' -localPrinterName 'EPSON TM-T88III Receipt' }`""
KML Holds processing
"-NoLogo -NoProfile -NoExit -Command `"& { Start-Sleep 30;. .\FetchAlmaPrint.ps1;Fetch-Jobs -checkInterval 15 -printerId '993537480001381' -localPrinterName 'EPSON TM-T88III Receipt' }`""
- Currently, if the script is interrupted while it is
Working..
, say by pressingCTRL+C
, there's a chance that the original default printer andPage Setup
settings as mentioned previously won't be restored. It might be possible to improve this by usingTry
,Catch
,Finally
as indicated here. - The limitations of using Internet Explorer for printing could be overcome by using a third-party HTML rendering/printing tool like this one. But sadly, having tested it, it doesn't cope with
Roll Paper 80 x 297 mm
paper size. Another option is the Print HTML command line tool but it's old (~2009) and untested. - It would also be good to see if this script could be made into a Windows service, perhaps using NSSM, instead of invoking the script via shortcuts.
- To protect against a possible API endpoint security compromise, it would be a good idea to sanitise the HTML letter content before "opening" it, as is effectively done with
$ie.Navigate($printOut)
. The idea would be that this would protect against e.g. malicious<script></script>
code from running, if the perpetrator managed to inject this into the HTML. Thought needs to be given to the most suitable & effective way to do this, be it via the Internet Explorer zone-based security controls inInternet options
, using an allow-list of HTML tags akin to htmlpurifier, or some other method. - Currently named parameters are specified on the command line. A nice improvement might be to store these parameters in a settings XML file so that they do not need to be passed on every invocation of the script.
- Corrections should ideally be made so that the script falls into line with the Powershell Style Guide.