And I was completely wrong. The problem is (sort of) with the tonr2 configuration. Or more correctly the problem is with one of the classes referenced by the tonr2 configuration. This is a known issue in the Spring Framework when the accepted media types are set to all, i.e. */*. When this happens, the configured BufferedImageHttpMessageConverter has a method called isWritable() that returns false, apparently since it doesn't recognize that particular type (really the fault comes at the level of the ImageIO.getImageWritersByMIMEType() call, which returns an empty list of image writers for that media type, but that makes sense because it can't return a specific writer for that MIME type). This problem is described in a fairly detailed and now fairly old bug report on the Spring Framework JIRA.
Now this gets to the root of the problem, which is the HTTP Accept header. This hadn't even occurred to me, but the REAL issue here is the interaction of this relatively minor bug and the Chrome browser. Here's the Accept header for Chrome:
Here's the Accept header for Opera, which works just fine:
For a workable fix, see the EnhancedBufferedImageHttpMessageConverter implementation suggested by Shawn Clark. That's working for me right now. It'd be nice to see this addressed in the core framework, however.
Accept: text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/webp, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1