Configuring Printers Programmatically for AirPrint

For the past several weeks, I’ve been troubleshooting an issue one of our users reported when printing large MB files to an HP Color LaserJet Pro M452.

Historically, we’d had trouble getting these M452s to print jobs sent from Macs routed via our print server and temporarily configured direct IP printing to work around it. However, after upgrading to macOS Mojave (10.14.6) this year and temporarily implementing a solution proposed on Jamfnation to get things working with our print server, we ran into a different set of issues entirely.

While test pages and files ~5MB in size would print just fine, files closer to ~10MB would take an exceptionally long time to process – getting stuck spooling at several points – and when ultimately finished after several minutes only printed out the following error:

ERROR: limit check
OFFENDING COMMAND: showpage

Interestingly, the print experience was the same regardless if we were going through our print server, via direct IP, and even via a physical USB connection.

Given our previous issues with print server printing, I tried the following for direct IP printing (lpd://), all with no success:

  • Using both the available driver from HP’s website and Generic Postscript Printer driver
  • Testing different file types totaling ~10MB, including saving several large test images as PDFs before printing
  • Manually adding the printer via the ‘Add Printer’ menu as well as programmatically
  • Booting the test Macs to Safe Mode
  • Testing with different Macs running 10.14.6
  • Attempting to upgrade the firmware of the printer itself using the HP firmware update utility failed to transfer
  • Upgrading the Macs running 10.14.6 to the latest available supplemental update

The one thing that did successfully print ~10MB files without issue were Macs running 10.13.6. So, something is definitely not happy in Mojave …

We went so far as to purchase a new HP M454, but unfortunately ran into the same issues.

Desperately hoping for a long-term solution, I reached out to Apple Enterprise support, and after being escalated was able to confirm with the advisor that the printer error we were seeing is a known issue. Besides confirming that most issues reported were with HP printers, it has also been identified both on 10.14 and 10.15 …

The only good news here was that there is a known workaround available: configure printing via AirPrint. Having handled all our printing configurations with the help of PrinterGenerator, I needed to figure out how we could programmatically setup AirPrint our few M452s and new M454s.

With some help with members of the #printers-n-cups MacAdmin Slack channel, I learned that AirPrint utilizes the ipp protocol. When manually configuring a printer for AirPrint, a built-in macOS tool is used called ipp2ppd (/System/Library/Printers/Libraries/ipp2ppd) and which creates a ppd file in /etc/cups/ppd.

Running ipp2ppd with no arguments reveals it’s usage:

Usage: ipp2ppd <printer_uri> <input_ppd_path>

With the printer installed for AirPrint manually, you can get the settings with lpoptions, critically the device-uri, which we need for <printer_uri>:

# To get printer names
lpstat -e
# Get printer settings
lpoptions -p <printer_name>

In our case, we’re working with ipp:// rather than dnssd://. With the printer uri, the last piece is supplying a ppd path. A little searching on Jamfnation revealed the buried built-in AirPrint.ppd file (although you can just supply everywhere and get the same result):

/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/PrintCore.framework/Versions/A/Resources/AirPrint.ppd

Now, running the ipp2ppd tool with the required arguments produced the necessary ppd information! Albeit, to stdout. Redirecting to a file produces the needed ppd file.

/System/Library/Printers/Libraries/ipp2ppd ipp://<printer_ip> /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/PrintCore.framework/Versions/A/Resources/AirPrint.ppd > /path/to/<printer_name>.ppd

Because this file normally gets created in /etc/cups/ppd, you can simply redirect the output to a file there. In my tests, this must be done as root.

With the AirPrint ppd file created, you’ll want to test using it to install the printer:

lpadmin -p <printer_name> -D "<printer_display_name" -L "<printer_location>" -E -v ipp://<printer_ip> -P /path/to/<printer_name>.ppd

With the printer installed programmatically, we can test printing our large image file and verify that a ~10MB file now successfully prints!

If you happen to use PrinterGenerator or a similar munki nopkg item to install your printers via postinstall_script, I created the preinstall_script below to create the necessary AirPrint ppd file via ipp2ppd:

A Small Caveat …

The one caveat I found is that running this script as a local user produced a slightly different ppd file than when run as root.

Mysteriously, the ppd has a single line that is different when run as root: it did not have a printer icon listed. Run as a local user, the printer’s icon file is copied to /Library/Printers/Icons and is then referenced in the ppd file.

This missing icon behavior occurred both when adding sudo as well as when attempting to run by root (running sudo -s) as a local user (ex. as root: sudo -u <username> ipp2ppd ...). With the help of a fellow Mac Admin in the MacAdmins #printers-n-cups channel, we found that a local user could successfully get the icon when run as root by adding the -E flag:

sudo -E -u <user> ipp2ppd ipp://<printer_ip> /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/PrintCore.framework/Versions/A/Resources/AirPrint.ppd > /path/to/<printer_name>.ppd

Unfortunately, this doesn’t really help us because when run via munki, Jamf, or some other method it will still run as root. So as of yet, I’ve been unable to programmatically configure AirPrint printers which also include the printer icon.

With a little help from Wireshark, I was able to determine that as part of its printer attributes query (you can run this manually by running ipptool -tv ipp://<printer_ip>/ipp/print get-printer-attributes.test) ipp2ppd uses the printer-icons information collected to download 3 files (in the case of the M452):

'http://<printer_ip>/images/printer-small.png'
'http://<printer_ip>/images/printer.png'
'http://<printer_ip>/images/printer-large.png'

Once downloaded, these are converted & merged into a single .icns file which is populated in /Library/Printers/Icons. The traffic shows that these .png files are also collected when ipp2ppd is run as root, but does not produce this .icns file and likely why it does not then subsequently include this in the final ppd file.

Despite not being terribly important on the whole, this does itch my curiosity and my hope for an eventual fix.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s