Skip to content

Instantly share code, notes, and snippets.

@apizz
Last active May 1, 2024 14:34
Show Gist options
  • Star 20 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save apizz/5ed7a944d8b17f28ddc53a017e99cd35 to your computer and use it in GitHub Desktop.
Save apizz/5ed7a944d8b17f28ddc53a017e99cd35 to your computer and use it in GitHub Desktop.
Programmatically adds an AirPrint printer, ideally with an icon
#!/bin/bash
# Use the built-in ipp2ppd tool to create a PPD file for AirPrint
# Add icon to PPD (if we can get one) and install the printer
# Required printer info
readonly PRINTER_IP='XXX.XXX.XXX.XXX'
readonly PRINTER_NAME='PRINTER_NAME'
readonly PRINTER_DISPLAY_NAME='PRINTER NAME'
readonly PRINTER_LOCATION='PRINTER LOCATION'
# Requiring icon will prevent install if we can't get it
readonly REQUIRE_ICON=true
#readonly REQUIRE_ICON=false
# Number of seconds to wait for TCP verification before exiting
readonly CHECK_TIMEOUT=2
# Custom PPD info
readonly PPD_PATH='/tmp'
readonly PPD="${PPD_PATH}/${PRINTER_NAME}.ppd"
# Base info
readonly AIR_PPD='/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/PrintCore.framework/Versions/A/Resources/AirPrint.ppd'
readonly EXE='/System/Library/Printers/Libraries/ipp2ppd'
readonly ICON_PATH='/Library/Printers/Icons'
readonly ICON="${ICON_PATH}/${PRINTER_NAME}.icns"
AppendPPDIcon() {
# Verify we have a file
if [ ! -f "$ICON" ] && [ "$ICON_AVAILABLE" != "false" ]; then
/bin/echo "Don't have an icon. Exiting..."
exit 1
fi
/bin/echo "Appending ${ICON} to ${PPD}..."
# Append the icon to the PPD
/bin/echo "*APPrinterIconPath: \"${ICON}\"" >> "${PPD}"
}
CheckPrinter() {
# Verify we can communicate with the printer via the AirPrint port via TCP
local CHECK=$(/usr/bin/nc -G ${CHECK_TIMEOUT} -z ${PRINTER_IP} 631 2&> /dev/null; /bin/echo $?)
if [ "$CHECK" != 0 ]; then
/bin/echo "Cannot communicate with ${PRINTER_IP} on port 631/tcp. Exiting..."
exit 1
fi
}
CheckIcon() {
# Query & parse printer icon, largest usually last?
readonly PRINTER_ICON=$(/usr/bin/ipptool -tv ipp://${PRINTER_IP}/ipp/print get-printer-attributes.test \
| /usr/bin/awk -F, '/printer-icons/ {print $NF}')
# Verify we have an icon to download
if [ -z "$PRINTER_ICON" ] && [ "$REQUIRE_ICON" = "true" ]; then
/bin/echo "Did not successfully query a printer icon. Will not install printer. Exiting..."
exit 1
elif [ -z "$PRINTER_ICON" ]; then
/bin/echo "Did not successfully query printer icon. Will continue with printer install..."
readonly ICON_AVAILABLE=false
fi
/bin/echo "Downloading printer icon from ${PRINTER_ICON} to ${ICON}..."
# Download the PNG icon and make it an .icns file
/usr/bin/curl -skL "$PRINTER_ICON" -o "$ICON"
# Did we actually write an icon successfully?
STATUS=$(echo $?)
if [[ "${STATUS}" != 0 ]]; then
/bin/echo ""
/bin/echo "Was not able to write the file ${ICON}..."
/bin/echo "Does the user running this script have the ability to write to ${ICON_PATH}?"
/bin/echo "If not, either run with 'sudo' or choose a different location to write the icon file."
exit 1
fi
}
CreatePPD() {
# Create the PPD file
/bin/echo "Creating the .ppd file at ${PPD}..."
$EXE ipp://${PRINTER_IP} "$AIR_PPD" > "$PPD"
}
InstallPrinter() {
/bin/echo "Installing printer..."
/usr/sbin/lpadmin -p ${PRINTER_NAME} -D "${PRINTER_DISPLAY_NAME}" -L "${PRINTER_LOCATION}" -E -v ipp://${PRINTER_IP} -P ${PPD} -o printer-is-shared=false
}
main() {
CheckPrinter
CreatePPD
CheckIcon
AppendPPDIcon
InstallPrinter
}
main "@"
@neilmartin83
Copy link

Great addition for the icons!

I've got one suggestion - adding -k to the curl command, as some printers will transfer the icon over HTTPS and have self-signed certs which cause curl to otherwise bail.

Thank you!

@apizz
Copy link
Author

apizz commented May 26, 2021

Done!

@saber-oz
Copy link

saber-oz commented Sep 27, 2021

Hey I am getting an error on this running on Big Sur
ipp2ppd: No strings file for "en".
Any thoughts as to what would cause that.

It also says
Downloading printer icon from https://192.168.0.152/printer-icon/machine_512.png
sides-supported to /Library/Printers/Icons/Kyocera_Ecom_M3540idn.icns...
Don't have an icon. Exiting...

However I can confirm that the png file does exist.

@hildahlp
Copy link

I received the same error when not running the script as root (via sudo).

@apizz
Copy link
Author

apizz commented Feb 12, 2022

@saber-oz & @hildahlp

Are you running this script manually or with sudo? Reason being without running the script with admin privileges attempting to write to /Library/Printers/Icons will produce an error:

> touch /Library/Printers/Icons/testicon.icns                                                                                                                      
touch: /Library/Printers/Icons/testicon.icns: Permission denied
> ls -lah /Library/Printers/Icons                                                                                                                                 
total 248
dr-xr-xr-x  3 root  wheel    96B Feb 12 03:48 .
dr-xr-xr-x  7 root  wheel   224B Feb 11 12:12 ..
/Users/Shared > sudo touch /Library/Printers/Icons/testicon.icns                                                                                                                 03:48:31
> ls -lah /Library/Printers/Icons                                                                                                                                 
total 248
dr-xr-xr-x  4 root  wheel   128B Feb 12 03:48 .
dr-xr-xr-x  7 root  wheel   224B Feb 11 12:12 ..
-rw-r--r--  1 root  wheel     0B Feb 12 03:48 testicon.icns

Given the output you've shared, I would guess there's either a problem with the PRINTER_ICON variable or the ICON variable, which in my mind would be most easily be explained by not running this with elevated privileges.

@bfaulkner-scc
Copy link

So... I've been using this shell script recently without issue. It worked as described and without issue. Here comes the but....

Within the last couple of weeks we started having issues. When using the script the printer will install on a machine just like before. However when we attempt to use the printer I get a strange issue where the system continually tries to connect to the printer, then says printing, then back to connecting to printer.

When we install a printer the good ol' fashioned way it works great. It has to be something with how the install is done via the script.

Any Thoughts???

@porteusconf
Copy link

bfaulkner-scc ... perhaps an expired certificate? A bad cert might cause printing failure if trying to talk to printer with ipps (instead of un-encrypted ipp). I noticed when I share a printer in macos, my chromebook will let me add and save the printer, and the chromebook is using ipps to talk to the macos cups server. In an ideal world all traffic to the printer would be encrypted (ipps or https) rather than un-encrypted methods like ipp, http, lpd, port 9100, etc. I'm looking for advice on getting all this working with ipps or https if anyone has done so.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment