Forcing Single-Sided Printing with AppKit

I've been hand-writing GOTV postcards for two organizations1, and I decided to write a quick app to print address labels for one of them. (The other requires hand-written addresses.) It's been a long time since I did any coding, and this seemed like a nice low-stakes project to start the Cocoa wheels turning in my head again2. Worst case, I could always throw the code away and use mail-merge in Word or Pages like a normal person.

I soon had a fully functional app — not in a state to share publicly, but good enough for personal use.

There was one thing that bugged me though.

If your printer supports double-sided printing, the macOS print panel always defaults to double-sided, no matter what option you chose the last time you printed. This is apparently on purpose and has been the case for a while now.

I found that as a user you can change this with a few simple steps:

  1. Using sudo privileges, enable the web interface to CUPS.
  2. And I stopped reading there, because WTF.

Okay, well, I figured I'm writing the printing code in my app, so as the All-Powerful Programmer all I have to do is set the NSPrintInfo's duplex mode to kPMDuplexNone, which indicates single-sided printing. But no, AppKit blows away the value I set and forces the duplex mode to kPMDuplexNoTumble, which indicates double-sided printing.

The only workaround I could figure out was to subclass NSPrintPanel and override the method that opens the print panel in a sheet. My override sets the duplex mode immediately after the sheet is presented, replacing the double-sided setting AppKit stuck in, and doing so before the user interacts with the print panel. You can see my code on GitHub.

I haven't looked at how (or whether) commercial label-printing programs deal with this issue. All I know is how I dealt with it. [EDIT: It occurred to me that one way would be to not use the system print panel at all, but rather to implement one's own print panel, like Google Chrome does. Doable, but not worth it for me, and IMO it's not great to have to resort to re-implementing standard system UI just to work around a quirk.]

Unfortunately this is not a generally useful workaround, because the "Two-Sided" checkbox in the print panel is still checked, which means it does not accurately reflect the state of the NSPrintInfo. My app hides the checkbox, but this means it also hides the number-of-copies text field. This is okay for my purposes. I never want multiple copies, and because I'm printing labels I never want double-sided.

Right now I'm too lazy, but I should probably file a Feedback ticket:

  • I want the ability for my app to force single-sided every time instead of AppKit insisting on double-sided3. Another way of putting it is that I'd like the controls in the print panel to accurately reflect the values they correspond to in the NSPrintInfo instance I provide.
  • I want to be able to hide just the "Two-Sided" checkbox without having to hide the number-of-copies text field.
  • It might make sense for AppKit to default to single-sided printing, if not outright force it, when "Labels" is chosen as the media type.

Printer Brother HL L2395DW series


  1. Postcards to Voters and the Georgia Postcard Project. There's a third GOTV group I've joined called Vote Forward that writes letters rather than postcards. I haven't written for them yet. 

  2. This is the "quick Mac app" I referred to in my previous post

  3. In the latest Big Sur beta (20A5374i as of 2020-09-30) I think there is a change I did not see in the release notes. If I'm not mistaken, the "Two-Sided" checkbox remembers its setting between invocations of the print panel. But this doesn't help me. I need to always force single-sided. 

Quick Notes on Printing from AppKit

Recently, while writing a quick Mac app for personal use, I had to refresh my memory on the basics of printing from AppKit. Here, for my future self who will surely have forgotten again, are the basic ideas I used.

First, here are the three main printing classes to be aware of:

  • NSPrintOperation. A print operation performs the overall sequence of events that is triggered when the user asks to print. This includes the presentation of the print panel, sending the print job to the printer, and displaying a progress bar during printing. NSPrintOperation is not a subclass of NSOperation, but there are conceptual similarities. A print operation is something that runs, has a context (the print info), and can optionally spawn a background thread (for the actual printing).
  • NSPrintInfo. A print info contains settings that affect how the printing is done. Superficially, it maps to the settings presented in a standard print panel.
  • NSPrintPanel. This class represents the standard macOS print panel. Superficially, it is a UI for editing the values in a print info. The main reason you might deal directly with an NSPrintPanel instance is to set its options.

Now, here's a typical sequence of events:

  • The user asks to print, usually by selecting "Print" from the menu or hitting ⌘P. This triggers an action method somewhere in your code.
  • Your action method tells AppKit to print a view. That is what printing normally means at the application level — printing a specific view. You print a view by passing it to an instance of NSPrintOperation and telling the print operation to run.
  • The print operation, which is now running, displays a print panel to the user, either as a modal dialog or as a sheet, depending on which run method you called. The user selects a printer, changes other settings as they wish, and hits the Print button, which means it's time to start outputting to the printer.
  • To generate the printer output, the view's drawRect: method is invoked. But instead of drawing on the screen, it now draws in a special graphics context, so that whatever drawRect: does is sent to the printer.
  • Paper comes out of the printer with ink on it, and now the print operation's work is done.
  • In all the above, the print info used by default is [NSPrintInfo sharedPrintInfo]. You can just let AppKit do its default thing, or you can fiddle with the print info if you need to.

