public function generate($url, $width = null, $height = null, $options = null) {

        // Extract the path from a URL and remove it's leading slash
        $path = $this->toPath($url);

        // Skip croppa requests for images the ignore regexp
        if (isset($this->config['ignore'])
            && preg_match('#'.$this->config['ignore'].'#', $path)) {
            return $this->pathToUrl($path);
        }

        // Defaults
        if (empty($path)) return; // Don't allow empty strings
        if (!$width && !$height) return $this->pathToUrl($path); // Pass through if empty
        $width = $width ? round($width) : '_';
        $height = $height ? round($height) : '_';

        // Produce width, height, and options
        $suffix = '-'.$width.'x'.$height;
        if ($options && is_array($options)) {
            foreach($options as $key => $val) {
                if (is_numeric($key)) $suffix .= '-'.$val;
                elseif (is_array($val)) $suffix .= '-'.$key.'('.implode(',',$val).')';
                else $suffix .= '-'.$key.'('.$val.')';
            }
        }

        // Assemble the new path
        $parts = pathinfo($path);
        $path = trim($parts['dirname'],'/').'/'.$parts['filename'].$suffix;
        if (isset($parts['extension'])) $path .= '.'.$parts['extension'];
        $url = $this->pathToUrl($path);

        // Secure with hash token
        if ($token = $this->signingToken($url)) $url .= '?token='.$token;

        // Return the $url
        return $url;
    }