jazzace.ca

Anthony’s Mac Labs Blog

The Joys of Colour Calibration

Posted 2020 February 01

Recently, our Labs moved from using a shared user account to binding our Macs to Active Directory and having the users receive their own account when they login. (Adobe forced our hand on this one.) I understand this is old news to most of you who run Labs, but one thing that we have to do that you may not is colour calibrate displays. Applying that profile to every new or existing account is non-trivial in our new environment. The solution we ended up implementing leveraged two open source utilities and a shell script wrapped in an AppleScript droplet. Because there is very little posted publicly on this kind of task, I thought it was time to document at least one solution.

ColorSync and Calibration Primer

Similar to the Preferences for your favourite apps, ColorSync profiles can live in a few places in the file structure: /System/Library/ColorSync/Profiles, /Library/ColorSync/Profiles, and/or ~/Library/ColorSync/Profiles. The last two locations are where you can install your own profiles. They share the file extension .icc, whether they are profiling input or output devices such as scanners, cameras, displays, projectors, and printers.[1]

You can make a rough calibration of your display(s) using the Display Calibrator Assistant (generally launched from System Preferences > Displays > Color tab > Calibrate… button) or you can purchase an external calibration device that will come bundled with custom software starting at about $200.[2] Either way, you create a new ColorSync profile that applies to just that display. Colours will drift over time, so you will need to recalibrate periodically.

As you would expect, locating the profile in /Library/ColorSync/Profiles makes it available to all users. The issue in our case is not availability but setting the profile for each user (new or existing), so I went searching for a tool that could do that.

customdisplayprofiles

I miss having Tim Sutton as an active member of the Mac Admins community. In his previous job, he and I supported a lot of the same stuff because we were both working in the Arts at a University. So it was no surprise that my search turned up a tool that Tim had written: customdisplayprofiles. It was recommended by a veritable Who’s Who of the Mac Admin community when I searched in Slack and yet I had never heard of it. I found that it still worked in Mojave, even though it had not seen an update in 6 years.

As the Read Me suggests, customdisplayprofiles “can check, set or unset a custom ColorSync ICC profile for a given display.” If the Mac has more than one display, you simply specify the display by number (1, 2, etc.). You can run it at the command line or in a shell script. Tim even included a helpful sample script to set the desired profile that also documents the file structure needed for the profiles. It was not going to take much work to deploy this tool, so that gave me the first piece of the puzzle.

login-every

While Tim’s solution had the option to try to apply the profile at the system level, the sample script showed applying it at the user level. The latter method made sense to me and my situation, which made me think of another open source tool that could make that task easier: Joseph Chilcote’s Outset. I have this tool deployed to all my Macs as an alternative to having to write LaunchAgents and LaunchDaemons, so I could just deploy the script to Outset’s login-every folder and the current profile would be set for the user. Even if I had to recalibrate the display(s), the user would always get the latest profile(s). That was the second piece of the puzzle.

Drop and Go

Finally, we needed to create the folder structure that customdisplayprofiles requires. It can be located anywhere on the system that all users can reach, but you need to put the profile for your first or only display in a folder named “1”, for your second display in a folder at the same level named “2”, and so on. Each folder should only have one profile in it to avoid mis-calibration, as customdisplayprofiles will only see the first file in the folder. Tim’s documented example creates a special folder in /Library associated with the organization for these profiles, but there is no reason you can’t put them with the rest of the profiles, so I chose /Library/ColorSync/Profiles/Custom/. That way, if something ever went wrong with my implementation or customdisplayprofiles stopped working, you could still manually select the profiles, since the System would be able to find them there.

