docs: def generate_file(self, r, undistorted=False):
          """Generate file for IIIFRequest object r from this image.
  
          FIXME - Would be nicer to have the test for an undistorted image request
          based on the IIIFRequest object, and then know whether to apply canonicalization
          or not.
  
          Logically we might use `w,h` instead of the Image API v2.0 canonical
          form `w,` if the api_version is 1.x. However, OSD 1.2.1 and 2.x assume
          the new canonical form even in the case where the API version is declared
          earlier. Thus, determine whether to use the canonical or `w,h` form based
          solely on the setting of osd_version.
          """
          use_canonical = self.get_osd_config(self.osd_version)['use_canonical']
          height = None
          if (undistorted and use_canonical):
              height = r.size_wh[1]
              r.size_wh = [r.size_wh[0], None]  # [sw,sh] -> [sw,]
          path = r.url()
          # Generate...
          if (self.dryrun):
              self.logger.info("%s / %s" % (self.dst, path))
          else:
              m = self.manipulator_klass(api_version=self.api_version)
              try:
                  m.derive(srcfile=self.src, request=r,
                           outfile=os.path.join(self.dst, path))
                  self.logger.info("%s / %s" % (self.dst, path))
              except IIIFZeroSizeError:
                  self.logger.info("%s / %s - zero size, skipped" %
                                   (self.dst, path))
                  return  # done if zero size
          if (r.region_full and use_canonical and height is not None):
              # In v2.0 of the spec, the canonical URI form `w,` for scaled
              # images of the full region was introduced. This is somewhat at
              # odds with the requirement for `w,h` specified in `sizes` to
              # be available, and has problems of precision with tall narrow
              # images. Hopefully will be fixed in 3.0 but for now symlink
              # the `w,h` form to the `w,` dirs so that might use the specified
              # `w,h` also work. See
              # <https://github.com/IIIF/iiif.io/issues/544>
              #
              # FIXME - This is ugly because we duplicate code in
              # iiif.request.url to construct the partial URL
              region_dir = os.path.join(r.quote(r.identifier), "full")
              wh_dir = "%d,%d" % (r.size_wh[0], height)
              wh_path = os.path.join(region_dir, wh_dir)
              wc_dir = "%d," % (r.size_wh[0])
              wc_path = os.path.join(region_dir, wc_dir)
              if (not self.dryrun):
                  ln = os.path.join(self.dst, wh_path)
                  if (os.path.exists(ln)):
                      os.remove(ln)
                  os.symlink(wc_dir, ln)
              self.logger.info("%s / %s -> %s" % (self.dst, wh_path, wc_path))