That's the general flow of things.

There are two more topics to know about when implementing your application's print function:

  • First, you'll likely need to deal with pagination. Depending on the size of the view, the amount of data it contains (e.g. a table with hundreds of rows), and the requirements of your application, the printing of the view may have to span multiple pages of paper. To customize pagination behavior, override the NSView methods knowsPageRange: and rectForPage:. For more info on pagination, see "Laying Out Page Content".
  • Second, you'll likely want to draw some things differently when printing than when displaying the view on the screen.
    • One way to do this is to have different code branches in drawRect: based on the return value of [NSGraphicsContext currentContextDrawingToScreen].
    • Another way is to use a completely different view object for printing than for displaying. Printing means printing a view, but it can be any view you decide to give to the NSPrintOperation. The printing view can do all the printing-specific drawing that you don't want to clutter the display view's code with.

There is lots more to know about printing, like how accessory views work, how NSDocument support works, and other stuff. The stuff above is the foundation for all of that. It's what was relevant to my app, and probably to many other basic non-document-based Mac apps.

Xcode Docs Navigator

[The following is basically a transcription of a series of tweets.]

I have my wishes and druthers regarding the Xcode docs, but the Navigator pane in the docs window is pretty nice. Sometimes I want to learn the lay of the land of an unfamiliar framework. The docs Navigator is organized well for that use case.

Screen Shot 2018 07 26 at 1 47 40 PM

I like this list of Sequence and Collection protocols. For me it helps to have things like this summarized in one place. When I dive into a new project with an existing code base, I sometimes make a similar list by hand — for example, an outline of a particular class hierarchy.

Sequence and Collection Protocols

I like that there are "First Steps" sections for many of the frameworks. It's good to present them in this structured, discoverable way. They remind me of the old "concepts manuals" in NeXTSTEP.

Core Location First Steps

UIKit First Steps

One suggestion for improvement: IMO it would help to add an "Xcode" tab here:

Xcode Docs Navigator Tabs

The stuff that's in Help > Xcode Help > Show topics could be moved there. It would be more discoverable, it would provide a unified UI for all of Xcode's documentation, and we'd be able to open help topics in new tabs.

Whom

I've known for a long time that who am i prints info about the current user. Today I learned it's synonymous with who -m, though not synonymous with whoami.

I also learned that the two words after the who can be any non-option arguments. The Linux man page says:

If ARG1 ARG2 given, -m presumed: 'am i' or 'mom likes' are usual.

For example:

$ who -m
alee     ttys019  Jan 28 21:58 

$ who am i
alee     ttys019  Jan 28 21:58 

$ who mom likes
alee     ttys019  Jan 28 21:58 

$ who fred wilma
alee     ttys019  Jan 28 21:58 

This works on the Mac too, even though the Mac's man page only mentions am i and not that you can use any two words.

So of course I've now added an alias whom so that I can type whom Mom likes. I actually pride myself on not being a grammar pedant. I make an exception in this case because it amuses me.

Using Xcode for Advent of Code 2017

This year I'm using Swift as much as I can for Advent of Code. After fiddling with a few different coding environments, I settled on Xcode. I have a "Command Line Tool" project that I reset everyday to contain some minimal boilerplate code. Starting with that, I code my solution for the day's AoC puzzle.

This might seem like the obvious choice — what else would one use for writing Swift code? — but I actually started out with CodeRunner, a great tool that I used last year as well. I switched to Xcode after a few days because I was having trouble getting Swift breakpoints to work in CodeRunner.

On top of that I realized how helpful the other goodies in Xcode are, even for one-off exercises like AoC. By using Xcode I get integration with the documentation, automatic "fix-its" for common syntax errors, and "Edit All in Scope". That last feature is handy because when I catch myself agonizing over a name I can stop, use anything quick that comes to mind, and move on. It'll be trivial to change the name when a better one comes to me, usually while I'm on some other part of the code. I don't need the full "Refactor" feature, which is a bit slower to use, because all my code is almost always in one file.

I do keep CodeRunner on my Dock and use it all the time to try out bits of syntax in various languages. It's much easier to open a new file and start coding, and possibly never even save the file, than to set up a full-blown project directory. I have used Swift, Python, Java, and Objective-C to code AoC solutions, all in CodeRunner, and usually the integration with the respective language's symbolic debugger worked fine.

You might think a Swift playground would be just the thing for AoC, but I have not found that to be the case. For one thing, as far as I can tell there is no support for symbolic debugging. For another thing, AoC puzzles sometimes involve loops with millions of iterations, and it is prohibitively slow to have the results pane updated on every iteration. As far as I know there is no way to turn that off.

Out of curiosity I took a quick look at AppCode and VSCode. Both are highly un-Mac-like and required more of an investment than I was willing to make at this time.

An approach I might try when I'm feeling my hacker oats is to do my Swift coding in Terminal, with vim in one window and lldb in another. The oats aren't quite there yet, though. Maybe next year.