Gratefully, I have student assistants that can help with this work, so I needed to make this process as straightforward as possible for them. An AppleScript-based droplet app seemed to fit the bill here. The only technical knowledge required by them would be finding the location of the calibration profile they would create (usually in the user Library folder). Once they located it, they would just drag the profile onto the droplet, which would then generate a dialogue box asking them if this was a profile for the primary or secondary display, and it would then copy the profile to the location customdisplayprofiles required, creating the necessary folder structure as needed. I also realized that when we started re-calibrating these displays, we would no longer want or need the old calibration (especially since it was likely to come first alphabetically because we include the calibration date in the file name). So the script deletes the old profile and copies the new one.[3]

I have a little bit of experience with AppleScript, so I fired up Script Editor. It just so happens that Apple provides useful templates in this regard, so I opened the Droplet with Settable Properties template from File > New from Template > Droplets. It already had almost everything I needed, including file type verification and handling of the case where the user double-clicked the droplet instead of dropping a file on it. All I had to create was the dialogue box to let the user assign the profile to the correct display and the code to copy the profile to the desired location. This is what I ended up with:

a screenshot of the dialogue box from the completed Applescript droplet

One fun thing about AppleScript is that it can run shell commands as well. It ended up that it was much easier to write the file copy commands as shell commands, particularly because I could more easily handle creation of the numbered folder(s) for the profile(s). After some trips to the AppleScript reference books I keep on hand for such an occasion, I was able to create the code I needed. I saved my script and then Exported it as a Run-only Application.[4] I gave it the name “Drop Calibration Profiles Here” and placed it in Utilities.

The only qualification I should mention is that I needed admin access to run the script successfully, since we were putting files in /Library. I opted to include the local admin username and password in the script, since I was distributing a Read-Only (binary) version of the droplet. The password is not readable in that format but that still may not be acceptable for your environment.

The Full Package

I now had a full solution. I tested it by deploying the tools manually. When that was working, I built a package installer that placed all the pieces of the solution in the required places: customdisplayprofiles in /usr/local/bin, the script that assigns the profile for the current user in /usr/local/outset/login-every, and the droplet in /Applications/Utilities. For your reference (and my documentation), I’ve created a GitHub repo called “colour-calibration” that has my fractionally-modified version of Tim’s sample script for assigning profiles and the (sanitized) code for my AppleScript droplet, in case you want to try this yourself.

What’s Next

I should be able to use this method for the next year and a bit, since we generally don’t deploy new major versions of macOS from September to April each academic year. But Tim’s code is written in Python 2, so it will eventually require an update for Python 3 (just like the update Outset got shortly after I posted this article). Here’s hoping that the community can come through and update this useful tool. Or maybe I’ll just pay Tim to do it. :-)

Updates: The community came through on the Python 3 update for customdisplayprofiles. Jonathon Irons contributed a Pull Request for this in March 2022 and Tim added a self-contained executable (with Python 3 included) for either Intel or Apple Silicon that people can download. The Python version of Outset has been “decommissioned,” but everything here still works with the new Swift version of Outset written by Bart Reardon. See also my 2024 post on Packaging Choices, which describes possible methods for packaging all these things (and more) in a single package for deployment.

Article updated 2020-02-08 to reflect the Python3-friendly version of Outset and again on 2024-04-01 to provide further updates on customdisplayprofiles, the Swift version of Outset, and a reference to a related blog post.


[1] With printers, you calibrate a printer + output media combination. This is why you may see dozens of profiles in your system-level ColorSync profiles folder if you have a printer designed for photographic output. It’s also why specialty paper makers provide profiles for their media for a wide variety of printers. [Return to main text]

[2] If you want to be able to calibrate all your input and output, you could spend a few thousand dollars on a kit. We’ve made that investment for my Labs. [Return to main text]

[3] That’s an oversimplification. I tried to delete the file(s) inside the folder directly but I couldn’t get the correct shell incantation or globbing pattern to make that happen. It was easier and more reliable to delete the numbered folder and all its contents, and then re-create the folder while copying the new profile. [Return to main text]

[4] Yes, you can Code Sign your droplet app. The option is available in the Export dialogue box. [Return to main text]