Full Code of mosbth/cimage for AI

master b44be78f063c cached
178 files
2.4 MB
641.4k tokens
559 symbols
1 requests
Download .txt
Showing preview only (2,565K chars total). Download the full file or copy to clipboard to get everything.
Repository: mosbth/cimage
Branch: master
Commit: b44be78f063c
Files: 178
Total size: 2.4 MB

Directory structure:
gitextract_499basvb/

├── .gitignore
├── .scrutinizer.yml
├── .travis.yml
├── CAsciiArt.php
├── CCache.php
├── CFastTrackCache.php
├── CHttpGet.php
├── CImage.php
├── CRemoteImage.php
├── CWhitelist.php
├── LICENSE.txt
├── README.md
├── REVISION.md
├── SECURITY.md
├── autoload.php
├── bin/
│   ├── cache.bash
│   └── create-img-single.bash
├── cache/
│   └── .gitignore
├── composer.json
├── defines.php
├── docker-compose.yaml
├── docs/
│   └── api/
│       ├── .htaccess
│       ├── classes/
│       │   ├── CAsciiArt.html
│       │   ├── CHttpGet.html
│       │   ├── CImage.html
│       │   ├── CImage_RemoteDownloadTest.html
│       │   ├── CRemoteImage.html
│       │   ├── CWhitelist.html
│       │   └── CWhitelistTest.html
│       ├── css/
│       │   ├── jquery.iviewer.css
│       │   ├── phpdocumentor-clean-icons/
│       │   │   ├── Read Me.txt
│       │   │   ├── lte-ie7.js
│       │   │   └── style.css
│       │   ├── prism.css
│       │   └── template.css
│       ├── files/
│       │   ├── CAsciiArt.html
│       │   ├── CAsciiArt.php.txt
│       │   ├── CHttpGet.html
│       │   ├── CHttpGet.php.txt
│       │   ├── CImage.html
│       │   ├── CImage.php.txt
│       │   ├── CRemoteImage.html
│       │   ├── CRemoteImage.php.txt
│       │   ├── CWhitelist.html
│       │   ├── CWhitelist.php.txt
│       │   ├── autoload.html
│       │   ├── autoload.php.txt
│       │   ├── test%2FCImage_RemoteDownloadTest.php.txt
│       │   ├── test%2FCWhitelistTest.php.txt
│       │   ├── test%2Fconfig.php.txt
│       │   ├── test.CImage_RemoteDownloadTest.html
│       │   ├── test.CWhitelistTest.html
│       │   ├── test.config.html
│       │   ├── webroot/
│       │   │   ├── img.php.txt
│       │   │   └── img_config.php.txt
│       │   ├── webroot%2Fcheck_system.php.txt
│       │   ├── webroot%2Fcompare%2Fcompare-test.php.txt
│       │   ├── webroot%2Fcompare%2Fcompare.php.txt
│       │   ├── webroot%2Fimg.php.txt
│       │   ├── webroot%2Fimg_config.php.txt
│       │   ├── webroot%2Fimg_header.php.txt
│       │   ├── webroot%2Fimgd.php.txt
│       │   ├── webroot%2Fimgp.php.txt
│       │   ├── webroot%2Fimgs.php.txt
│       │   ├── webroot%2Ftest%2Fconfig.php.txt
│       │   ├── webroot%2Ftest%2Ftemplate.php.txt
│       │   ├── webroot%2Ftest%2Ftest.php.txt
│       │   ├── webroot%2Ftest%2Ftest_issue29.php.txt
│       │   ├── webroot%2Ftest%2Ftest_issue36_aro.php.txt
│       │   ├── webroot%2Ftest%2Ftest_issue36_rb-ra-180.php.txt
│       │   ├── webroot%2Ftest%2Ftest_issue36_rb-ra-270.php.txt
│       │   ├── webroot%2Ftest%2Ftest_issue36_rb-ra-45.php.txt
│       │   ├── webroot%2Ftest%2Ftest_issue36_rb-ra-90.php.txt
│       │   ├── webroot%2Ftest%2Ftest_issue38.php.txt
│       │   ├── webroot%2Ftest%2Ftest_issue40.php.txt
│       │   ├── webroot%2Ftest%2Ftest_issue49.php.txt
│       │   ├── webroot%2Ftest%2Ftest_issue52-cf.php.txt
│       │   ├── webroot%2Ftest%2Ftest_issue52-stretch.php.txt
│       │   ├── webroot%2Ftest%2Ftest_issue52.php.txt
│       │   ├── webroot%2Ftest%2Ftest_issue58.php.txt
│       │   ├── webroot%2Ftest%2Ftest_issue60.php.txt
│       │   ├── webroot%2Ftest%2Ftest_option-crop.php.txt
│       │   ├── webroot%2Ftest%2Ftest_option-no-upscale.php.txt
│       │   ├── webroot%2Ftest%2Ftest_option-save-as.php.txt
│       │   ├── webroot.check_system.html
│       │   ├── webroot.compare.compare-test.html
│       │   ├── webroot.compare.compare.html
│       │   ├── webroot.img.html
│       │   ├── webroot.img_config.html
│       │   ├── webroot.img_header.html
│       │   ├── webroot.imgd.html
│       │   ├── webroot.imgp.html
│       │   ├── webroot.imgs.html
│       │   ├── webroot.test.config.html
│       │   ├── webroot.test.template.html
│       │   ├── webroot.test.test.html
│       │   ├── webroot.test.test_issue29.html
│       │   ├── webroot.test.test_issue36_aro.html
│       │   ├── webroot.test.test_issue36_rb-ra-180.html
│       │   ├── webroot.test.test_issue36_rb-ra-270.html
│       │   ├── webroot.test.test_issue36_rb-ra-45.html
│       │   ├── webroot.test.test_issue36_rb-ra-90.html
│       │   ├── webroot.test.test_issue38.html
│       │   ├── webroot.test.test_issue40.html
│       │   ├── webroot.test.test_issue49.html
│       │   ├── webroot.test.test_issue52-cf.html
│       │   ├── webroot.test.test_issue52-stretch.html
│       │   ├── webroot.test.test_issue52.html
│       │   ├── webroot.test.test_issue58.html
│       │   ├── webroot.test.test_issue60.html
│       │   ├── webroot.test.test_option-crop.html
│       │   ├── webroot.test.test_option-no-upscale.html
│       │   └── webroot.test.test_option-save-as.html
│       ├── font/
│       │   └── FontAwesome.otf
│       ├── graphs/
│       │   └── class.html
│       ├── images/
│       │   └── iviewer/
│       │       ├── grab.cur
│       │       └── hand.cur
│       ├── index.html
│       ├── js/
│       │   ├── html5.js
│       │   ├── jquery.dotdotdot-1.5.9.js
│       │   ├── jquery.iviewer.js
│       │   ├── jquery.mousewheel.js
│       │   └── jquery.smooth-scroll.js
│       ├── namespaces/
│       │   └── default.html
│       └── reports/
│           ├── deprecated.html
│           ├── errors.html
│           └── markers.html
├── functions.php
├── icc/
│   └── sRGB_IEC61966-2-1_black_scaled.icc
├── phpcs.xml
├── phpdoc.xml
├── phpunit.xml
├── test/
│   ├── CCacheTest.php
│   ├── CImageDummyTest.php
│   ├── CImageRemoteDownloadTest.php
│   ├── CImageSRGBTest.php
│   ├── CWhitelistTest.php
│   └── config.php
└── webroot/
    ├── check_system.php
    ├── compare/
    │   ├── compare-test.php
    │   ├── compare.php
    │   └── issue117-PNG24.php
    ├── htaccess
    ├── img/
    │   ├── car-gif
    │   ├── car-jpg
    │   ├── car-png
    │   └── lena.tif
    ├── img.php
    ├── img_config.php
    ├── img_header.php
    ├── imgd.php
    ├── imgf.php
    ├── imgp.php
    ├── imgs.php
    ├── js/
    │   └── cimage.js
    ├── test/
    │   ├── config.php
    │   ├── template.php
    │   ├── test.php
    │   ├── test_issue101-dummy.php
    │   ├── test_issue29.php
    │   ├── test_issue36_aro.php
    │   ├── test_issue36_rb-ra-180.php
    │   ├── test_issue36_rb-ra-270.php
    │   ├── test_issue36_rb-ra-45.php
    │   ├── test_issue36_rb-ra-90.php
    │   ├── test_issue38.php
    │   ├── test_issue40.php
    │   ├── test_issue49.php
    │   ├── test_issue52-cf.php
    │   ├── test_issue52-stretch.php
    │   ├── test_issue52.php
    │   ├── test_issue58.php
    │   ├── test_issue60.php
    │   ├── test_issue85.php
    │   ├── test_option-crop.php
    │   ├── test_option-no-upscale.php
    │   └── test_option-save-as.php
    └── tests.php

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
# Cache files
cache/*

# Test
coverage/
coverage.clover

# Composer
composer.lock
vendor

# Build and test
build/

# Mac OS
.DS_Store


================================================
FILE: .scrutinizer.yml
================================================
imports:
    - php

filter:
    excluded_paths:
        - test/
        - webroot/check*
        - webroot/imgd.php
        - webroot/imgp.php
        - webroot/imgs.php
        - webroot/test/

checks:
    php:
        code_rating: true
        duplication: true

tools:
   # Copy/Paste Detector                              
   php_cpd: true

   # Metrics
   php_pdepend: true

   # Some Metrics + Bug Detection/Auto-Fixes          
   php_analyzer: true

   php_code_sniffer:
       config:
           standard: "PSR2"

   php_sim:
       min_mass: 16 # Defaults to 16                  

   php_mess_detector:
       #config:                                       
       #    ruleset: ../your-phpmd-ruleset/ruleset.xml

build:
    tests:
        override:
            -
                command: 'phpunit'
                coverage:
                    file: 'coverage.clover'
                    format: 'php-clover'


================================================
FILE: .travis.yml
================================================
language: php

php:
    - 5.4
    - 5.5
    - 5.6
    - hhvm
    - nightly
    - "7.0"



sudo: false



git:
    submodules: false



addons:
    apt:
        packages:
            #- php-codesniffer
            #- phpmd
            #- shellcheck


matrix:
    allow_failures:
        - php: hhvm
        - php: nightly



before_script:

    # Create a build directory for output
    # Store all files in your own bin
    #- install --directory build/bin
    #- export PATH=$PATH:$PWD/build/bin/


    # Install validation tools
    #- npm install -g htmlhint csslint jshint jscs jsonlint js-yaml html-minifier@0.8.0 clean-css uglify-js
    
    # Install phpcs
    #- curl -OL https://squizlabs.github.io/PHP_CodeSniffer/phpcs.phar
    #- install --mode 0755 phpcs.phar $PWD/build/bin/phpcs

    # Install phpmd
    #- wget -c http://static.phpmd.org/php/latest/phpmd.phar
    #- install --mode 0755 phpmd.phar $PWD/build/bin/phpmd



script:
    # Check versions of validation tools
    #- node --version
    #- npm --version
    
    #- htmlhint --version
    #- csslint --version
    #- jscs --version
    #- jshint --version
    #- phpcs --version
    #- phpmd --version
    #- jsonlint --version
    #- js-yaml --version
    #- shellcheck --version
    
    #- html-minifier --version
    #- cleancss --version
    #- uglifyjs --version
    
    # Run validation & publish
    #- make phpunit
    - composer validate
    - phpunit
    #- make phpcs



notifications:
    irc: "irc.freenode.org#dbwebb"

    webhooks:
        urls:
          - https://webhooks.gitter.im/e/a89832db4f939e85ba97
        on_success: change  # options: [always|never|change] default: always
        on_failure: always  # options: [always|never|change] default: always
        on_start: never     # options: [always|never|change] default: always


================================================
FILE: CAsciiArt.php
================================================
<?php
/**
 * Create an ASCII version of an image.
 *
 */
class CAsciiArt
{
    /**
     * Character set to use.
     */
    private $characterSet = array(
        'one' => "#0XT|:,.' ",
        'two' => "@%#*+=-:. ",
        'three' => "$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,\"^`'. "
    );



    /**
     * Current character set.
     */
    private $characters = null;



    /**
     * Length of current character set.
     */
    private $charCount = null;



    /**
     * Scale of the area to swap to a character.
     */
    private $scale = null;



    /**
     * Strategy to calculate luminance.
     */
    private $luminanceStrategy = null;



    /**
     * Constructor which sets default options.
     */
    public function __construct()
    {
        $this->setOptions();
    }



    /**
     * Add a custom character set.
     *
     * @param string $key   for the character set.
     * @param string $value for the character set.
     *
     * @return $this
     */
    public function addCharacterSet($key, $value)
    {
        $this->characterSet[$key] = $value;
        return $this;
    }



    /**
     * Set options for processing, defaults are available.
     *
     * @param array $options to use as default settings.
     *
     * @return $this
     */
    public function setOptions($options = array())
    {
        $default = array(
            "characterSet" => 'two',
            "scale" => 14,
            "luminanceStrategy" => 3,
            "customCharacterSet" => null,
        );
        $default = array_merge($default, $options);
        
        if (!is_null($default['customCharacterSet'])) {
            $this->addCharacterSet('custom', $default['customCharacterSet']);
            $default['characterSet'] = 'custom';
        }
        
        $this->scale = $default['scale'];
        $this->characters = $this->characterSet[$default['characterSet']];
        $this->charCount = strlen($this->characters);
        $this->luminanceStrategy = $default['luminanceStrategy'];
        
        return $this;
    }



    /**
     * Create an Ascii image from an image file.
     *
     * @param string $filename of the image to use.
     *
     * @return string $ascii with the ASCII image.
     */
    public function createFromFile($filename)
    {
        $img = imagecreatefromstring(file_get_contents($filename));
        list($width, $height) = getimagesize($filename);

        $ascii = null;
        $incY = $this->scale;
        $incX = $this->scale / 2;
        
        for ($y = 0; $y < $height - 1; $y += $incY) {
            for ($x = 0; $x < $width - 1; $x += $incX) {
                $toX = min($x + $this->scale / 2, $width - 1);
                $toY = min($y + $this->scale, $height - 1);
                $luminance = $this->luminanceAreaAverage($img, $x, $y, $toX, $toY);
                $ascii .= $this->luminance2character($luminance);
            }
            $ascii .= PHP_EOL;
        }

        return $ascii;
    }



    /**
     * Get the luminance from a region of an image using average color value.
     *
     * @param string  $img the image.
     * @param integer $x1  the area to get pixels from.
     * @param integer $y1  the area to get pixels from.
     * @param integer $x2  the area to get pixels from.
     * @param integer $y2  the area to get pixels from.
     *
     * @return integer $luminance with a value between 0 and 100.
     */
    public function luminanceAreaAverage($img, $x1, $y1, $x2, $y2)
    {
        $numPixels = ($x2 - $x1 + 1) * ($y2 - $y1 + 1);
        $luminance = 0;
        
        for ($x = $x1; $x <= $x2; $x++) {
            for ($y = $y1; $y <= $y2; $y++) {
                $rgb   = imagecolorat($img, $x, $y);
                $red   = (($rgb >> 16) & 0xFF);
                $green = (($rgb >> 8) & 0xFF);
                $blue  = ($rgb & 0xFF);
                $luminance += $this->getLuminance($red, $green, $blue);
            }
        }
        
        return $luminance / $numPixels;
    }



    /**
     * Calculate luminance value with different strategies.
     *
     * @param integer $red   The color red.
     * @param integer $green The color green.
     * @param integer $blue  The color blue.
     *
     * @return float $luminance with a value between 0 and 1.
     */
    public function getLuminance($red, $green, $blue)
    {
        switch ($this->luminanceStrategy) {
            case 1:
                $luminance = ($red * 0.2126 + $green * 0.7152 + $blue * 0.0722) / 255;
                break;
            case 2:
                $luminance = ($red * 0.299 + $green * 0.587 + $blue * 0.114) / 255;
                break;
            case 3:
                $luminance = sqrt(0.299 * pow($red, 2) + 0.587 * pow($green, 2) + 0.114 * pow($blue, 2)) / 255;
                break;
            case 0:
            default:
                $luminance = ($red + $green + $blue) / (255 * 3);
        }

        return $luminance;
    }



    /**
     * Translate the luminance value to a character.
     *
     * @param string $position a value between 0-100 representing the
     *                         luminance.
     *
     * @return string with the ascii character.
     */
    public function luminance2character($luminance)
    {
        $position = (int) round($luminance * ($this->charCount - 1));
        $char = $this->characters[$position];
        return $char;
    }
}


================================================
FILE: CCache.php
================================================
<?php
/**
 * Deal with the cache directory and cached items.
 *
 */
class CCache
{
    /**
     * Path to the cache directory.
     */
    private $path;



    /**
     * Set the path to the cache dir which must exist.
     *
     * @param string path to the cache dir.
     *
     * @throws Exception when $path is not a directory.
     *
     * @return $this
     */
    public function setDir($path)
    {
        if (!is_dir($path)) {
            throw new Exception("Cachedir is not a directory.");
        }

        $this->path = $path;

        return $this;
    }



    /**
     * Get the path to the cache subdir and try to create it if its not there.
     *
     * @param string $subdir name of subdir
     * @param array  $create default is to try to create the subdir
     *
     * @return string | boolean as real path to the subdir or
     *                          false if it does not exists
     */
    public function getPathToSubdir($subdir, $create = true)
    {
        $path = realpath($this->path . "/" . $subdir);

        if (is_dir($path)) {
            return $path;
        }

        if ($create && defined('WINDOWS2WSL')) {
            // Special case to solve Windows 2 WSL integration
            $path = $this->path . "/" . $subdir;

            if (mkdir($path)) {
                return realpath($path);
            }
        }

        if ($create && is_writable($this->path)) {
            $path = $this->path . "/" . $subdir;

            if (mkdir($path)) {
                return realpath($path);
            }
        }

        return false;
    }



    /**
     * Get status of the cache subdir.
     *
     * @param string $subdir name of subdir
     *
     * @return string with status
     */
    public function getStatusOfSubdir($subdir)
    {
        $path = realpath($this->path . "/" . $subdir);

        $exists = is_dir($path);
        $res  = $exists ? "exists" : "does not exist";
        
        if ($exists) {
            $res .= is_writable($path) ? ", writable" : ", not writable";
        }

        return $res;
    }



    /**
     * Remove the cache subdir.
     *
     * @param string $subdir name of subdir
     *
     * @return null | boolean true if success else false, null if no operation
     */
    public function removeSubdir($subdir)
    {
        $path = realpath($this->path . "/" . $subdir);

        if (is_dir($path)) {
            return rmdir($path);
        }

        return null;
    }
}


================================================
FILE: CFastTrackCache.php
================================================
<?php
/**
 * Enable a fast track cache with a json representation of the image delivery.
 *
 */
class CFastTrackCache
{
    /**
     * Cache is disabled to start with.
     */
    private $enabled = false;



    /**
     * Path to the cache directory.
     */
    private $path;



    /**
     * Filename of current cache item.
     */
    private $filename;



    /**
     * Container with items to store as cached item.
     */
    private $container;



    /**
     * Enable or disable cache.
     *
     * @param boolean $enable set to true to enable, false to disable
     *
     * @return $this
     */
    public function enable($enabled)
    {
        $this->enabled = $enabled;
        return $this;
    }



    /**
     * Set the path to the cache dir which must exist.
     *
     * @param string $path to the cache dir.
     *
     * @throws Exception when $path is not a directory.
     *
     * @return $this
     */
    public function setCacheDir($path)
    {
        if (!is_dir($path)) {
            throw new Exception("Cachedir is not a directory.");
        }

        $this->path = rtrim($path, "/");

        return $this;
    }



    /**
     * Set the filename to store in cache, use the querystring to create that
     * filename.
     *
     * @param array $clear items to clear in $_GET when creating the filename.
     *
     * @return string as filename created.
     */
    public function setFilename($clear)
    {
        $query = $_GET;

        // Remove parts from querystring that should not be part of filename
        foreach ($clear as $value) {
            unset($query[$value]);
        }

        arsort($query);
        $queryAsString = http_build_query($query);

        $this->filename = md5($queryAsString);

        if (CIMAGE_DEBUG) {
            $this->container["query-string"] = $queryAsString;
        }

        return $this->filename;
    }



    /**
     * Add header items.
     *
     * @param string $header add this as header.
     *
     * @return $this
     */
    public function addHeader($header)
    {
        $this->container["header"][] = $header;
        return $this;
    }



    /**
     * Add header items on output, these are not output when 304.
     *
     * @param string $header add this as header.
     *
     * @return $this
     */
    public function addHeaderOnOutput($header)
    {
        $this->container["header-output"][] = $header;
        return $this;
    }



    /**
     * Set path to source image to.
     *
     * @param string $source path to source image file.
     *
     * @return $this
     */
    public function setSource($source)
    {
        $this->container["source"] = $source;
        return $this;
    }



    /**
     * Set last modified of source image, use to check for 304.
     *
     * @param string $lastModified
     *
     * @return $this
     */
    public function setLastModified($lastModified)
    {
        $this->container["last-modified"] = $lastModified;
        return $this;
    }



    /**
     * Get filename of cached item.
     *
     * @return string as filename.
     */
    public function getFilename()
    {
        return $this->path . "/" . $this->filename;
    }



    /**
     * Write current item to cache.
     *
     * @return boolean if cache file was written.
     */
    public function writeToCache()
    {
        if (!$this->enabled) {
            return false;
        }

        if (is_dir($this->path) && is_writable($this->path)) {
            $filename = $this->getFilename();
            return file_put_contents($filename, json_encode($this->container)) !== false;
        }

        return false;
    }



    /**
     * Output current item from cache, if available.
     *
     * @return void
     */
    public function output()
    {
        $filename = $this->getFilename();
        if (!is_readable($filename)) {
            return;
        }

        $item = json_decode(file_get_contents($filename), true);

        if (!is_readable($item["source"])) {
            return;
        }

        foreach ($item["header"] as $value) {
            header($value);
        }

        if (isset($_SERVER["HTTP_IF_MODIFIED_SINCE"])
            && strtotime($_SERVER["HTTP_IF_MODIFIED_SINCE"]) == $item["last-modified"]) {
            header("HTTP/1.0 304 Not Modified");
            if (CIMAGE_DEBUG) {
                trace(__CLASS__ . " 304");
            }
            exit;
        }

        foreach ($item["header-output"] as $value) {
            header($value);
        }

        if (CIMAGE_DEBUG) {
            trace(__CLASS__ . " 200");
        }
        readfile($item["source"]);
        exit;
    }
}


================================================
FILE: CHttpGet.php
================================================
<?php
/**
 * Get a image from a remote server using HTTP GET and If-Modified-Since.
 *
 */
class CHttpGet
{
    private $request  = array();
    private $response = array();



    /**
    * Constructor
    *
    */
    public function __construct()
    {
        $this->request['header'] = array();
    }



    /**
     * Build an encoded url.
     *
     * @param string $baseUrl This is the original url which will be merged.
     * @param string $merge   Thse parts should be merged into the baseUrl,
     *                        the format is as parse_url.
     *
     * @return string $url as the modified url.
     */
    public function buildUrl($baseUrl, $merge)
    {
        $parts = parse_url($baseUrl);
        $parts = array_merge($parts, $merge);

        $url  = $parts['scheme'];
        $url .= "://";
        $url .= $parts['host'];
        $url .= isset($parts['port'])
            ? ":" . $parts['port']
            : "" ;
        $url .= $parts['path'];

        return $url;
    }



    /**
     * Set the url for the request.
     *
     * @param string $url
     *
     * @return $this
     */
    public function setUrl($url)
    {
        $parts = parse_url($url);
        
        $path = "";
        if (isset($parts['path'])) {
            $pathParts = explode('/', $parts['path']);
            unset($pathParts[0]);
            foreach ($pathParts as $value) {
                $path .= "/" . rawurlencode($value);
            }
        }
        $url = $this->buildUrl($url, array("path" => $path));

        $this->request['url'] = $url;
        return $this;
    }



    /**
     * Set custom header field for the request.
     *
     * @param string $field
     * @param string $value
     *
     * @return $this
     */
    public function setHeader($field, $value)
    {
        $this->request['header'][] = "$field: $value";
        return $this;
    }



    /**
     * Set header fields for the request.
     *
     * @param string $field
     * @param string $value
     *
     * @return $this
     */
    public function parseHeader()
    {
        //$header = explode("\r\n", rtrim($this->response['headerRaw'], "\r\n"));
        
        $rawHeaders = rtrim($this->response['headerRaw'], "\r\n");
        # Handle multiple responses e.g. with redirections (proxies too)
        $headerGroups = explode("\r\n\r\n", $rawHeaders);
        # We're only interested in the last one
        $header = explode("\r\n", end($headerGroups));

        $output = array();

        if ('HTTP' === substr($header[0], 0, 4)) {
            list($output['version'], $output['status']) = explode(' ', $header[0]);
            unset($header[0]);
        }

        foreach ($header as $entry) {
            $pos = strpos($entry, ':');
            $output[trim(substr($entry, 0, $pos))] = trim(substr($entry, $pos + 1));
        }

        $this->response['header'] = $output;
        return $this;
    }



    /**
     * Perform the request.
     *
     * @param boolean $debug set to true to dump headers.
     *
     * @throws Exception when curl fails to retrieve url.
     *
     * @return boolean
     */
    public function doGet($debug = false)
    {
        $options = array(
            CURLOPT_URL             => $this->request['url'],
            CURLOPT_HEADER          => 1,
            CURLOPT_HTTPHEADER      => $this->request['header'],
            CURLOPT_AUTOREFERER     => true,
            CURLOPT_RETURNTRANSFER  => true,
            CURLINFO_HEADER_OUT     => $debug,
            CURLOPT_CONNECTTIMEOUT  => 5,
            CURLOPT_TIMEOUT         => 5,
            CURLOPT_FOLLOWLOCATION  => true,
            CURLOPT_MAXREDIRS       => 2,
        );

        $ch = curl_init();
        curl_setopt_array($ch, $options);
        $response = curl_exec($ch);

        if (!$response) {
            throw new Exception("Failed retrieving url, details follows: " . curl_error($ch));
        }

        $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
        $this->response['headerRaw'] = substr($response, 0, $headerSize);
        $this->response['body']      = substr($response, $headerSize);

        $this->parseHeader();

        if ($debug) {
            $info = curl_getinfo($ch);
            echo "Request header<br><pre>", var_dump($info['request_header']), "</pre>";
            echo "Response header (raw)<br><pre>", var_dump($this->response['headerRaw']), "</pre>";
            echo "Response header (parsed)<br><pre>", var_dump($this->response['header']), "</pre>";
        }

        curl_close($ch);
        return true;
    }



    /**
     * Get HTTP code of response.
     *
     * @return integer as HTTP status code or null if not available.
     */
    public function getStatus()
    {
        return isset($this->response['header']['status'])
            ? (int) $this->response['header']['status']
            : null;
    }



    /**
     * Get file modification time of response.
     *
     * @return int as timestamp.
     */
    public function getLastModified()
    {
        return isset($this->response['header']['Last-Modified'])
            ? strtotime($this->response['header']['Last-Modified'])
            : null;
    }



    /**
     * Get content type.
     *
     * @return string as the content type or null if not existing or invalid.
     */
    public function getContentType()
    {
        $type = isset($this->response['header']['Content-Type'])
            ? $this->response['header']['Content-Type']
            : '';

        return preg_match('#[a-z]+/[a-z]+#', $type)
            ? $type
            : null;
    }



    /**
     * Get file modification time of response.
     *
     * @param mixed $default as default value (int seconds) if date is
     *                       missing in response header.
     *
     * @return int as timestamp or $default if Date is missing in
     *             response header.
     */
    public function getDate($default = false)
    {
        return isset($this->response['header']['Date'])
            ? strtotime($this->response['header']['Date'])
            : $default;
    }



    /**
     * Get max age of cachable item.
     *
     * @param mixed $default as default value if date is missing in response
     *                       header.
     *
     * @return int as timestamp or false if not available.
     */
    public function getMaxAge($default = false)
    {
        $cacheControl = isset($this->response['header']['Cache-Control'])
            ? $this->response['header']['Cache-Control']
            : null;

        $maxAge = null;
        if ($cacheControl) {
            // max-age=2592000
            $part = explode('=', $cacheControl);
            $maxAge = ($part[0] == "max-age")
                ? (int) $part[1]
                : null;
        }

        if ($maxAge) {
            return $maxAge;
        }

        $expire = isset($this->response['header']['Expires'])
            ? strtotime($this->response['header']['Expires'])
            : null;

        return $expire ? $expire : $default;
    }



    /**
     * Get body of response.
     *
     * @return string as body.
     */
    public function getBody()
    {
        return $this->response['body'];
    }
}


================================================
FILE: CImage.php
================================================
<?php
/**
 * Resize and crop images on the fly, store generated images in a cache.
 *
 * @author  Mikael Roos mos@dbwebb.se
 * @example http://dbwebb.se/opensource/cimage
 * @link    https://github.com/mosbth/cimage
 */
#[AllowDynamicProperties]
class CImage
{

    /**
     * Constants type of PNG image
     */
    const PNG_GREYSCALE         = 0;
    const PNG_RGB               = 2;
    const PNG_RGB_PALETTE       = 3;
    const PNG_GREYSCALE_ALPHA   = 4;
    const PNG_RGB_ALPHA         = 6;



    /**
     * Constant for default image quality when not set
     */
    const JPEG_QUALITY_DEFAULT = 60;



    /**
     * Quality level for JPEG images.
     */
    private $quality;



    /**
     * Is the quality level set from external use (true) or is it default (false)?
     */
    private $useQuality = false;



    /**
     * Constant for default image quality when not set
     */
    const PNG_COMPRESSION_DEFAULT = -1;



    /**
     * Compression level for PNG images.
     */
    private $compress;



    /**
     * Is the compress level set from external use (true) or is it default (false)?
     */
    private $useCompress = false;




    /**
     * Add HTTP headers for outputing image.
     */
    private $HTTPHeader = array();



    /**
     * Default background color, red, green, blue, alpha.
     *
     * @todo remake when upgrading to PHP 5.5
     */
    /*
    const BACKGROUND_COLOR = array(
        'red'   => 0,
        'green' => 0,
        'blue'  => 0,
        'alpha' => null,
    );*/



    /**
     * Default background color to use.
     *
     * @todo remake when upgrading to PHP 5.5
     */
    //private $bgColorDefault = self::BACKGROUND_COLOR;
    private $bgColorDefault = array(
        'red'   => 0,
        'green' => 0,
        'blue'  => 0,
        'alpha' => null,
    );


    /**
     * Background color to use, specified as part of options.
     */
    private $bgColor;



    /**
     * Where to save the target file.
     */
    private $saveFolder;



    /**
     * The working image object.
     */
    private $image;



    /**
     * Image filename, may include subdirectory, relative from $imageFolder
     */
    private $imageSrc;



    /**
     * Actual path to the image, $imageFolder . '/' . $imageSrc
     */
    private $pathToImage;



    /**
     * File type for source image, as provided by getimagesize()
     */
    private $fileType;



    /**
     * File extension to use when saving image.
     */
    private $extension;



    /**
     * Output format, supports null (image) or json.
     */
    private $outputFormat = null;



    /**
     * Do lossy output using external postprocessing tools.
     */
    private $lossy = null;



    /**
     * Verbose mode to print out a trace and display the created image
     */
    private $verbose = false;



    /**
     * Keep a log/trace on what happens
     */
    private $log = array();



    /**
     * Handle image as palette image
     */
    private $palette;



    /**
     * Target filename, with path, to save resulting image in.
     */
    private $cacheFileName;



    /**
     * Set a format to save image as, or null to use original format.
     */
    private $saveAs;


    /**
     * Path to command for lossy optimize, for example pngquant.
     */
    private $pngLossy;
    private $pngLossyCmd;



    /**
     * Path to command for filter optimize, for example optipng.
     */
    private $pngFilter;
    private $pngFilterCmd;



    /**
     * Path to command for deflate optimize, for example pngout.
     */
    private $pngDeflate;
    private $pngDeflateCmd;



    /**
     * Path to command to optimize jpeg images, for example jpegtran or null.
     */
     private $jpegOptimize;
     private $jpegOptimizeCmd;



    /**
     * Image dimensions, calculated from loaded image.
     */
    private $width;  // Calculated from source image
    private $height; // Calculated from source image


    /**
     * New image dimensions, incoming as argument or calculated.
     */
    private $newWidth;
    private $newWidthOrig;  // Save original value
    private $newHeight;
    private $newHeightOrig; // Save original value


    /**
     * Change target height & width when different dpr, dpr 2 means double image dimensions.
     */
    private $dpr = 1;


    /**
     * Always upscale images, even if they are smaller than target image.
     */
    const UPSCALE_DEFAULT = true;
    private $upscale = self::UPSCALE_DEFAULT;



    /**
     * Array with details on how to crop, incoming as argument and calculated.
     */
    public $crop;
    public $cropOrig; // Save original value


    /**
     * String with details on how to do image convolution. String
     * should map a key in the $convolvs array or be a string of
     * 11 float values separated by comma. The first nine builds
     * up the matrix, then divisor and last offset.
     */
    private $convolve;


    /**
     * Custom convolution expressions, matrix 3x3, divisor and offset.
     */
    private $convolves = array(
        'lighten'       => '0,0,0, 0,12,0, 0,0,0, 9, 0',
        'darken'        => '0,0,0, 0,6,0, 0,0,0, 9, 0',
        'sharpen'       => '-1,-1,-1, -1,16,-1, -1,-1,-1, 8, 0',
        'sharpen-alt'   => '0,-1,0, -1,5,-1, 0,-1,0, 1, 0',
        'emboss'        => '1,1,-1, 1,3,-1, 1,-1,-1, 3, 0',
        'emboss-alt'    => '-2,-1,0, -1,1,1, 0,1,2, 1, 0',
        'blur'          => '1,1,1, 1,15,1, 1,1,1, 23, 0',
        'gblur'         => '1,2,1, 2,4,2, 1,2,1, 16, 0',
        'edge'          => '-1,-1,-1, -1,8,-1, -1,-1,-1, 9, 0',
        'edge-alt'      => '0,1,0, 1,-4,1, 0,1,0, 1, 0',
        'draw'          => '0,-1,0, -1,5,-1, 0,-1,0, 0, 0',
        'mean'          => '1,1,1, 1,1,1, 1,1,1, 9, 0',
        'motion'        => '1,0,0, 0,1,0, 0,0,1, 3, 0',
    );


    /**
     * Resize strategy to fill extra area with background color.
     * True or false.
     */
    private $fillToFit;



    /**
     * To store value for option scale.
     */
    private $scale;



    /**
     * To store value for option.
     */
    private $rotateBefore;



    /**
     * To store value for option.
     */
    private $rotateAfter;



    /**
     * To store value for option.
     */
    private $autoRotate;



    /**
     * To store value for option.
     */
    private $sharpen;



    /**
     * To store value for option.
     */
    private $emboss;



    /**
     * To store value for option.
     */
    private $blur;



    /**
     * Used with option area to set which parts of the image to use.
     */
    private $offset;



    /**
     * Calculate target dimension for image when using fill-to-fit resize strategy.
     */
    private $fillWidth;
    private $fillHeight;



    /**
     * Allow remote file download, default is to disallow remote file download.
     */
    private $allowRemote = false;



    /**
     * Path to cache for remote download.
     */
    private $remoteCache;



    /**
     * Pattern to recognize a remote file.
     */
    //private $remotePattern = '#^[http|https]://#';
    private $remotePattern = '#^https?://#';



    /**
     * Use the cache if true, set to false to ignore the cached file.
     */
    private $useCache = true;


    /**
    * Disable the fasttrackCacke to start with, inject an object to enable it.
    */
    private $fastTrackCache = null;



    /*
     * Set whitelist for valid hostnames from where remote source can be
     * downloaded.
     */
    private $remoteHostWhitelist = null;



    /*
     * Do verbose logging to file by setting this to a filename.
     */
    private $verboseFileName = null;



    /*
     * Output to ascii can take som options as an array.
     */
    private $asciiOptions = array();



    /*
     * Use interlaced progressive mode for JPEG images.
     */
    private $interlace = false;



    /*
     * Image copy strategy, defaults to RESAMPLE.
     */
     const RESIZE = 1;
     const RESAMPLE = 2;
     private $copyStrategy = NULL;



    /**
     * Properties, the class is mutable and the method setOptions()
     * decides (partly) what properties are created.
     *
     * @todo Clean up these and check if and how they are used
     */

    public $keepRatio;
    public $cropToFit;
    private $cropWidth;
    private $cropHeight;
    public $crop_x;
    public $crop_y;
    public $filters;
    private $attr; // Calculated from source image




    /**
     * Constructor, can take arguments to init the object.
     *
     * @param string $imageSrc    filename which may contain subdirectory.
     * @param string $imageFolder path to root folder for images.
     * @param string $saveFolder  path to folder where to save the new file or null to skip saving.
     * @param string $saveName    name of target file when saveing.
     */
    public function __construct($imageSrc = null, $imageFolder = null, $saveFolder = null, $saveName = null)
    {
        $this->setSource($imageSrc, $imageFolder);
        $this->setTarget($saveFolder, $saveName);
    }



    /**
     * Inject object and use it, must be available as member.
     *
     * @param string $property to set as object.
     * @param object $object   to set to property.
     *
     * @return $this
     */
    public function injectDependency($property, $object)
    {
        if (!property_exists($this, $property)) {
            $this->raiseError("Injecting unknown property.");
        }
        $this->$property = $object;
        return $this;
    }



    /**
     * Set verbose mode.
     *
     * @param boolean $mode true or false to enable and disable verbose mode,
     *                      default is true.
     *
     * @return $this
     */
    public function setVerbose($mode = true)
    {
        $this->verbose = $mode;
        return $this;
    }



    /**
     * Set save folder, base folder for saving cache files.
     *
     * @todo clean up how $this->saveFolder is used in other methods.
     *
     * @param string $path where to store cached files.
     *
     * @return $this
     */
    public function setSaveFolder($path)
    {
        $this->saveFolder = $path;
        return $this;
    }



    /**
     * Use cache or not.
     *
     * @param boolean $use true or false to use cache.
     *
     * @return $this
     */
    public function useCache($use = true)
    {
        $this->useCache = $use;
        return $this;
    }



    /**
     * Create and save a dummy image. Use dimensions as stated in
     * $this->newWidth, or $width or default to 100 (same for height.
     *
     * @param integer $width  use specified width for image dimension.
     * @param integer $height use specified width for image dimension.
     *
     * @return $this
     */
    public function createDummyImage($width = null, $height = null)
    {
        $this->newWidth  = $this->newWidth  ?: $width  ?: 100;
        $this->newHeight = $this->newHeight ?: $height ?: 100;

        $this->image = $this->CreateImageKeepTransparency($this->newWidth, $this->newHeight);

        return $this;
    }



    /**
     * Allow or disallow remote image download.
     *
     * @param boolean $allow   true or false to enable and disable.
     * @param string  $cache   path to cache dir.
     * @param string  $pattern to use to detect if its a remote file.
     *
     * @return $this
     */
    public function setRemoteDownload($allow, $cache, $pattern = null)
    {
        $this->allowRemote = $allow;
        $this->remoteCache = $cache;
        $this->remotePattern = is_null($pattern) ? $this->remotePattern : $pattern;

        $this->log(
            "Set remote download to: "
            . ($this->allowRemote ? "true" : "false")
            . " using pattern "
            . $this->remotePattern
        );

        return $this;
    }



    /**
     * Check if the image resource is a remote file or not.
     *
     * @param string $src check if src is remote.
     *
     * @return boolean true if $src is a remote file, else false.
     */
    public function isRemoteSource($src)
    {
        $remote = preg_match($this->remotePattern, $src);
        $this->log("Detected remote image: " . ($remote ? "true" : "false"));
        return !!$remote;
    }



    /**
     * Set whitelist for valid hostnames from where remote source can be
     * downloaded.
     *
     * @param array $whitelist with regexp hostnames to allow download from.
     *
     * @return $this
     */
    public function setRemoteHostWhitelist($whitelist = null)
    {
        $this->remoteHostWhitelist = $whitelist;
        $this->log(
            "Setting remote host whitelist to: "
            . (is_null($whitelist) ? "null" : print_r($whitelist, 1))
        );
        return $this;
    }



    /**
     * Check if the hostname for the remote image, is on a whitelist,
     * if the whitelist is defined.
     *
     * @param string $src the remote source.
     *
     * @return boolean true if hostname on $src is in the whitelist, else false.
     */
    public function isRemoteSourceOnWhitelist($src)
    {
        if (is_null($this->remoteHostWhitelist)) {
            $this->log("Remote host on whitelist not configured - allowing.");
            return true;
        }

        $whitelist = new CWhitelist();
        $hostname = parse_url($src, PHP_URL_HOST);
        $allow = $whitelist->check($hostname, $this->remoteHostWhitelist);

        $this->log(
            "Remote host is on whitelist: "
            . ($allow ? "true" : "false")
        );
        return $allow;
    }



    /**
     * Check if file extension is valid as a file extension.
     *
     * @param string $extension of image file.
     *
     * @return $this
     */
    private function checkFileExtension($extension)
    {
        $valid = array('jpg', 'jpeg', 'png', 'gif', 'webp');

        in_array(strtolower($extension), $valid)
            or $this->raiseError('Not a valid file extension.');

        return $this;
    }



    /**
     * Normalize the file extension.
     *
     * @param string $extension of image file or skip to use internal.
     *
     * @return string $extension as a normalized file extension.
     */
    private function normalizeFileExtension($extension = "")
    {
        $extension = strtolower($extension ? $extension : $this->extension ?? "");

        if ($extension == 'jpeg') {
                $extension = 'jpg';
        }

        return $extension;
    }



    /**
     * Download a remote image and return path to its local copy.
     *
     * @param string $src remote path to image.
     *
     * @return string as path to downloaded remote source.
     */
    public function downloadRemoteSource($src)
    {
        if (!$this->isRemoteSourceOnWhitelist($src)) {
            throw new Exception("Hostname is not on whitelist for remote sources.");
        }

        $remote = new CRemoteImage();

        if (!is_writable($this->remoteCache)) {
            $this->log("The remote cache is not writable.");
        }

        $remote->setCache($this->remoteCache);
        $remote->useCache($this->useCache);
        $src = $remote->download($src);

        $this->log("Remote HTTP status: " . $remote->getStatus());
        $this->log("Remote item is in local cache: $src");
        $this->log("Remote details on cache:" . print_r($remote->getDetails(), true));

        return $src;
    }



    /**
     * Set source file to use as image source.
     *
     * @param string $src of image.
     * @param string $dir as optional base directory where images are.
     *
     * @return $this
     */
    public function setSource($src, $dir = null)
    {
        if (!isset($src)) {
            $this->imageSrc = null;
            $this->pathToImage = null;
            return $this;
        }

        if ($this->allowRemote && $this->isRemoteSource($src)) {
            $src = $this->downloadRemoteSource($src);
            $dir = null;
        }

        if (!isset($dir)) {
            $dir = dirname($src);
            $src = basename($src);
        }

        $this->imageSrc     = ltrim($src, '/');
        $imageFolder        = rtrim($dir, '/');
        $this->pathToImage  = $imageFolder . '/' . $this->imageSrc;

        return $this;
    }



    /**
     * Set target file.
     *
     * @param string $src of target image.
     * @param string $dir as optional base directory where images are stored.
     *                    Uses $this->saveFolder if null.
     *
     * @return $this
     */
    public function setTarget($src = null, $dir = null)
    {
        if (!isset($src)) {
            $this->cacheFileName = null;
            return $this;
        }

        if (isset($dir)) {
            $this->saveFolder = rtrim($dir, '/');
        }

        $this->cacheFileName  = $this->saveFolder . '/' . $src;

        // Sanitize filename
        $this->cacheFileName = preg_replace('/^a-zA-Z0-9\.-_/', '', $this->cacheFileName);
        $this->log("The cache file name is: " . $this->cacheFileName);

        return $this;
    }



    /**
     * Get filename of target file.
     *
     * @return Boolean|String as filename of target or false if not set.
     */
    public function getTarget()
    {
        return $this->cacheFileName;
    }



    /**
     * Set options to use when processing image.
     *
     * @param array $args used when processing image.
     *
     * @return $this
     */
    public function setOptions($args)
    {
        $this->log("Set new options for processing image.");

        $defaults = array(
            // Options for calculate dimensions
            'newWidth'    => null,
            'newHeight'   => null,
            'aspectRatio' => null,
            'keepRatio'   => true,
            'cropToFit'   => false,
            'fillToFit'   => null,
            'crop'        => null, //array('width'=>null, 'height'=>null, 'start_x'=>0, 'start_y'=>0),
            'area'        => null, //'0,0,0,0',
            'upscale'     => self::UPSCALE_DEFAULT,

            // Options for caching or using original
            'useCache'    => true,
            'useOriginal' => true,

            // Pre-processing, before resizing is done
            'scale'        => null,
            'rotateBefore' => null,
            'autoRotate'  => false,

            // General options
            'bgColor'     => null,

            // Post-processing, after resizing is done
            'palette'     => null,
            'filters'     => null,
            'sharpen'     => null,
            'emboss'      => null,
            'blur'        => null,
            'convolve'       => null,
            'rotateAfter' => null,
            'interlace' => null,

            // Output format
            'outputFormat' => null,
            'dpr'          => 1,

            // Postprocessing using external tools
            'lossy' => null,
        );

        // Convert crop settings from string to array
        if (isset($args['crop']) && !is_array($args['crop'])) {
            $pices = explode(',', $args['crop']);
            $args['crop'] = array(
                'width'   => $pices[0],
                'height'  => $pices[1],
                'start_x' => $pices[2],
                'start_y' => $pices[3],
            );
        }

        // Convert area settings from string to array
        if (isset($args['area']) && !is_array($args['area'])) {
                $pices = explode(',', $args['area']);
                $args['area'] = array(
                    'top'    => $pices[0],
                    'right'  => $pices[1],
                    'bottom' => $pices[2],
                    'left'   => $pices[3],
                );
        }

        // Convert filter settings from array of string to array of array
        if (isset($args['filters']) && is_array($args['filters'])) {
            foreach ($args['filters'] as $key => $filterStr) {
                $parts = explode(',', $filterStr);
                $filter = $this->mapFilter($parts[0]);
                $filter['str'] = $filterStr;
                for ($i=1; $i<=$filter['argc']; $i++) {
                    if (isset($parts[$i])) {
                        $filter["arg{$i}"] = $parts[$i];
                    } else {
                        throw new Exception(
                            'Missing arg to filter, review how many arguments are needed at
                            http://php.net/manual/en/function.imagefilter.php'
                        );
                    }
                }
                $args['filters'][$key] = $filter;
            }
        }

        // Merge default arguments with incoming and set properties.
        //$args = array_merge_recursive($defaults, $args);
        $args = array_merge($defaults, $args);
        foreach ($defaults as $key => $val) {
            $this->{$key} = $args[$key];
        }

        if ($this->bgColor) {
            $this->setDefaultBackgroundColor($this->bgColor);
        }

        // Save original values to enable re-calculating
        $this->newWidthOrig  = $this->newWidth;
        $this->newHeightOrig = $this->newHeight;
        $this->cropOrig      = $this->crop;

        return $this;
    }



    /**
     * Map filter name to PHP filter and id.
     *
     * @param string $name the name of the filter.
     *
     * @return array with filter settings
     * @throws Exception
     */
    private function mapFilter($name)
    {
        $map = array(
            'negate'          => array('id'=>0,  'argc'=>0, 'type'=>IMG_FILTER_NEGATE),
            'grayscale'       => array('id'=>1,  'argc'=>0, 'type'=>IMG_FILTER_GRAYSCALE),
            'brightness'      => array('id'=>2,  'argc'=>1, 'type'=>IMG_FILTER_BRIGHTNESS),
            'contrast'        => array('id'=>3,  'argc'=>1, 'type'=>IMG_FILTER_CONTRAST),
            'colorize'        => array('id'=>4,  'argc'=>4, 'type'=>IMG_FILTER_COLORIZE),
            'edgedetect'      => array('id'=>5,  'argc'=>0, 'type'=>IMG_FILTER_EDGEDETECT),
            'emboss'          => array('id'=>6,  'argc'=>0, 'type'=>IMG_FILTER_EMBOSS),
            'gaussian_blur'   => array('id'=>7,  'argc'=>0, 'type'=>IMG_FILTER_GAUSSIAN_BLUR),
            'selective_blur'  => array('id'=>8,  'argc'=>0, 'type'=>IMG_FILTER_SELECTIVE_BLUR),
            'mean_removal'    => array('id'=>9,  'argc'=>0, 'type'=>IMG_FILTER_MEAN_REMOVAL),
            'smooth'          => array('id'=>10, 'argc'=>1, 'type'=>IMG_FILTER_SMOOTH),
            'pixelate'        => array('id'=>11, 'argc'=>2, 'type'=>IMG_FILTER_PIXELATE),
        );

        if (isset($map[$name])) {
            return $map[$name];
        } else {
            throw new Exception('No such filter.');
        }
    }



    /**
     * Load image details from original image file.
     *
     * @param string $file the file to load or null to use $this->pathToImage.
     *
     * @return $this
     * @throws Exception
     */
    public function loadImageDetails($file = null)
    {
        $file = $file ? $file : $this->pathToImage;

        // Special case to solve Windows 2 WSL integration
        if (!defined('WINDOWS2WSL')) {
            is_readable($file)
                or $this->raiseError('Image file does not exist.');
        }

        $info = list($this->width, $this->height, $this->fileType) = getimagesize($file);
        if (empty($info)) {
            // To support webp
            $this->fileType = false;
            if (function_exists("exif_imagetype")) {
                $this->fileType = exif_imagetype($file);
                if ($this->fileType === false) {
                    if (function_exists("imagecreatefromwebp")) {
                        $webp = imagecreatefromwebp($file);
                        if ($webp !== false) {
                            $this->width  = imagesx($webp);
                            $this->height = imagesy($webp);
                            $this->fileType = IMG_WEBP;
                        }
                    }
                }
            }
        }

        if (!$this->fileType) {
            throw new Exception("Loading image details, the file doesn't seem to be a valid image.");
        }

        if ($this->verbose) {
            $this->log("Loading image details for: {$file}");
            $this->log(" Image width x height (type): {$this->width} x {$this->height} ({$this->fileType}).");
            $this->log(" Image filesize: " . filesize($file) . " bytes.");
            $this->log(" Image mimetype: " . $this->getMimeType());
        }

        return $this;
    }



    /**
     * Get mime type for image type.
     *
     * @return $this
     * @throws Exception
     */
    protected function getMimeType()
    {
        if ($this->fileType === IMG_WEBP) {
            return "image/webp";
        }

        return image_type_to_mime_type($this->fileType);
    }



    /**
     * Init new width and height and do some sanity checks on constraints, before any
     * processing can be done.
     *
     * @return $this
     * @throws Exception
     */
    public function initDimensions()
    {
        $this->log("Init dimension (before) newWidth x newHeight is {$this->newWidth} x {$this->newHeight}.");

        // width as %
        if ($this->newWidth
            && $this->newWidth[strlen($this->newWidth)-1] == '%') {
            $this->newWidth = $this->width * substr($this->newWidth, 0, -1) / 100;
            $this->log("Setting new width based on % to {$this->newWidth}");
        }

        // height as %
        if ($this->newHeight
            && $this->newHeight[strlen($this->newHeight)-1] == '%') {
            $this->newHeight = $this->height * substr($this->newHeight, 0, -1) / 100;
            $this->log("Setting new height based on % to {$this->newHeight}");
        }

        is_null($this->aspectRatio) or is_numeric($this->aspectRatio) or $this->raiseError('Aspect ratio out of range');

        // width & height from aspect ratio
        if ($this->aspectRatio && is_null($this->newWidth) && is_null($this->newHeight)) {
            if ($this->aspectRatio >= 1) {
                $this->newWidth   = $this->width;
                $this->newHeight  = $this->width / $this->aspectRatio;
                $this->log("Setting new width & height based on width & aspect ratio (>=1) to (w x h) {$this->newWidth} x {$this->newHeight}");

            } else {
                $this->newHeight  = $this->height;
                $this->newWidth   = $this->height * $this->aspectRatio;
                $this->log("Setting new width & height based on width & aspect ratio (<1) to (w x h) {$this->newWidth} x {$this->newHeight}");
            }

        } elseif ($this->aspectRatio && is_null($this->newWidth)) {
            $this->newWidth   = $this->newHeight * $this->aspectRatio;
            $this->log("Setting new width based on aspect ratio to {$this->newWidth}");

        } elseif ($this->aspectRatio && is_null($this->newHeight)) {
            $this->newHeight  = $this->newWidth / $this->aspectRatio;
            $this->log("Setting new height based on aspect ratio to {$this->newHeight}");
        }

        // Change width & height based on dpr
        if ($this->dpr != 1) {
            if (!is_null($this->newWidth)) {
                $this->newWidth  = round($this->newWidth * $this->dpr);
                $this->log("Setting new width based on dpr={$this->dpr} - w={$this->newWidth}");
            }
            if (!is_null($this->newHeight)) {
                $this->newHeight = round($this->newHeight * $this->dpr);
                $this->log("Setting new height based on dpr={$this->dpr} - h={$this->newHeight}");
            }
        }

        // Check values to be within domain
        is_null($this->newWidth)
            or is_numeric($this->newWidth)
            or $this->raiseError('Width not numeric');

        is_null($this->newHeight)
            or is_numeric($this->newHeight)
            or $this->raiseError('Height not numeric');

        $this->log("Init dimension (after) newWidth x newHeight is {$this->newWidth} x {$this->newHeight}.");

        return $this;
    }



    /**
     * Calculate new width and height of image, based on settings.
     *
     * @return $this
     */
    public function calculateNewWidthAndHeight()
    {
        // Crop, use cropped width and height as base for calulations
        $this->log("Calculate new width and height.");
        $this->log("Original width x height is {$this->width} x {$this->height}.");
        $this->log("Target dimension (before calculating) newWidth x newHeight is {$this->newWidth} x {$this->newHeight}.");

        // Check if there is an area to crop off
        if (isset($this->area)) {
            $this->offset['top']    = round($this->area['top'] / 100 * $this->height);
            $this->offset['right']  = round($this->area['right'] / 100 * $this->width);
            $this->offset['bottom'] = round($this->area['bottom'] / 100 * $this->height);
            $this->offset['left']   = round($this->area['left'] / 100 * $this->width);
            $this->offset['width']  = $this->width - $this->offset['left'] - $this->offset['right'];
            $this->offset['height'] = $this->height - $this->offset['top'] - $this->offset['bottom'];
            $this->width  = $this->offset['width'];
            $this->height = $this->offset['height'];
            $this->log("The offset for the area to use is top {$this->area['top']}%, right {$this->area['right']}%, bottom {$this->area['bottom']}%, left {$this->area['left']}%.");
            $this->log("The offset for the area to use is top {$this->offset['top']}px, right {$this->offset['right']}px, bottom {$this->offset['bottom']}px, left {$this->offset['left']}px, width {$this->offset['width']}px, height {$this->offset['height']}px.");
        }

        $width  = $this->width;
        $height = $this->height;

        // Check if crop is set
        if ($this->crop) {
            $width  = $this->crop['width']  = $this->crop['width'] <= 0 ? $this->width + $this->crop['width'] : $this->crop['width'];
            $height = $this->crop['height'] = $this->crop['height'] <= 0 ? $this->height + $this->crop['height'] : $this->crop['height'];

            if ($this->crop['start_x'] == 'left') {
                $this->crop['start_x'] = 0;
            } elseif ($this->crop['start_x'] == 'right') {
                $this->crop['start_x'] = $this->width - $width;
            } elseif ($this->crop['start_x'] == 'center') {
                $this->crop['start_x'] = round($this->width / 2) - round($width / 2);
            }

            if ($this->crop['start_y'] == 'top') {
                $this->crop['start_y'] = 0;
            } elseif ($this->crop['start_y'] == 'bottom') {
                $this->crop['start_y'] = $this->height - $height;
            } elseif ($this->crop['start_y'] == 'center') {
                $this->crop['start_y'] = round($this->height / 2) - round($height / 2);
            }

            $this->log("Crop area is width {$width}px, height {$height}px, start_x {$this->crop['start_x']}px, start_y {$this->crop['start_y']}px.");
        }

        // Calculate new width and height if keeping aspect-ratio.
        if ($this->keepRatio) {

            $this->log("Keep aspect ratio.");

            // Crop-to-fit and both new width and height are set.
            if (($this->cropToFit || $this->fillToFit) && isset($this->newWidth) && isset($this->newHeight)) {

                // Use newWidth and newHeigh as width/height, image should fit in box.
                $this->log("Use newWidth and newHeigh as width/height, image should fit in box.");

            } elseif (isset($this->newWidth) && isset($this->newHeight)) {

                // Both new width and height are set.
                // Use newWidth and newHeigh as max width/height, image should not be larger.
                $ratioWidth  = $width  / $this->newWidth;
                $ratioHeight = $height / $this->newHeight;
                $ratio = ($ratioWidth > $ratioHeight) ? $ratioWidth : $ratioHeight;
                $this->newWidth  = round($width  / $ratio);
                $this->newHeight = round($height / $ratio);
                $this->log("New width and height was set.");

            } elseif (isset($this->newWidth)) {

                // Use new width as max-width
                $factor = (float)$this->newWidth / (float)$width;
                $this->newHeight = round($factor * $height);
                $this->log("New width was set.");

            } elseif (isset($this->newHeight)) {

                // Use new height as max-hight
                $factor = (float)$this->newHeight / (float)$height;
                $this->newWidth = round($factor * $width);
                $this->log("New height was set.");

            } else {

                // Use existing width and height as new width and height.
                $this->newWidth = $width;
                $this->newHeight = $height;
            }


            // Get image dimensions for pre-resize image.
            if ($this->cropToFit || $this->fillToFit) {

                // Get relations of original & target image
                $ratioWidth  = $width  / $this->newWidth;
                $ratioHeight = $height / $this->newHeight;

                if ($this->cropToFit) {

                    // Use newWidth and newHeigh as defined width/height,
                    // image should fit the area.
                    $this->log("Crop to fit.");
                    $ratio = ($ratioWidth < $ratioHeight) ? $ratioWidth : $ratioHeight;
                    $this->cropWidth  = round($width  / $ratio);
                    $this->cropHeight = round($height / $ratio);
                    $this->log("Crop width, height, ratio: $this->cropWidth x $this->cropHeight ($ratio).");

                } elseif ($this->fillToFit) {

                    // Use newWidth and newHeigh as defined width/height,
                    // image should fit the area.
                    $this->log("Fill to fit.");
                    $ratio = ($ratioWidth < $ratioHeight) ? $ratioHeight : $ratioWidth;
                    $this->fillWidth  = round($width  / $ratio);
                    $this->fillHeight = round($height / $ratio);
                    $this->log("Fill width, height, ratio: $this->fillWidth x $this->fillHeight ($ratio).");
                }
            }
        }

        // Crop, ensure to set new width and height
        if ($this->crop) {
            $this->log("Crop.");
            $this->newWidth = round(isset($this->newWidth) ? $this->newWidth : $this->crop['width']);
            $this->newHeight = round(isset($this->newHeight) ? $this->newHeight : $this->crop['height']);
        }

        // Fill to fit, ensure to set new width and height
        /*if ($this->fillToFit) {
            $this->log("FillToFit.");
            $this->newWidth = round(isset($this->newWidth) ? $this->newWidth : $this->crop['width']);
            $this->newHeight = round(isset($this->newHeight) ? $this->newHeight : $this->crop['height']);
        }*/

        // No new height or width is set, use existing measures.
        $this->newWidth  = round(isset($this->newWidth) ? $this->newWidth : $this->width);
        $this->newHeight = round(isset($this->newHeight) ? $this->newHeight : $this->height);
        $this->log("Calculated new width x height as {$this->newWidth} x {$this->newHeight}.");

        return $this;
    }



    /**
     * Re-calculate image dimensions when original image dimension has changed.
     *
     * @return $this
     */
    public function reCalculateDimensions()
    {
        $this->log("Re-calculate image dimensions, newWidth x newHeigh was: " . $this->newWidth . " x " . $this->newHeight);

        $this->newWidth  = $this->newWidthOrig;
        $this->newHeight = $this->newHeightOrig;
        $this->crop      = $this->cropOrig;

        $this->initDimensions()
             ->calculateNewWidthAndHeight();

        return $this;
    }



    /**
     * Set extension for filename to save as.
     *
     * @param string $saveas extension to save image as
     *
     * @return $this
     */
    public function setSaveAsExtension($saveAs = null)
    {
        if (isset($saveAs)) {
            $saveAs = strtolower($saveAs);
            $this->checkFileExtension($saveAs);
            $this->saveAs = $saveAs;
            $this->extension = $saveAs;
        }

        $this->log("Prepare to save image as: " . $this->extension);

        return $this;
    }



    /**
     * Set JPEG quality to use when saving image
     *
     * @param int $quality as the quality to set.
     *
     * @return $this
     */
    public function setJpegQuality($quality = null)
    {
        if ($quality) {
            $this->useQuality = true;
        }

        $this->quality = isset($quality)
            ? $quality
            : self::JPEG_QUALITY_DEFAULT;

        (is_numeric($this->quality) and $this->quality > 0 and $this->quality <= 100)
            or $this->raiseError('Quality not in range.');

        $this->log("Setting JPEG quality to {$this->quality}.");

        return $this;
    }



    /**
     * Set PNG compressen algorithm to use when saving image
     *
     * @param int $compress as the algorithm to use.
     *
     * @return $this
     */
    public function setPngCompression($compress = null)
    {
        if ($compress) {
            $this->useCompress = true;
        }

        $this->compress = isset($compress)
            ? $compress
            : self::PNG_COMPRESSION_DEFAULT;

        (is_numeric($this->compress) and $this->compress >= -1 and $this->compress <= 9)
            or $this->raiseError('Quality not in range.');

        $this->log("Setting PNG compression level to {$this->compress}.");

        return $this;
    }



    /**
     * Use original image if possible, check options which affects image processing.
     *
     * @param boolean $useOrig default is to use original if possible, else set to false.
     *
     * @return $this
     */
    public function useOriginalIfPossible($useOrig = true)
    {
        if ($useOrig
            && ($this->newWidth == $this->width)
            && ($this->newHeight == $this->height)
            && !$this->area
            && !$this->crop
            && !$this->cropToFit
            && !$this->fillToFit
            && !$this->filters
            && !$this->sharpen
            && !$this->emboss
            && !$this->blur
            && !$this->convolve
            && !$this->palette
            && !$this->useQuality
            && !$this->useCompress
            && !$this->saveAs
            && !$this->rotateBefore
            && !$this->rotateAfter
            && !$this->autoRotate
            && !$this->bgColor
            && ($this->upscale === self::UPSCALE_DEFAULT)
            && !$this->lossy
        ) {
            $this->log("Using original image.");
            $this->output($this->pathToImage);
        }

        return $this;
    }



    /**
     * Generate filename to save file in cache.
     *
     * @param string  $base      as optional basepath for storing file.
     * @param boolean $useSubdir use or skip the subdir part when creating the
     *                           filename.
     * @param string  $prefix    to add as part of filename
     *
     * @return $this
     */
    public function generateFilename($base = null, $useSubdir = true, $prefix = null)
    {
        $filename     = basename($this->pathToImage);
        $cropToFit    = $this->cropToFit    ? '_cf'                      : null;
        $fillToFit    = $this->fillToFit    ? '_ff'                      : null;
        $crop_x       = $this->crop_x       ? "_x{$this->crop_x}"        : null;
        $crop_y       = $this->crop_y       ? "_y{$this->crop_y}"        : null;
        $scale        = $this->scale        ? "_s{$this->scale}"         : null;
        $bgColor      = $this->bgColor      ? "_bgc{$this->bgColor}"     : null;
        $quality      = $this->quality      ? "_q{$this->quality}"       : null;
        $compress     = $this->compress     ? "_co{$this->compress}"     : null;
        $rotateBefore = $this->rotateBefore ? "_rb{$this->rotateBefore}" : null;
        $rotateAfter  = $this->rotateAfter  ? "_ra{$this->rotateAfter}"  : null;
        $lossy        = $this->lossy        ? "_l"                       : null;
        $interlace    = $this->interlace    ? "_i"                       : null;

        $saveAs = $this->normalizeFileExtension();
        $saveAs = $saveAs ? "_$saveAs" : null;

        $copyStrat = null;
        if ($this->copyStrategy === self::RESIZE) {
            $copyStrat = "_rs";
        }

        $width  = $this->newWidth  ? '_' . $this->newWidth  : null;
        $height = $this->newHeight ? '_' . $this->newHeight : null;

        $offset = isset($this->offset)
            ? '_o' . $this->offset['top'] . '-' . $this->offset['right'] . '-' . $this->offset['bottom'] . '-' . $this->offset['left']
            : null;

        $crop = $this->crop
            ? '_c' . $this->crop['width'] . '-' . $this->crop['height'] . '-' . $this->crop['start_x'] . '-' . $this->crop['start_y']
            : null;

        $filters = null;
        if (isset($this->filters)) {
            foreach ($this->filters as $filter) {
                if (is_array($filter)) {
                    $filters .= "_f{$filter['id']}";
                    for ($i=1; $i<=$filter['argc']; $i++) {
                        $filters .= "-".$filter["arg{$i}"];
                    }
                }
            }
        }

        $sharpen = $this->sharpen ? 's' : null;
        $emboss  = $this->emboss  ? 'e' : null;
        $blur    = $this->blur    ? 'b' : null;
        $palette = $this->palette ? 'p' : null;

        $autoRotate = $this->autoRotate ? 'ar' : null;

        $optimize  = $this->jpegOptimize ? 'o' : null;
        $optimize .= $this->pngFilter    ? 'f' : null;
        $optimize .= $this->pngDeflate   ? 'd' : null;

        $convolve = null;
        if ($this->convolve) {
            $convolve = '_conv' . preg_replace('/[^a-zA-Z0-9]/', '', $this->convolve);
        }

        $upscale = null;
        if ($this->upscale !== self::UPSCALE_DEFAULT) {
            $upscale = '_nu';
        }

        $subdir = null;
        if ($useSubdir === true) {
            $subdir = str_replace('/', '-', dirname($this->imageSrc));
            $subdir = ($subdir == '.') ? '_.' : $subdir;
            $subdir .= '_';
        }

        $file = $prefix . $subdir . $filename . $width . $height
            . $offset . $crop . $cropToFit . $fillToFit
            . $crop_x . $crop_y . $upscale
            . $quality . $filters . $sharpen . $emboss . $blur . $palette
            . $optimize . $compress
            . $scale . $rotateBefore . $rotateAfter . $autoRotate . $bgColor
            . $convolve . $copyStrat . $lossy . $interlace . $saveAs;

        return $this->setTarget($file, $base);
    }



    /**
     * Use cached version of image, if possible.
     *
     * @param boolean $useCache is default true, set to false to avoid using cached object.
     *
     * @return $this
     */
    public function useCacheIfPossible($useCache = true)
    {
        if ($useCache && is_readable($this->cacheFileName)) {
            $fileTime   = filemtime($this->pathToImage);
            $cacheTime  = filemtime($this->cacheFileName);

            if ($fileTime <= $cacheTime) {
                if ($this->useCache) {
                    if ($this->verbose) {
                        $this->log("Use cached file.");
                        $this->log("Cached image filesize: " . filesize($this->cacheFileName) . " bytes.");
                    }
                    $this->output($this->cacheFileName, $this->outputFormat);
                } else {
                    $this->log("Cache is valid but ignoring it by intention.");
                }
            } else {
                $this->log("Original file is modified, ignoring cache.");
            }
        } else {
            $this->log("Cachefile does not exists or ignoring it.");
        }

        return $this;
    }



    /**
     * Load image from disk. Try to load image without verbose error message,
     * if fail, load again and display error messages.
     *
     * @param string $src of image.
     * @param string $dir as base directory where images are.
     *
     * @return $this
     *
     */
    public function load($src = null, $dir = null)
    {
        if (isset($src)) {
            $this->setSource($src, $dir);
        }

        $this->loadImageDetails();

        if ($this->fileType === IMG_WEBP) {
            $this->image = imagecreatefromwebp($this->pathToImage);
        } else {
            $imageAsString = file_get_contents($this->pathToImage);
            $this->image = imagecreatefromstring($imageAsString);
        }
        if ($this->image === false) {
            throw new Exception("Could not load image.");
        }

        /* Removed v0.7.7
        if (image_type_to_mime_type($this->fileType) == 'image/png') {
            $type = $this->getPngType();
            $hasFewColors = imagecolorstotal($this->image);

            if ($type == self::PNG_RGB_PALETTE || ($hasFewColors > 0 && $hasFewColors <= 256)) {
                if ($this->verbose) {
                    $this->log("Handle this image as a palette image.");
                }
                $this->palette = true;
            }
        }
        */

        if ($this->verbose) {
            $this->log("### Image successfully loaded from file.");
            $this->log(" imageistruecolor() : " . (imageistruecolor($this->image) ? 'true' : 'false'));
            $this->log(" imagecolorstotal() : " . imagecolorstotal($this->image));
            $this->log(" Number of colors in image = " . $this->colorsTotal($this->image));
            $index = imagecolortransparent($this->image);
            $this->log(" Detected transparent color = " . ($index >= 0 ? implode(", ", imagecolorsforindex($this->image, $index)) : "NONE") . " at index = $index");
        }

        return $this;
    }



    /**
     * Get the type of PNG image.
     *
     * @param string $filename to use instead of default.
     *
     * @return int as the type of the png-image
     *
     */
    public function getPngType($filename = null)
    {
        $filename = $filename ? $filename : $this->pathToImage;

        $pngType = ord(file_get_contents($filename, false, null, 25, 1));

        if ($this->verbose) {
            $this->log("Checking png type of: " . $filename);
            $this->log($this->getPngTypeAsString($pngType));
        }

        return $pngType;
    }



    /**
     * Get the type of PNG image as a verbose string.
     *
     * @param integer $type     to use, default is to check the type.
     * @param string  $filename to use instead of default.
     *
     * @return int as the type of the png-image
     *
     */
    private function getPngTypeAsString($pngType = null, $filename = null)
    {
        if ($filename || !$pngType) {
            $pngType = $this->getPngType($filename);
        }

        $index = imagecolortransparent($this->image);
        $transparent = null;
        if ($index != -1) {
            $transparent = " (transparent)";
        }

        switch ($pngType) {

            case self::PNG_GREYSCALE:
                $text = "PNG is type 0, Greyscale$transparent";
                break;

            case self::PNG_RGB:
                $text = "PNG is type 2, RGB$transparent";
                break;

            case self::PNG_RGB_PALETTE:
                $text = "PNG is type 3, RGB with palette$transparent";
                break;

            case self::PNG_GREYSCALE_ALPHA:
                $text = "PNG is type 4, Greyscale with alpha channel";
                break;

            case self::PNG_RGB_ALPHA:
                $text = "PNG is type 6, RGB with alpha channel (PNG 32-bit)";
                break;

            default:
                $text = "PNG is UNKNOWN type, is it really a PNG image?";
        }

        return $text;
    }




    /**
     * Calculate number of colors in an image.
     *
     * @param resource $im the image.
     *
     * @return int
     */
    private function colorsTotal($im)
    {
        if (imageistruecolor($im)) {
            $this->log("Colors as true color.");
            $h = imagesy($im);
            $w = imagesx($im);
            $c = array();
            for ($x=0; $x < $w; $x++) {
                for ($y=0; $y < $h; $y++) {
                    @$c['c'.imagecolorat($im, $x, $y)]++;
                }
            }
            return count($c);
        } else {
            $this->log("Colors as palette.");
            return imagecolorstotal($im);
        }
    }



    /**
     * Preprocess image before rezising it.
     *
     * @return $this
     */
    public function preResize()
    {
        $this->log("### Pre-process before resizing");

        // Rotate image
        if ($this->rotateBefore) {
            $this->log("Rotating image.");
            $this->rotate($this->rotateBefore, $this->bgColor)
                 ->reCalculateDimensions();
        }

        // Auto-rotate image
        if ($this->autoRotate) {
            $this->log("Auto rotating image.");
            $this->rotateExif()
                 ->reCalculateDimensions();
        }

        // Scale the original image before starting
        if (isset($this->scale)) {
            $this->log("Scale by {$this->scale}%");
            $newWidth  = $this->width * $this->scale / 100;
            $newHeight = $this->height * $this->scale / 100;
            $img = $this->CreateImageKeepTransparency($newWidth, $newHeight);
            imagecopyresampled($img, $this->image, 0, 0, 0, 0, $newWidth, $newHeight, $this->width, $this->height);
            $this->image = $img;
            $this->width = $newWidth;
            $this->height = $newHeight;
        }

        return $this;
    }



    /**
     * Resize or resample the image while resizing.
     *
     * @param int $strategy as CImage::RESIZE or CImage::RESAMPLE
     *
     * @return $this
     */
     public function setCopyResizeStrategy($strategy)
     {
         $this->copyStrategy = $strategy;
         return $this;
     }



    /**
     * Resize and or crop the image.
     *
     * @return void
     */
    public function imageCopyResampled($dst_image, $src_image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h)
    {
        if($this->copyStrategy == self::RESIZE) {
            $this->log("Copy by resize");
            imagecopyresized($dst_image, $src_image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h);
        } else {
            $this->log("Copy by resample");
            imagecopyresampled($dst_image, $src_image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h);
        }
    }



    /**
     * Resize and or crop the image.
     *
     * @return $this
     */
    public function resize()
    {

        $this->log("### Starting to Resize()");
        $this->log("Upscale = '$this->upscale'");

        // Only use a specified area of the image, $this->offset is defining the area to use
        if (isset($this->offset)) {

            $this->log("Offset for area to use, cropping it width={$this->offset['width']}, height={$this->offset['height']}, start_x={$this->offset['left']}, start_y={$this->offset['top']}");
            $img = $this->CreateImageKeepTransparency($this->offset['width'], $this->offset['height']);
            imagecopy($img, $this->image, 0, 0, $this->offset['left'], $this->offset['top'], $this->offset['width'], $this->offset['height']);
            $this->image = $img;
            $this->width = $this->offset['width'];
            $this->height = $this->offset['height'];
        }

        if ($this->crop) {

            // Do as crop, take only part of image
            $this->log("Cropping area width={$this->crop['width']}, height={$this->crop['height']}, start_x={$this->crop['start_x']}, start_y={$this->crop['start_y']}");
            $img = $this->CreateImageKeepTransparency($this->crop['width'], $this->crop['height']);
            imagecopy($img, $this->image, 0, 0, $this->crop['start_x'], $this->crop['start_y'], $this->crop['width'], $this->crop['height']);
            $this->image = $img;
            $this->width = $this->crop['width'];
            $this->height = $this->crop['height'];
        }

        if (!$this->upscale) {
            // Consider rewriting the no-upscale code to fit within this if-statement,
            // likely to be more readable code.
            // The code is more or leass equal in below crop-to-fit, fill-to-fit and stretch
        }

        if ($this->cropToFit) {

            // Resize by crop to fit
            $this->log("Resizing using strategy - Crop to fit");

            if (!$this->upscale
                && ($this->width < $this->newWidth || $this->height < $this->newHeight)) {
                $this->log("Resizing - smaller image, do not upscale.");

                $posX = 0;
                $posY = 0;
                $cropX = 0;
                $cropY = 0;

                if ($this->newWidth > $this->width) {
                    $posX = round(($this->newWidth - $this->width) / 2);
                }
                if ($this->newWidth < $this->width) {
                    $cropX = round(($this->width/2) - ($this->newWidth/2));
                }

                if ($this->newHeight > $this->height) {
                    $posY = round(($this->newHeight - $this->height) / 2);
                }
                if ($this->newHeight < $this->height) {
                    $cropY = round(($this->height/2) - ($this->newHeight/2));
                }
                $this->log(" cwidth: $this->cropWidth");
                $this->log(" cheight: $this->cropHeight");
                $this->log(" nwidth: $this->newWidth");
                $this->log(" nheight: $this->newHeight");
                $this->log(" width: $this->width");
                $this->log(" height: $this->height");
                $this->log(" posX: $posX");
                $this->log(" posY: $posY");
                $this->log(" cropX: $cropX");
                $this->log(" cropY: $cropY");

                $imageResized = $this->CreateImageKeepTransparency($this->newWidth, $this->newHeight);
                imagecopy($imageResized, $this->image, $posX, $posY, $cropX, $cropY, $this->width, $this->height);
            } else {
                $cropX = round(($this->cropWidth/2) - ($this->newWidth/2));
                $cropY = round(($this->cropHeight/2) - ($this->newHeight/2));
                $imgPreCrop   = $this->CreateImageKeepTransparency($this->cropWidth, $this->cropHeight);
                $imageResized = $this->CreateImageKeepTransparency($this->newWidth, $this->newHeight);
                $this->imageCopyResampled($imgPreCrop, $this->image, 0, 0, 0, 0, $this->cropWidth, $this->cropHeight, $this->width, $this->height);
                imagecopy($imageResized, $imgPreCrop, 0, 0, $cropX, $cropY, $this->newWidth, $this->newHeight);
            }

            $this->image = $imageResized;
            $this->width = $this->newWidth;
            $this->height = $this->newHeight;

        } elseif ($this->fillToFit) {

            // Resize by fill to fit
            $this->log("Resizing using strategy - Fill to fit");

            $posX = 0;
            $posY = 0;

            $ratioOrig = $this->width / $this->height;
            $ratioNew  = $this->newWidth / $this->newHeight;

            // Check ratio for landscape or portrait
            if ($ratioOrig < $ratioNew) {
                $posX = round(($this->newWidth - $this->fillWidth) / 2);
            } else {
                $posY = round(($this->newHeight - $this->fillHeight) / 2);
            }

            if (!$this->upscale
                && ($this->width < $this->newWidth && $this->height < $this->newHeight)
            ) {

                $this->log("Resizing - smaller image, do not upscale.");
                $posX = round(($this->newWidth - $this->width) / 2);
                $posY = round(($this->newHeight - $this->height) / 2);
                $imageResized = $this->CreateImageKeepTransparency($this->newWidth, $this->newHeight);
                imagecopy($imageResized, $this->image, $posX, $posY, 0, 0, $this->width, $this->height);

            } else {
                $imgPreFill   = $this->CreateImageKeepTransparency($this->fillWidth, $this->fillHeight);
                $imageResized = $this->CreateImageKeepTransparency($this->newWidth, $this->newHeight);
                $this->imageCopyResampled($imgPreFill, $this->image, 0, 0, 0, 0, $this->fillWidth, $this->fillHeight, $this->width, $this->height);
                imagecopy($imageResized, $imgPreFill, $posX, $posY, 0, 0, $this->fillWidth, $this->fillHeight);
            }

            $this->image = $imageResized;
            $this->width = $this->newWidth;
            $this->height = $this->newHeight;

        } elseif (!($this->newWidth == $this->width && $this->newHeight == $this->height)) {

            // Resize it
            $this->log("Resizing, new height and/or width");

            if (!$this->upscale
                && ($this->width < $this->newWidth || $this->height < $this->newHeight)
            ) {
                $this->log("Resizing - smaller image, do not upscale.");

                if (!$this->keepRatio) {
                    $this->log("Resizing - stretch to fit selected.");

                    $posX = 0;
                    $posY = 0;
                    $cropX = 0;
                    $cropY = 0;

                    if ($this->newWidth > $this->width && $this->newHeight > $this->height) {
                        $posX = round(($this->newWidth - $this->width) / 2);
                        $posY = round(($this->newHeight - $this->height) / 2);
                    } elseif ($this->newWidth > $this->width) {
                        $posX = round(($this->newWidth - $this->width) / 2);
                        $cropY = round(($this->height - $this->newHeight) / 2);
                    } elseif ($this->newHeight > $this->height) {
                        $posY = round(($this->newHeight - $this->height) / 2);
                        $cropX = round(($this->width - $this->newWidth) / 2);
                    }

                    $imageResized = $this->CreateImageKeepTransparency($this->newWidth, $this->newHeight);
                    imagecopy($imageResized, $this->image, $posX, $posY, $cropX, $cropY, $this->width, $this->height);
                    $this->image = $imageResized;
                    $this->width = $this->newWidth;
                    $this->height = $this->newHeight;
                }
            } else {
                $imageResized = $this->CreateImageKeepTransparency($this->newWidth, $this->newHeight);
                $this->imageCopyResampled($imageResized, $this->image, 0, 0, 0, 0, $this->newWidth, $this->newHeight, $this->width, $this->height);
                $this->image = $imageResized;
                $this->width = $this->newWidth;
                $this->height = $this->newHeight;
            }
        }

        return $this;
    }



    /**
     * Postprocess image after rezising image.
     *
     * @return $this
     */
    public function postResize()
    {
        $this->log("### Post-process after resizing");

        // Rotate image
        if ($this->rotateAfter) {
            $this->log("Rotating image.");
            $this->rotate($this->rotateAfter, $this->bgColor);
        }

        // Apply filters
        if (isset($this->filters) && is_array($this->filters)) {

            foreach ($this->filters as $filter) {
                $this->log("Applying filter {$filter['type']}.");

                switch ($filter['argc']) {

                    case 0:
                        imagefilter($this->image, $filter['type']);
                        break;

                    case 1:
                        imagefilter($this->image, $filter['type'], $filter['arg1']);
                        break;

                    case 2:
                        imagefilter($this->image, $filter['type'], $filter['arg1'], $filter['arg2']);
                        break;

                    case 3:
                        imagefilter($this->image, $filter['type'], $filter['arg1'], $filter['arg2'], $filter['arg3']);
                        break;

                    case 4:
                        imagefilter($this->image, $filter['type'], $filter['arg1'], $filter['arg2'], $filter['arg3'], $filter['arg4']);
                        break;
                }
            }
        }

        // Convert to palette image
        if ($this->palette) {
            $this->log("Converting to palette image.");
            $this->trueColorToPalette();
        }

        // Blur the image
        if ($this->blur) {
            $this->log("Blur.");
            $this->blurImage();
        }

        // Emboss the image
        if ($this->emboss) {
            $this->log("Emboss.");
            $this->embossImage();
        }

        // Sharpen the image
        if ($this->sharpen) {
            $this->log("Sharpen.");
            $this->sharpenImage();
        }

        // Custom convolution
        if ($this->convolve) {
            //$this->log("Convolve: " . $this->convolve);
            $this->imageConvolution();
        }

        return $this;
    }



    /**
     * Rotate image using angle.
     *
     * @param float $angle        to rotate image.
     * @param int   $anglebgColor to fill image with if needed.
     *
     * @return $this
     */
    public function rotate($angle, $bgColor)
    {
        $this->log("Rotate image " . $angle . " degrees with filler color.");

        $color = $this->getBackgroundColor();
        $this->image = imagerotate($this->image, $angle, $color);

        $this->width  = imagesx($this->image);
        $this->height = imagesy($this->image);

        $this->log("New image dimension width x height: " . $this->width . " x " . $this->height);

        return $this;
    }



    /**
     * Rotate image using information in EXIF.
     *
     * @return $this
     */
    public function rotateExif()
    {
        if (!in_array($this->fileType, array(IMAGETYPE_JPEG, IMAGETYPE_TIFF_II, IMAGETYPE_TIFF_MM))) {
            $this->log("Autorotate ignored, EXIF not supported by this filetype.");
            return $this;
        }

        $exif = exif_read_data($this->pathToImage);

        if (!empty($exif['Orientation'])) {
            switch ($exif['Orientation']) {
                case 3:
                    $this->log("Autorotate 180.");
                    $this->rotate(180, $this->bgColor);
                    break;

                case 6:
                    $this->log("Autorotate -90.");
                    $this->rotate(-90, $this->bgColor);
                    break;

                case 8:
                    $this->log("Autorotate 90.");
                    $this->rotate(90, $this->bgColor);
                    break;

                default:
                    $this->log("Autorotate ignored, unknown value as orientation.");
            }
        } else {
            $this->log("Autorotate ignored, no orientation in EXIF.");
        }

        return $this;
    }



    /**
     * Convert true color image to palette image, keeping alpha.
     * http://stackoverflow.com/questions/5752514/how-to-convert-png-to-8-bit-png-using-php-gd-library
     *
     * @return void
     */
    public function trueColorToPalette()
    {
        $img = imagecreatetruecolor($this->width, $this->height);
        $bga = imagecolorallocatealpha($img, 0, 0, 0, 127);
        imagecolortransparent($img, $bga);
        imagefill($img, 0, 0, $bga);
        imagecopy($img, $this->image, 0, 0, 0, 0, $this->width, $this->height);
        imagetruecolortopalette($img, false, 255);
        imagesavealpha($img, true);

        if (imageistruecolor($this->image)) {
            $this->log("Matching colors with true color image.");
            imagecolormatch($this->image, $img);
        }

        $this->image = $img;
    }



    /**
     * Sharpen image using image convolution.
     *
     * @return $this
     */
    public function sharpenImage()
    {
        $this->imageConvolution('sharpen');
        return $this;
    }



    /**
     * Emboss image using image convolution.
     *
     * @return $this
     */
    public function embossImage()
    {
        $this->imageConvolution('emboss');
        return $this;
    }



    /**
     * Blur image using image convolution.
     *
     * @return $this
     */
    public function blurImage()
    {
        $this->imageConvolution('blur');
        return $this;
    }



    /**
     * Create convolve expression and return arguments for image convolution.
     *
     * @param string $expression constant string which evaluates to a list of
     *                           11 numbers separated by komma or such a list.
     *
     * @return array as $matrix (3x3), $divisor and $offset
     */
    public function createConvolveArguments($expression)
    {
        // Check of matching constant
        if (isset($this->convolves[$expression])) {
            $expression = $this->convolves[$expression];
        }

        $part = explode(',', $expression);
        $this->log("Creating convolution expressen: $expression");

        // Expect list of 11 numbers, split by , and build up arguments
        if (count($part) != 11) {
            throw new Exception(
                "Missmatch in argument convolve. Expected comma-separated string with
                11 float values. Got $expression."
            );
        }

        array_walk($part, function ($item, $key) {
            if (!is_numeric($item)) {
                throw new Exception("Argument to convolve expression should be float but is not.");
            }
        });

        return array(
            array(
                array($part[0], $part[1], $part[2]),
                array($part[3], $part[4], $part[5]),
                array($part[6], $part[7], $part[8]),
            ),
            $part[9],
            $part[10],
        );
    }



    /**
     * Add custom expressions (or overwrite existing) for image convolution.
     *
     * @param array $options Key value array with strings to be converted
     *                       to convolution expressions.
     *
     * @return $this
     */
    public function addConvolveExpressions($options)
    {
        $this->convolves = array_merge($this->convolves, $options);
        return $this;
    }



    /**
     * Image convolution.
     *
     * @param string $options A string with 11 float separated by comma.
     *
     * @return $this
     */
    public function imageConvolution($options = null)
    {
        // Use incoming options or use $this.
        $options = $options ? $options : $this->convolve;

        // Treat incoming as string, split by +
        $this->log("Convolution with '$options'");
        $options = explode(":", $options);

        // Check each option if it matches constant value
        foreach ($options as $option) {
            list($matrix, $divisor, $offset) = $this->createConvolveArguments($option);
            imageconvolution($this->image, $matrix, $divisor, $offset);
        }

        return $this;
    }



    /**
     * Set default background color between 000000-FFFFFF or if using
     * alpha 00000000-FFFFFF7F.
     *
     * @param string $color as hex value.
     *
     * @return $this
    */
    public function setDefaultBackgroundColor($color)
    {
        $this->log("Setting default background color to '$color'.");

        if (!(strlen($color) == 6 || strlen($color) == 8)) {
            throw new Exception(
                "Background color needs a hex value of 6 or 8
                digits. 000000-FFFFFF or 00000000-FFFFFF7F.
                Current value was: '$color'."
            );
        }

        $red    = hexdec(substr($color, 0, 2));
        $green  = hexdec(substr($color, 2, 2));
        $blue   = hexdec(substr($color, 4, 2));

        $alpha = (strlen($color) == 8)
            ? hexdec(substr($color, 6, 2))
            : null;

        if (($red < 0 || $red > 255)
            || ($green < 0 || $green > 255)
            || ($blue < 0 || $blue > 255)
            || ($alpha < 0 || $alpha > 127)
        ) {
            throw new Exception(
                "Background color out of range. Red, green blue
                should be 00-FF and alpha should be 00-7F.
                Current value was: '$color'."
            );
        }

        $this->bgColor = strtolower($color);
        $this->bgColorDefault = array(
            'red'   => $red,
            'green' => $green,
            'blue'  => $blue,
            'alpha' => $alpha
        );

        return $this;
    }



    /**
     * Get the background color.
     *
     * @param resource $img the image to work with or null if using $this->image.
     *
     * @return color value or null if no background color is set.
    */
    private function getBackgroundColor($img = null)
    {
        $img = isset($img) ? $img : $this->image;

        if ($this->bgColorDefault) {

            $red   = $this->bgColorDefault['red'];
            $green = $this->bgColorDefault['green'];
            $blue  = $this->bgColorDefault['blue'];
            $alpha = $this->bgColorDefault['alpha'];

            if ($alpha) {
                $color = imagecolorallocatealpha($img, $red, $green, $blue, $alpha);
            } else {
                $color = imagecolorallocate($img, $red, $green, $blue);
            }

            return $color;

        } else {
            return 0;
        }
    }



    /**
     * Create a image and keep transparency for png and gifs.
     *
     * @param int $width of the new image.
     * @param int $height of the new image.
     *
     * @return image resource.
    */
    private function createImageKeepTransparency($width, $height)
    {
        $this->log("Creating a new working image width={$width}px, height={$height}px.");
        $img = imagecreatetruecolor($width, $height);
        imagealphablending($img, false);
        imagesavealpha($img, true);

        $index = $this->image
            ? imagecolortransparent($this->image)
            : -1;

        if ($index != -1) {

            imagealphablending($img, true);
            $transparent = imagecolorsforindex($this->image, $index);
            $color = imagecolorallocatealpha($img, $transparent['red'], $transparent['green'], $transparent['blue'], $transparent['alpha']);
            imagefill($img, 0, 0, $color);
            $index = imagecolortransparent($img, $color);
            $this->Log("Detected transparent color = " . implode(", ", $transparent) . " at index = $index");

        } elseif ($this->bgColorDefault) {

            $color = $this->getBackgroundColor($img);
            imagefill($img, 0, 0, $color);
            $this->Log("Filling image with background color.");
        }

        return $img;
    }



    /**
     * Set optimizing  and post-processing options.
     *
     * @param array $options with config for postprocessing with external tools.
     *
     * @return $this
     */
    public function setPostProcessingOptions($options)
    {
        if (isset($options['jpeg_optimize']) && $options['jpeg_optimize']) {
            $this->jpegOptimizeCmd = $options['jpeg_optimize_cmd'];
        } else {
            $this->jpegOptimizeCmd = null;
        }

        if (array_key_exists("png_lossy", $options)
            && $options['png_lossy'] !== false) {
            $this->pngLossy = $options['png_lossy'];
            $this->pngLossyCmd = $options['png_lossy_cmd'];
        } else {
            $this->pngLossyCmd = null;
        }

        if (isset($options['png_filter']) && $options['png_filter']) {
            $this->pngFilterCmd = $options['png_filter_cmd'];
        } else {
            $this->pngFilterCmd = null;
        }

        if (isset($options['png_deflate']) && $options['png_deflate']) {
            $this->pngDeflateCmd = $options['png_deflate_cmd'];
        } else {
            $this->pngDeflateCmd = null;
        }

        return $this;
    }



    /**
     * Find out the type (file extension) for the image to be saved.
     *
     * @return string as image extension.
     */
    protected function getTargetImageExtension()
    {
        // switch on mimetype
        if (isset($this->extension)) {
            return strtolower($this->extension);
        } elseif ($this->fileType === IMG_WEBP) {
            return "webp";
        }

        return substr(image_type_to_extension($this->fileType), 1);
    }



    /**
     * Save image.
     *
     * @param string  $src       as target filename.
     * @param string  $base      as base directory where to store images.
     * @param boolean $overwrite or not, default to always overwrite file.
     *
     * @return $this or false if no folder is set.
     */
    public function save($src = null, $base = null, $overwrite = true)
    {
        if (isset($src)) {
            $this->setTarget($src, $base);
        }

        if ($overwrite === false && is_file($this->cacheFileName)) {
            $this->Log("Not overwriting file since its already exists and \$overwrite if false.");
            return;
        }

        if (!defined("WINDOWS2WSL")) {
            is_writable($this->saveFolder)
            or $this->raiseError('Target directory is not writable.');
        }

        $type = $this->getTargetImageExtension();
        $this->Log("Saving image as " . $type);
        switch($type) {

            case 'jpeg':
            case 'jpg':
                // Set as interlaced progressive JPEG
                if ($this->interlace) {
                    $this->Log("Set JPEG image to be interlaced.");
                    $res = imageinterlace($this->image, true);
                }

                $this->Log("Saving image as JPEG to cache using quality = {$this->quality}.");
                imagejpeg($this->image, $this->cacheFileName, $this->quality);

                // Use JPEG optimize if defined
                if ($this->jpegOptimizeCmd) {
                    if ($this->verbose) {
                        clearstatcache();
                        $this->log("Filesize before optimize: " . filesize($this->cacheFileName) . " bytes.");
                    }
                    $res = array();
                    $cmd = $this->jpegOptimizeCmd . " -outfile $this->cacheFileName $this->cacheFileName";
                    exec($cmd, $res);
                    $this->log($cmd);
                    $this->log($res);
                }
                break;

            case 'gif':
                $this->Log("Saving image as GIF to cache.");
                imagegif($this->image, $this->cacheFileName);
                break;

            case 'webp':
                $this->Log("Saving image as WEBP to cache using quality = {$this->quality}.");
                imagewebp($this->image, $this->cacheFileName, $this->quality);
                break;

            case 'png':
            default:
                $this->Log("Saving image as PNG to cache using compression = {$this->compress}.");

                // Turn off alpha blending and set alpha flag
                imagealphablending($this->image, false);
                imagesavealpha($this->image, true);
                imagepng($this->image, $this->cacheFileName, $this->compress);

                // Use external program to process lossy PNG, if defined
                $lossyEnabled = $this->pngLossy === true;
                $lossySoftEnabled = $this->pngLossy === null;
                $lossyActiveEnabled = $this->lossy === true;
                if ($lossyEnabled || ($lossySoftEnabled && $lossyActiveEnabled)) {
                    if ($this->verbose) {
                        clearstatcache();
                        $this->log("Lossy enabled: $lossyEnabled");
                        $this->log("Lossy soft enabled: $lossySoftEnabled");
                        $this->Log("Filesize before lossy optimize: " . filesize($this->cacheFileName) . " bytes.");
                    }
                    $res = array();
                    $cmd = $this->pngLossyCmd . " $this->cacheFileName $this->cacheFileName";
                    exec($cmd, $res);
                    $this->Log($cmd);
                    $this->Log($res);
                }

                // Use external program to filter PNG, if defined
                if ($this->pngFilterCmd) {
                    if ($this->verbose) {
                        clearstatcache();
                        $this->Log("Filesize before filter optimize: " . filesize($this->cacheFileName) . " bytes.");
                    }
                    $res = array();
                    $cmd = $this->pngFilterCmd . " $this->cacheFileName";
                    exec($cmd, $res);
                    $this->Log($cmd);
                    $this->Log($res);
                }

                // Use external program to deflate PNG, if defined
                if ($this->pngDeflateCmd) {
                    if ($this->verbose) {
                        clearstatcache();
                        $this->Log("Filesize before deflate optimize: " . filesize($this->cacheFileName) . " bytes.");
                    }
                    $res = array();
                    $cmd = $this->pngDeflateCmd . " $this->cacheFileName";
                    exec($cmd, $res);
                    $this->Log($cmd);
                    $this->Log($res);
                }
                break;
        }

        if ($this->verbose) {
            clearstatcache();
            $this->log("Saved image to cache.");
            $this->log(" Cached image filesize: " . filesize($this->cacheFileName) . " bytes.");
            $this->log(" imageistruecolor() : " . (imageistruecolor($this->image) ? 'true' : 'false'));
            $this->log(" imagecolorstotal() : " . imagecolorstotal($this->image));
            $this->log(" Number of colors in image = " . $this->ColorsTotal($this->image));
            $index = imagecolortransparent($this->image);
            $this->log(" Detected transparent color = " . ($index > 0 ? implode(", ", imagecolorsforindex($this->image, $index)) : "NONE") . " at index = $index");
        }

        return $this;
    }



    /**
     * Convert image from one colorpsace/color profile to sRGB without
     * color profile.
     *
     * @param string  $src      of image.
     * @param string  $dir      as base directory where images are.
     * @param string  $cache    as base directory where to store images.
     * @param string  $iccFile  filename of colorprofile.
     * @param boolean $useCache or not, default to always use cache.
     *
     * @return string | boolean false if no conversion else the converted
     *                          filename.
     */
    public function convert2sRGBColorSpace($src, $dir, $cache, $iccFile, $useCache = true)
    {
        if ($this->verbose) {
            $this->log("# Converting image to sRGB colorspace.");
        }

        if (!class_exists("Imagick")) {
            $this->log(" Ignoring since Imagemagick is not installed.");
            return false;
        }

        // Prepare
        $this->setSaveFolder($cache)
             ->setSource($src, $dir)
             ->generateFilename(null, false, 'srgb_');

        // Check if the cached version is accurate.
        if ($useCache && is_readable($this->cacheFileName)) {
            $fileTime  = filemtime($this->pathToImage);
            $cacheTime = filemtime($this->cacheFileName);

            if ($fileTime <= $cacheTime) {
                $this->log(" Using cached version: " . $this->cacheFileName);
                return $this->cacheFileName;
            }
        }

        // Only covert if cachedir is writable
        if (is_writable($this->saveFolder)) {
            // Load file and check if conversion is needed
            $image      = new Imagick($this->pathToImage);
            $colorspace = $image->getImageColorspace();
            $this->log(" Current colorspace: " . $colorspace);

            $profiles      = $image->getImageProfiles('*', false);
            $hasICCProfile = (array_search('icc', $profiles) !== false);
            $this->log(" Has ICC color profile: " . ($hasICCProfile ? "YES" : "NO"));

            if ($colorspace != Imagick::COLORSPACE_SRGB || $hasICCProfile) {
                $this->log(" Converting to sRGB.");

                $sRGBicc = file_get_contents($iccFile);
                $image->profileImage('icc', $sRGBicc);

                $image->transformImageColorspace(Imagick::COLORSPACE_SRGB);
                $image->writeImage($this->cacheFileName);
                return $this->cacheFileName;
            }
        }

        return false;
    }



    /**
     * Create a hard link, as an alias, to the cached file.
     *
     * @param string $alias where to store the link,
     *                      filename without extension.
     *
     * @return $this
     */
    public function linkToCacheFile($alias)
    {
        if ($alias === null) {
            $this->log("Ignore creating alias.");
            return $this;
        }

        if (is_readable($alias)) {
            unlink($alias);
        }

        $res = link($this->cacheFileName, $alias);

        if ($res) {
            $this->log("Created an alias as: $alias");
        } else {
            $this->log("Failed to create the alias: $alias");
        }

        return $this;
    }



    /**
     * Add HTTP header for output together with image.
     *
     * @param string $type  the header type such as "Cache-Control"
     * @param string $value the value to use
     *
     * @return void
     */
    public function addHTTPHeader($type, $value)
    {
        $this->HTTPHeader[$type] = $value;
    }



    /**
     * Output image to browser using caching.
     *
     * @param string $file   to read and output, default is to
     *                       use $this->cacheFileName
     * @param string $format set to json to output file as json
     *                       object with details
     *
     * @return void
     */
    public function output($file = null, $format = null)
    {
        if (is_null($file)) {
            $file = $this->cacheFileName;
        }

        if (is_null($format)) {
            $format = $this->outputFormat;
        }

        $this->log("### Output");
        $this->log("Output format is: $format");

        if (!$this->verbose && $format == 'json') {
            header('Content-type: application/json');
            echo $this->json($file);
            exit;
        } elseif ($format == 'ascii') {
            header('Content-type: text/plain');
            echo $this->ascii($file);
            exit;
        }

        $this->log("Outputting image: $file");

        // Get image modification time
        clearstatcache();
        $lastModified = filemtime($file);
        $lastModifiedFormat = "D, d M Y H:i:s";
        $gmdate = gmdate($lastModifiedFormat, $lastModified);

        if (!$this->verbose) {
            $header = "Last-Modified: $gmdate GMT";
            header($header);
            $this->fastTrackCache->addHeader($header);
            $this->fastTrackCache->setLastModified($lastModified);
        }

        foreach ($this->HTTPHeader as $key => $val) {
            $header = "$key: $val";
            header($header);
            $this->fastTrackCache->addHeader($header);
        }

        if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])
            && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) == $lastModified) {

            if ($this->verbose) {
                $this->log("304 not modified");
                $this->verboseOutput();
                exit;
            }

            header("HTTP/1.0 304 Not Modified");
            if (CIMAGE_DEBUG) {
                trace(__CLASS__ . " 304");
            }

        } else {

            $this->loadImageDetails($file);
            $mime = $this->getMimeType();
            $size = filesize($file);

            if ($this->verbose) {
                $this->log("Last-Modified: " . $gmdate . " GMT");
                $this->log("Content-type: " . $mime);
                $this->log("Content-length: " . $size);
                $this->verboseOutput();

                if (is_null($this->verboseFileName)) {
                    exit;
                }
            }

            $header = "Content-type: $mime";
            header($header);
            $this->fastTrackCache->addHeaderOnOutput($header);

            $header = "Content-length: $size";
            header($header);
            $this->fastTrackCache->addHeaderOnOutput($header);

            $this->fastTrackCache->setSource($file);
            $this->fastTrackCache->writeToCache();
            if (CIMAGE_DEBUG) {
                trace(__CLASS__ . " 200");
            }
            readfile($file);
        }

        exit;
    }



    /**
     * Create a JSON object from the image details.
     *
     * @param string $file the file to output.
     *
     * @return string json-encoded representation of the image.
     */
    public function json($file = null)
    {
        $file = $file ? $file : $this->cacheFileName;

        $details = array();

        clearstatcache();

        $details['src']       = $this->imageSrc;
        $lastModified         = filemtime($this->pathToImage);
        $details['srcGmdate'] = gmdate("D, d M Y H:i:s", $lastModified);

        $details['cache']       = basename($this->cacheFileName ?? "");
        $lastModified           = filemtime($this->cacheFileName ?? "");
        $details['cacheGmdate'] = gmdate("D, d M Y H:i:s", $lastModified);

        $this->load($file);

        $details['filename']    = basename($file ?? "");
        $details['mimeType']    = $this->getMimeType($this->fileType);
        $details['width']       = $this->width;
        $details['height']      = $this->height;
        $details['aspectRatio'] = round($this->width / $this->height, 3);
        $details['size']        = filesize($file ?? "");
        $details['colors'] = $this->colorsTotal($this->image);
        $details['includedFiles'] = count(get_included_files());
        $details['memoryPeek'] = round(memory_get_peak_usage()/1024/1024, 3) . " MB" ;
        $details['memoryCurrent'] = round(memory_get_usage()/1024/1024, 3) . " MB";
        $details['memoryLimit'] = ini_get('memory_limit');

        if (isset($_SERVER['REQUEST_TIME_FLOAT'])) {
            $details['loadTime'] = (string) round((microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']), 3) . "s";
        }

        if ($details['mimeType'] == 'image/png') {
            $details['pngType'] = $this->getPngTypeAsString(null, $file);
        }

        $options = null;
        if (defined("JSON_PRETTY_PRINT") && defined("JSON_UNESCAPED_SLASHES")) {
            $options = JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES;
        }

        return json_encode($details, $options);
    }



    /**
     * Set options for creating ascii version of image.
     *
     * @param array $options empty to use default or set options to change.
     *
     * @return void.
     */
    public function setAsciiOptions($options = array())
    {
        $this->asciiOptions = $options;
    }



    /**
     * Create an ASCII version from the image details.
     *
     * @param string $file the file to output.
     *
     * @return string ASCII representation of the image.
     */
    public function ascii($file = null)
    {
        $file = $file ? $file : $this->cacheFileName;

        $asciiArt = new CAsciiArt();
        $asciiArt->setOptions($this->asciiOptions);
        return $asciiArt->createFromFile($file);
    }



    /**
     * Log an event if verbose mode.
     *
     * @param string $message to log.
     *
     * @return this
     */
    public function log($message)
    {
        if ($this->verbose) {
            $this->log[] = $message;
        }

        return $this;
    }



    /**
     * Do verbose output to a file.
     *
     * @param string $fileName where to write the verbose output.
     *
     * @return void
     */
    public function setVerboseToFile($fileName)
    {
        $this->log("Setting verbose output to file.");
        $this->verboseFileName = $fileName;
    }



    /**
     * Do verbose output and print out the log and the actual images.
     *
     * @return void
     */
    private function verboseOutput()
    {
        $log = null;
        $this->log("### Summary of verbose log");
        $this->log("As JSON: \n" . $this->json());
        $this->log("Memory peak: " . round(memory_get_peak_usage() /1024/1024) . "M");
        $this->log("Memory limit: " . ini_get('memory_limit'));

        $included = get_included_files();
        $this->log("Included files: " . count($included));

        foreach ($this->log as $val) {
            if (is_array($val)) {
                foreach ($val as $val1) {
                    $log .= htmlentities($val1) . '<br/>';
                }
            } else {
                $log .= htmlentities($val) . '<br/>';
            }
        }

        if (!is_null($this->verboseFileName)) {
            file_put_contents(
                $this->verboseFileName,
                str_replace("<br/>", "\n", $log)
            );
        } else {
            echo <<<EOD
<h1>CImage Verbose Output</h1>
<pre>{$log}</pre>
EOD;
        }
    }



    /**
     * Raise error, enables to implement a selection of error methods.
     *
     * @param string $message the error message to display.
     *
     * @return void
     * @throws Exception
     */
    private function raiseError($message)
    {
        throw new Exception($message);
    }
}


================================================
FILE: CRemoteImage.php
================================================
<?php
/**
 * Get a image from a remote server using HTTP GET and If-Modified-Since.
 *
 */
class CRemoteImage
{
    /**
     * Path to cache files.
     */
    private $saveFolder = null;



    /**
     * Use cache or not.
     */
    private $useCache = true;



    /**
     * HTTP object to aid in download file.
     */
    private $http;



    /**
     * Status of the HTTP request.
     */
    private $status;



    /**
     * Defalt age for cached items 60*60*24*7.
     */
    private $defaultMaxAge = 604800;



    /**
     * Url of downloaded item.
     */
    private $url;



    /**
     * Base name of cache file for downloaded item and name of image.
     */
    private $fileName;



    /**
     * Filename for json-file with details of cached item.
     */
    private $fileJson;



    /**
     * Cache details loaded from file.
     */
    private $cache;



    /**
     * Get status of last HTTP request.
     *
     * @return int as status
     */
    public function getStatus()
    {
        return $this->status;
    }



    /**
     * Get JSON details for cache item.
     *
     * @return array with json details on cache.
     */
    public function getDetails()
    {
        return $this->cache;
    }



    /**
     * Set the path to the cache directory.
     *
     * @param boolean $use true to use the cache and false to ignore cache.
     *
     * @return $this
     */
    public function setCache($path)
    {
        $this->saveFolder = rtrim($path, "/") . "/";
        return $this;
    }



    /**
     * Check if cache is writable or throw exception.
     *
     * @return $this
     *
     * @throws Exception if cahce folder is not writable.
     */
    public function isCacheWritable()
    {
        if (!is_writable($this->saveFolder)) {
            throw new Exception("Cache folder is not writable for downloaded files.");
        }
        return $this;
    }



    /**
     * Decide if the cache should be used or not before trying to download
     * a remote file.
     *
     * @param boolean $use true to use the cache and false to ignore cache.
     *
     * @return $this
     */
    public function useCache($use = true)
    {
        $this->useCache = $use;
        return $this;
    }



    /**
     * Set header fields.
     *
     * @return $this
     */
    public function setHeaderFields()
    {
        $cimageVersion = "CImage";
        if (defined("CIMAGE_USER_AGENT")) {
            $cimageVersion = CIMAGE_USER_AGENT;
        }
        
        $this->http->setHeader("User-Agent", "$cimageVersion (PHP/". phpversion() . " cURL)");
        $this->http->setHeader("Accept", "image/jpeg,image/png,image/gif");

        if ($this->useCache) {
            $this->http->setHeader("Cache-Control", "max-age=0");
        } else {
            $this->http->setHeader("Cache-Control", "no-cache");
            $this->http->setHeader("Pragma", "no-cache");
        }
    }



    /**
     * Save downloaded resource to cache.
     *
     * @return string as path to saved file or false if not saved.
     */
    public function save()
    {
        $this->cache = array();
        $date         = $this->http->getDate(time());
        $maxAge       = $this->http->getMaxAge($this->defaultMaxAge);
        $lastModified = $this->http->getLastModified();
        $type         = $this->http->getContentType();

        $this->cache['Date']           = gmdate("D, d M Y H:i:s T", $date);
        $this->cache['Max-Age']        = $maxAge;
        $this->cache['Content-Type']   = $type;
        $this->cache['Url']            = $this->url;

        if ($lastModified) {
            $this->cache['Last-Modified'] = gmdate("D, d M Y H:i:s T", $lastModified);
        }

        // Save only if body is a valid image
        $body = $this->http->getBody();
        $img = imagecreatefromstring($body);

        if ($img !== false) {
            file_put_contents($this->fileName, $body);
            file_put_contents($this->fileJson, json_encode($this->cache));
            return $this->fileName;
        }

        return false;
    }



    /**
     * Got a 304 and updates cache with new age.
     *
     * @return string as path to cached file.
     */
    public function updateCacheDetails()
    {
        $date         = $this->http->getDate(time());
        $maxAge       = $this->http->getMaxAge($this->defaultMaxAge);
        $lastModified = $this->http->getLastModified();

        $this->cache['Date']    = gmdate("D, d M Y H:i:s T", $date);
        $this->cache['Max-Age'] = $maxAge;

        if ($lastModified) {
            $this->cache['Last-Modified'] = gmdate("D, d M Y H:i:s T", $lastModified);
        }

        file_put_contents($this->fileJson, json_encode($this->cache));
        return $this->fileName;
    }



    /**
     * Download a remote file and keep a cache of downloaded files.
     *
     * @param string $url a remote url.
     *
     * @throws Exception when status code does not match 200 or 304.
     *
     * @return string as path to downloaded file or false if failed.
     */
    public function download($url)
    {
        $this->http = new CHttpGet();
        $this->url = $url;

        // First check if the cache is valid and can be used
        $this->loadCacheDetails();

        if ($this->useCache) {
            $src = $this->getCachedSource();
            if ($src) {
                $this->status = 1;
                return $src;
            }
        }

        // Do a HTTP request to download item
        $this->setHeaderFields();
        $this->http->setUrl($this->url);
        $this->http->doGet();

        $this->status = $this->http->getStatus();
        if ($this->status === 200) {
            $this->isCacheWritable();
            return $this->save();
        } elseif ($this->status === 304) {
            $this->isCacheWritable();
            return $this->updateCacheDetails();
        }

        throw new Exception("Unknown statuscode when downloading remote image: " . $this->status);
    }



    /**
     * Get the path to the cached image file if the cache is valid.
     *
     * @return $this
     */
    public function loadCacheDetails()
    {
        $cacheFile = md5($this->url);
        $this->fileName = $this->saveFolder . $cacheFile;
        $this->fileJson = $this->fileName . ".json";
        if (is_readable($this->fileJson)) {
            $this->cache = json_decode(file_get_contents($this->fileJson), true);
        }
    }



    /**
     * Get the path to the cached image file if the cache is valid.
     *
     * @return string as the path ot the image file or false if no cache.
     */
    public function getCachedSource()
    {
        $imageExists = is_readable($this->fileName);

        // Is cache valid?
        $date   = strtotime($this->cache['Date']);
        $maxAge = $this->cache['Max-Age'];
        $now    = time();
        
        if ($imageExists && $date + $maxAge > $now) {
            return $this->fileName;
        }

        // Prepare for a 304 if available
        if ($imageExists && isset($this->cache['Last-Modified'])) {
            $this->http->setHeader("If-Modified-Since", $this->cache['Last-Modified']);
        }

        return false;
    }
}


================================================
FILE: CWhitelist.php
================================================
<?php
/**
 * Act as whitelist (or blacklist).
 *
 */
class CWhitelist
{
    /**
     * Array to contain the whitelist options.
     */
    private $whitelist = array();



    /**
     * Set the whitelist from an array of strings, each item in the
     * whitelist should be a regexp without the surrounding / or #.
     *
     * @param array $whitelist with all valid options,
     *                         default is to clear the whitelist.
     *
     * @return $this
     */
    public function set($whitelist = array())
    {
        if (!is_array($whitelist)) {
            throw new Exception("Whitelist is not of a supported format.");
        }

        $this->whitelist = $whitelist;
        return $this;
    }



    /**
     * Check if item exists in the whitelist.
     *
     * @param string $item      string to check.
     * @param array  $whitelist optional with all valid options, default is null.
     *
     * @return boolean true if item is in whitelist, else false.
     */
    public function check($item, $whitelist = null)
    {
        if ($whitelist !== null) {
            $this->set($whitelist);
        }
        
        if (empty($item) or empty($this->whitelist)) {
            return false;
        }
        
        foreach ($this->whitelist as $regexp) {
            if (preg_match("#$regexp#", $item)) {
                return true;
            }
        }

        return false;
    }
}


================================================
FILE: LICENSE.txt
================================================
The MIT License (MIT)

Copyright (c) 2012 - 2016 Mikael Roos, https://mikaelroos.se, mos@dbwebb.se

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.


================================================
FILE: README.md
================================================
Image conversion on the fly using PHP
=====================================

[![Join the chat at https://gitter.im/mosbth/cimage](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/mosbth/cimage?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
<!--
[![Build Status](https://travis-ci.org/mosbth/cimage.svg?branch=master)](https://travis-ci.org/mosbth/cimage)
[![Build Status](https://scrutinizer-ci.com/g/mosbth/cimage/badges/build.png?b=master)](https://scrutinizer-ci.com/g/mosbth/cimage/build-status/master)
-->

About
-------------------------------------

<img src="https://cimage.se/cimage/imgd.php?src=example/kodim07.png&w=200&c=140,140,520,340&sharpen"/>

`CImage` is a PHP class enabling resizing of images through scaling, cropping and filtering effects -- using PHP GD. The script `img.php` uses `CImage` to enable server-side image processing utilizing caching and optimization of the processed images.

Server-side image processing is a most useful tool for any web developer, `img.php` has an easy to use interface and its powerful when you integrate it with your website. Using it might decrease the time and effort for managing images and it might improve your workflow for creating content for websites.

This software is free and open source, licensed according MIT.



Documentation
--------------------------------------

Read full documentation at:
<strike>http://dbwebb.se/opensource/cimage</strike>

New website is being setup at [cimage.se](https://cimage.se), to improve documentation (work is ongoing).




Requirements
--------------------------------------

`CImage` and `img.php` supports GIF (with transparency), JPEG and PNG (8bit transparent, 24bit semi transparent) images. It requires PHP 5.3 and PHP GD. You optionally need the EXIF extension to support auto-rotation of JPEG-images. 

*Version v0.7.x will be the last version to support PHP 5.3. Coming version will require newer version of PHP.*



Installation
--------------------------------------

There are several ways of installing. You either install the whole project which uses the autoloader to include the various files, or you install the all-included bundle that -- for convenience -- contains all code in one script.



### Install source from GitHub 

The [sourcode is available on GitHub](https://github.com/mosbth/cimage). Clone, fork or [download as zip](https://github.com/mosbth/cimage/archive/master.zip). 

**Latest stable version is v0.7.18 released 2016-08-09.**

I prefer cloning like this. Do switch to the latest stable version.

```bash
git clone git://github.com/mosbth/cimage.git
cd cimage
git checkout v0.7.18
```

Make the cache-directory writable by the webserver.

```bash
chmod 777 cache
```


### Install all-included bundle 

There are some all-included bundles of `img.php` that can be downloaded and used without dependency to the rest of the sourcecode.

| Scriptname | Description | 
|------------|-------------|
| `imgd.php` | Development mode with verbose error reporting and option `&verbose` enabled. | 
| `imgp.php` | Production mode logs all errors to file, giving server error 500 for bad usage, option `&verbose` disabled. | 
| `imgs.php` | Strict mode logs few errors to file, giving server error 500 for bad usage, option `&verbose` disabled. | 

Dowload the version of your choice like this.

```bash
wget https://raw.githubusercontent.com/mosbth/cimage/v0.7.18/webroot/imgp.php
```

Open up the file in your editor and edit the array `$config`. Ensure that the paths to the image directory and the cache directory matches your environment, or create an own config-file for the script.



### Install from Packagist

You can install the package [`mos/cimage` from Packagist](https://packagist.org/packages/mos/cimage) using composer.



Use cases 
--------------------------------------

Lets take some use cases to let you know when and how `img.php` might be useful.



### Make a thumbnail 

<img src="https://cimage.se/cimage/imgd.php?src=example/kodim04.png&w=80&h=80&cf">

Lets say you have a larger image and you want to make a smaller thumbnail of it with a size of 80x80 pixels. You simply take the image and add constraints on `width`, `height` and you use the resize strategy `crop-to-fit` to crops out the parts of the image that does not fit.

To produce such a thumbnail, create a link like this:

> `img.php?src=kodim04.png&width=80&height=80&crop-to-fit`



### Slightly complexer use case 

Perhaps you got an image from a friend. The image was taken with the iPhone and thus rotated. 

<img src="https://cimage.se/cimage/imgd.php?src=example/issue36/me-270.jpg&w=250">

The original image is looking like this one, scaled down to a width of 250 pixels. 

So, you need to rotate it and crop off some parts to make it intresting. 

To show it off, I'll auto-rotate the image based on its EXIF-information, I will crop it to a thumbnail of 100x100 pixels and add a filter to make it greyscale finishing up with a sharpen effect. Just for the show I'll rotate the image 25 degrees - do not ask me why.

Lets call this *the URL-Photoshopper*. This is how the magic looks like. 

> `img.php?src=issue36/me-270.jpg&w=100&h=100&cf&aro`
> `&rb=-25&a=8,30,30,38&f=grayscale&convolve=sharpen-alt`

<img src="https://cimage.se/cimage/imgd.php?src=example/issue36/me-270.jpg&w=100&h=100&cf&aro&rb=-25&a=8,30,30,38&f=grayscale&convolve=sharpen-alt">

For myself, I use `img.php` to put up all images on my website, it gives me the power of affecting the resulting images - without opening up a photo-editing application.



Get going quickly 
--------------------------------------



### Check out the test page 

Try it out by pointing your browser to the test file `webroot/test/test.php`. It will show some example images and you can review how they are created.



### Process your first image 

<img src="https://cimage.se/cimage/imgd.php?src=example/kodim04.png&amp;w=w2&amp;a=40,0,50,0" alt=''>

Try it yourself by opening up an image in your browser. Start with 

> `webroot/img.php?src=kodim04.png` 

and try to resize it to a thumbnail by adding the options 

> `&width=100&height=100&crop-to-fit`



### What does "processing the image" involves? 

Add `&verbose` to the link to get a verbose output of what is happens during image processing. This is useful for developers and those who seek a deeper understanding on how it works behind the scene. 



### Check your system 

Open up `webroot/check_system.php` if you are uncertain that your system has the right extensions loaded. 




### How does it work? 

Review the settings in `webroot/img_config.php` and check out `webroot/img.php` on how it uses `CImage`.

The programatic flow, just to get you oriented in the environment, is.

1. Start in `img.php`.
2. `img.php` reads configuration details from `img_config.php` (if the config-file is available).
3. `img.php` reads and processes incoming `$_GET` arguments to prepare using `CImage`.
4. `img.php` uses `CImage`.
5. `CImage` processes, caches and outputs the image according to how its used. 

Read on to learn more on how to use `img.php`.



Basic usage 
--------------------------------------



### Select the source 

Open an image through `img.php` by using its `src` attribute.

> `img.php?src=kodim13.png`

It looks like this.

<img src="https://cimage.se/cimage/imgd.php?src=example/kodim13.png&w=w1&save-as=jpg">

All images are stored in a directory structure and you access them as:

> `?src=dir1/dir2/image.png`



### Resize using constraints on width and height 

Create a thumbnail of the image by applying constraints on width and height, or one of them.

| `&width=150`        | `&height=150`       | `&w=150&h=150`      |
|---------------------|---------------------|---------------------|
| <img src=https://cimage.se/cimage/imgd.php?src=example/kodim13.png&w=150 alt=''> | <img src=https://cimage.se/cimage/imgd.php?src=example/kodim13.png&h=150 alt=''> | <img src=https://cimage.se/cimage/imgd.php?src=example/kodim13.png&w=150&h=150 alt=''> |

By setting `width`, `height` or both, the image gets resized to be *not larger* than the defined dimensions *and* keeping its original aspect ratio.

Think of the constraints as a imaginary box where the image should fit. With `width=150` and `height=150` the box would have the dimension of 150x150px. A landscape image would fit in that box and its width would be 150px and its height depending on the aspect ratio, but for sure less than 150px. A portrait image would fit with a height of 150px and the width depending on the aspect ratio, but surely less than 150px.



### Resize to fit a certain dimension 

Creating a thumbnail with a certain dimension of width and height, usually involves stretching or cropping the image to fit in the selected dimensions. Here is how you create a image that has the exact dimensions of 300x150 pixels, by either *stretching*, *cropping* or *fill to fit*.


| What                | The image           |
|---------------------|---------------------|
| **Original.** The original image resized with a max width and max height.<br>`?w=300&h=150` | <img src=https://cimage.se/cimage/imgd.php?src=example/kodim13.png&w=300&h=150 alt=''> |
| **Stretch.** Stretch the image so that the resulting image has the defined width and height.<br>`?w=300&h=150&stretch` | <img src=https://cimage.se/cimage/imgd.php?src=example/kodim13.png&w=300&h=150&stretch alt=''> |
| **Crop to fit.** Keep the aspect ratio and crop out the parts of the image that does not fit.<br>`?w=300&h=150&crop-to-fit` | <img src=https://cimage.se/cimage/imgd.php?src=example/kodim13.png&w=300&h=150&crop-to-fit alt=''> |
| **Fill to fit.** Keep the aspect ratio and fill then blank space with a background color.<br>`?w=300&h=150&fill-to-fit=006600` | <img src=https://cimage.se/cimage/imgd.php?src=example/kodim13.png&w=300&h=150&fill-to-fit=006600 alt=''> |

Learn to crop your images, creative cropping can make wonderful images from appearingly useless originals.

Stretching might work, like in the above example where you can not really notice that the image is stretched. But usually, stretching is not that a good option since it distorts the ratio. Stretching a face may not turn out particularly well.

Fill to fit is useful when you have some image that must fit in a certain dimension and stretching nor cropping can do it. Carefully choose the background color to make a good resulting image. Choose the same background color as your website and no one will notice.



### List of parameters

`img.php` supports a lot of parameters. Combine the parameters to get the desired behavior and resulting image. For example, take the original image, resize it using width, aspect-ratio and crop-to-fit, apply a sharpen effect, save the image as JPEG using quality 30.

> `img.php?src=kodim13.png&w=600&aspect-ratio=4`
> `&crop-to-fit&sharpen&save-as=jpg&q=30`

<img src=https://cimage.se/cimage/imgd.php?src=example/kodim13.png&w=600&aspect-ratio=4&crop-to-fit&sharpen&save-as=jpg&q=30 alt=''>

Here is a list of all parameters that you can use together with `img.php`, grouped by its basic intent of usage. 


#### Mandatory options and debugging 

Option `src` is the only mandatory option. The options in this section is useful for debugging or deciding what version of the target image is used.

| Parameter      | Explained                                    | 
|----------------|----------------------------------------------|
| `src`          | Source image to use, mandatory. `src=img.png` or with subdirectory `src=dir/img.png`. |
| `nc, no-cache` | Do not use the cached version, do all image processing and save a new image to cache. |
| `so, skip-original`| Skip using the original image, always process image, create and use a cached version of the original image. |
| `v, verbose`   | Do verbose output and print out a log what happens. Good for debugging, analyzing the process and inspecting how the image is being processed. |
| `json`         | Output a JSON-representation of the image, useful for testing or optimizing when one wants to know the image dimensions, before using it. |
| `pwd, password` | Use password to protect unauthorized usage. |



#### Options for deciding width and height of target image

These options are all affecting the final dimensions, width and height, of the resulting image.

| Parameter      | Explained                                    | 
|----------------|----------------------------------------------|
| `h, height`    | `h=200` sets the width to be to max 200px. `h=25%` sets the height to max 25% of its original height. |
| `w, width`     | `w=200` sets the height to be max 200px. `w=100%` sets the width to max 100% of its original width. |
| `ar, aspect-ratio` | Control target aspect ratio. Use together with either height or width or alone to base calculations on original image dimensions. This setting is used to calculate the resulting dimension for the image. `w=160&aspect-ratio=1.6` results in a height of 100px. Use `ar=!1.6` to inverse the ratio, useful for portrait images, compared to landscape images. |
| `dpr, device-pixel-ratio` | Default value is 1, set to 2 when you are delivering the image to a high density screen, `dpr=2` or `dpr=1.4`. Its a easy way to say the image should have larger dimensions. The resulting image will be twice as large (or 1.4 times), keeping its aspect ratio. |



#### Options for resize strategy

These options affect strategy to use when resizing an image into a target image that has both width and height set.

| Parameter      | Explained                                    | 
|----------------|----------------------------------------------|
| `nr, no-ratio, stretch` | Do *not* keep aspect ratio when resizing and using both width & height constraints. Results in stretching the image, if needed, to fit in the resulting box. |
| `cf, crop-to-fit`  | Set together with both `h` and `w` to make the image fit into dimensions, and crop out the rest of the image. |
| `ff, fill-to-fit` | Set together with both `h` and `w` to make the image fit into dimensions, and fill the rest using a background color. You can optionally supply a background color as this `ff=00ff00`, or `ff=00ff007f` when using the alpha channel. |
| `nu, no-upscale` | Avoid smaller images from being upscaled to larger ones. Combine with `stretch`, `crop-to-fit` or `fill-to-fit` to get the smaller image centered on a larger canvas. The requested dimension for the target image are thereby met. |



#### Options for cropping part of image

These options enable to decide what part of image to crop out.

| Parameter      | Explained                                    | 
|----------------|----------------------------------------------|
| `c, crop`      | Crops an area from the original image, set `width`, `height`, `start_x` and `start_y` to define the area to crop, for example `crop=100,100,10,10` (`crop=width,height,start_x,start_y`). Left top corner is 0, 0. You can use `left`, `right` or `center` when setting `start_x`. You may use `top`, `bottom` or `center` when setting `start_y`. |
| `a, area`      | Define the area of the image to work with. Set `area=10,10,10,10` (`top`, `right`, `bottom`, `left`) to crop out the 10% of the outermost area. It works like an offset to define the part of the image you want to process. Its an alternative of using `crop`. |



#### General processing options

These options are general options affecting processing.

| Parameter      | Explained                                    | 
|----------------|----------------------------------------------|
| `bgc, bg-color` | Set the backgroundcolor to use (if its needed). Use six hex digits as `bgc=00ff00` and 8 digits when using the alpha channel, as this `bgc=00ff007f`. The alpha value can be between 00 and 7f. |



#### Processing of image before resizing 

This option are executed *before* the image is resized.

| Parameter      | Explained                                    | 
|----------------|----------------------------------------------|
| `s, scale`     | Scale the image to a size proportional to a percentage of its original size, `scale=25` makes an image 25% of its original size and `size=200` doubles up the image size. Scale is applied before resizing and has no impact of the target width and height. |
| `rb, rotate-before` | Rotate the image before its processed, send the angle as parameter `rb=45`. |
| `aro, auto-rotate`  | Auto rotate the image based on EXIF information (useful when using images from smartphones). |



#### Processing of image after resizing 

These options are executed *after* the image is resized.

| Parameter      | Explained                                    |
|----------------|----------------------------------------------|
| `ra, rotate-after`<br>`r, rotate` | Rotate the image after its processed, send the angle as parameter `ra=45`. |
| `sharpen`            | Appy a convolution filter that sharpens the image.       |
| `emboss`             | Appy a convolution filter with an emboss effect.         |
| `blur`               | Appy a convolution filter with a blur effect.            |
| `convolve`           | Appy custom convolution filter as a 3x3 matrix, a divisor and offset, `convolve=0,-1,0,-1,5,-1,0,-1,0,1,0` sharpens the image. |
| `convolve`           | Use predefined convolution expression as `convolve=sharpen-alt` or a serie of convolutions as `convolve=draw,mean,motion`. These are supported out of the box: `lighten`, `darken`, `sharpen`, `sharpen-alt`, `emboss`, `emboss-alt`, `blur`, `gblur`, `edge`, `edge-alt`, `draw`, `mean`, `motion`. Add your own, or overwrite existing, in `img_config.php`. |
| `f, filter`          | Apply filter to image, `f=colorize,0,255,0,0` makes image more green. Supports all filters as defined in [PHP GD `imagefilter()`](http://php.net/manual/en/function.imagefilter.php). |
| `f0, f1-f9`    | Same as `filter`, just add more filters. Applied in order `f`, `f0-f9`.  |
| `sc, shortcut` | Save longer expressions in `img_config.php`. One place to change your favorite processing options, use as `sc=sepia` which is a shortcut for `&f=grayscale&f0=brightness,-10&f1=contrast,-20`<br>`&f2=colorize,120,60,0,0&sharpen`. |



#### Saving image, affecting quality and file size 

Options for saving the target image.

| Parameter      | Explained                                    | 
|----------------|----------------------------------------------|
| `q, quality`   | Quality affects lossy compression and file size for JPEG images by setting the quality between 1-100, default is 60.  Quality only affects JPEG. |
| `co, compress` | For PNG images it defines the compression algorithm, values can be 0-9, default is defined by PHP GD. Compress only affects PNG. |
| `p, palette`   | Create a palette version of the image with up to 256 colors. |
| `sa, save-as`  | Save resulting image as JPEG, PNG or GIF, for example `?src=river.png&save-as=gif`. |
| `alias`        | Save resulting image as a copy in the alias-directory. |

Carry on reading to view examples on how to use and combine the parameters to achieve desired effects and target images.



The configuration settings in `_config.php`
--------------------------------------

There are several configurations settings that can be used to change the behaviour of `img.php`. Here is an overview, listed as they appear in the default config-file.

| Setting                 | Explained                                    | 
|-------------------------|----------------------------------------------|
| `mode`                  | Set to "development", "production" or "strict" to match the mode of your environment. It mainly affects the error reporting and if option `verbose` is enabled or not. |
| `autoloader`            | Path to the file containing the autoloader. | 
| `image_path`            | Path to the directory-structure containing the images. |  
| `cache_path`            | Path to the directory where all the cache-files are stored. |  
| `alias_path`            | Path to where the alias, or copy, of the images are stored. |  
| `password`              | Set the password to use. |  
| `password_always`       | Always require the use of password and match with `password`. |  
| `remote_allow`          | Allow remote download of images when `src=http://example.com/img.jpg`. |  
| `remote_pattern`        | Pattern (regexp) to detect if a file is remote or not. |  
| `valid_filename`        | A regular expression to test if a `src` filename is valid or not. |  
| `valid_aliasname`       | A regular expression to test if a `alias` filename is valid or not. |  |  
| `img_path_constraint`   | Check that the target image is in a true subdirectory of `img-path` (disables symbolic linking to another part of the filesystem. |  
| `default_timezone`      | Use to set the timezone if its not already set. |  
| `max_width`             | Maximal width of the target image. Fails for larger values. | 
| `max_height`            | Maximal height of the target image. Fails for larger values. | 
| `background_color`      | Specify a default background color and overwrite the one proposed by `CImage`. |  
| `png_filter`            | Use (or not) an external command for filter PNG images. |  
| `png_filter_cmd`        | Path and options to the actual external command. |  
| `png_deflate`           | Use (or not) an external command for deflating PNG images. |  
| `png_deflate_cmd`       | Path and options to the actual external command. |  
| `jpeg_optimize`         | Use (or not) an external command for optimizing JPEG images. |  
| `jpeg_optimize_cmd`     | Path and options to the actual external command. |  
| `convolution_constant`  | Constants for own defined convolution expressions. |  
| `allow_hotlinking`      | Allow or disallow hotlinking7leeching of images. |  
| `hotlinking_whitelist`  | Array of regular expressions that allow hotlinking (if hotlinking is disabled). |  
| `shortcut`              | Define own shortcuts for more advanced combination of options to `img.php`. |  
| `size_constant`         | Create an array with constant values to be used instead of `width` and `height`. |  
| `aspect_ratio_constant` | Create an array for constant values to be used with option 'aspect-ratio`. |  

Consult the file `webroot/img-config.php` for a complete list together with the default values for each configuration setting. There is an [appendix where you can see the default config-file](#img-config).



### Create and name the config file 

The file `img.php` looks for the config-file `img_config.php`, and uses it if its found. The three files where everything is included -- `imgd.php`, `imgp.php` and `imgs.php` -- includes an empty `$config`-array which can be overridden by saving a config-file in the same directory. If the script is `imgp.php` then name the config-file `imgp_config.php` and it will find it and use those settings. 



Debugging image processing 
--------------------------------------

You can visualize what happens during image processing by adding the `v, verbose` parameter. It will then display the resulting image together with a verbose output on what is actually happening behind the scene.

<img src="http://dbwebb.se/image/snapshot/CImage_verbose_output.jpg?w=w2&q=60&sharpen">

This can be most useful for debugging and to understand what actually happen.

The parameter `nc, no-cache` ignores the cached item and will always create a new cached item.

The parameter `so, skip-original` skips the original image, even it that is a best fit. As a result a cached image is created and displayed.



A JSON representation of the image
--------------------------------------

You can ge a JSON representation of the image by adding the option `json`. This can be useful if you need to know the actual dimension of the image. 

For example, the following image is created like this:

> `&w=300&save-as=jpg`

<img src="https://cimage.se/cimage/imgd.php?src=example/kodim24.png&w=300&save-as=jpg" alt=''>

Its JSON-representation is retrieved like this:

> `&w=300&save-as=jpg&json`

Which gives the following result.

```php
{  
    "src":"kodim24.png",
    "srcGmdate":"Wed, 12 Feb 2014 13:46:19",
    "cache":"_._kodim24_300_200_q60.jpg",
    "cacheGmdate":"Sat, 06 Dec 2014 14:09:50",
    "filename":"_._kodim24_300_200_q60.jpg",
    "width":300,
    "height":200,
    "aspectRatio":1.5,
    "size":11008,
    "colors":25751
}
```

I'll use this feature for ease testing of `img.php` and `CImage`. But the feature can also be useful when one really want complete control over the resulting dimension of an image.




Implications and considerations 
--------------------------------------

Here are some thoughts when applying `img.php` on a live system.



### Select the proper mode 

Select the proper mode for `img.php`. Set it to "strict" or "production" to prevent outsiders to get information about your system. Use only "development" for internal use since its quite verbose in its nature of error reporting. 



### Put the installation directory outside web root 

Edit the config file to put the installation directory -- and the cache directory -- outside of the web root. Best practice would be to store the installation directory and cache, outside of the web root. The only thing needed in the web root is `img.php` and `img_config.php` (if used) which can be placed, for example, in `/img/img.php` or just as `/img.php`.



### Friendly urls through `.htaccess` 

Use `.htaccess`and rewrite rules (Apache) to get friendly image urls. Put `img.php` in the `/img` directory. Put the file `.htaccess` in the web root.

**.htaccess for `img.php`.**

```php
#
# Rewrite to have friendly urls to img.php, edit it to suite your environment.
#
# The example is set up as following.
#
#  img                 A directory where all images are stored
#  img/me.jpg          Access a image as usually.
#  img/img.php         This is where I choose to place img.php (and img_config.php).
#  image/me.jpg        Access a image though img.php using htaccess rewrite.
#  image/me.jpg?w=300  Using options to img.php.
# 
# Subdirectories also work.
#  img/me/me.jpg          Direct access to the image.
#  image/me/me.jpg        Accessed through img.php.
#  image/me/me.jpg?w=300  Using options to img.php.
#
RewriteRule ^image/(.*)$        img/img.php?src=$1 [QSA,NC,L]
```

You can now access all images through either `/image/car.jpg` (which uses `img.php`) or as usual through `/img/car.jpg` without passing through `img.php`. You send the arguments as usual.

> `/image/car.jpg?w=300&sharpen`

Or a image that resides in a subdirectory.

> `/image/all-cars/car.jpg?w=300&sharpen`

The result is good readable urls to your images. Its easy for the search engine to track and you can use the directory structure already existing in `/img`. Just like one wants to have it.



### Monitor cache size

There is a utility `cache.bash` included for monitoring the size of the cache-directory. It generates an output like this.

```bash
$ ./cache.bash
Usage: ./cache.bash [cache-dir]   

$ ./cache.bash cache                         
Total size:       27M                                            
Number of files:  225                                            
                                                                 
Top-5 largest files:                                             
1032    cache/_._kodim08_768_512_q60convolvesharpen.png          
960     cache/_._kodim08_768_512_q60convolveemboss.png           
932     cache/_._kodim08_768_512_q60_rb45.png                    
932     cache/_._kodim08_768_512_q60_ra45.png                    
856     cache/_._kodim08_768_512_q60_rb90.png                    
                                                                 
Last-5 created files:                                            
2014-11-26 16:51 cache/_._kodim08_768_512_q60convolvelighten.png 
2014-11-26 16:51 cache/_._kodim08_768_512_q60convolveblur.png    
2014-11-26 16:48 cache/_._kodim08_400_267_q60convolvesharpen.png 
2014-11-26 16:48 cache/_._kodim08_400_267_q60convolvelighten.png 
2014-11-26 16:48 cache/_._kodim08_400_267_q60convolveemboss.png  
                                                                 
Last-5 accessed files:                                           
2014-11-27 16:12 _._wider_900_581_q60.jpg                        
2014-11-27 16:12 _._wider_750_484_q60.jpg                        
2014-11-27 16:12 _._wider_640_413_q60.jpg                        
2014-11-27 16:12 _._wider_640_200_c640-200-0-100_q60.jpg         
2014-11-27 16:12 _._wider_600_387_q60.jpg                        
```

Use it as a base if you feel the need to monitor the size och the cache-directory.



### Read-only cache

The cache directory need to be writable for `img.php` to create new files. But its possible to first create all cache-files and then set the directory to be read-only. This will give you a way of shutting of `img.php` from creating new cache files. `img.php` will then continue to work for all images having a cached version but will fail if someone tries to create a new, not previously cached, version of the image.



### Post-processing with external tools

You can use external tools to post-process the images to optimize the file size. This option is available for JPEG and for PNG images. Post-processing is disabled by default, edit `img_config.php` to enable it.

It takes additional time to do post processing, it can take up to a couple of seconds. This is processing to create the cached image, thereafter the cached version will be used and no more post processing needs to be done.

These tools for post processing is not a part of `CImage` and `img.php`, you need to download and install them separately. I use them myself on my system to get an optimal file size.



### Allowing remote download of images

You can allow `img.php` to download remote images. That can be enabled in the config-file. However, before doing so, consider the implications on allowing anyone to download a file, hopefully an image, to your server and then the possibility to access it through the webserver.

That sounds scary. It should.

For my own sake I will use it like this, since I consider it a most useful feature.

* Create a special version of `img.php` that has remote download allowed, hide it from public usage.
* Always use a password.
* Download and process the image and save it as an `alias`.
* Integrate the image into your webpage and use the image in the alias directory.

This is an easy way to quickly download a remote image, process and share it.

So, its a scary feature and I might regret I did put it in. Still, its disabled by default and you enable it on your own risk. I have tried to make it as secure as I can, but I might have missed something. I will run it on my own system so I guess I'll find out how secure it is.



Community 
--------------------------------------

There is a Swedish forum where you can ask questions, even in English. The forum is a general forum for education in web development, it is not specific for this software. 

Ask questions on `CImage` and `img.php` [in the PHP sub forum]([BASEURL]forum/viewforum.php?f=12).

Or ask it on GitHub by creating an issue -- that would be the best place to ask questions.

Or if you fancy irc.

* `irc://irc.bsnet.se/#db-o-webb`
* `irc://irc.freenode.net/#dbwebb`



Trouble- and feature requests 
--------------------------------------

Use [GitHub to report issues](https://github.com/mosbth/cimage/issues). Always include the following.

1. Describe very shortly: What are you trying to achieve, what happens, what did you expect.
2. Parameter list used for `img.php`.
3. The image used.

If you request a feature, describe its usage and argument for why you think it fits into `CImage` and `img.php`.

Feel free to fork, clone and create pull requests.




```
 .
..:  Copyright 2012-2015 by Mikael Roos (me@mikaelroos.se)
```


================================================
FILE: REVISION.md
================================================
Revision history
=====================================

<!--
[![Build Status](https://travis-ci.org/mosbth/cimage.svg?branch=master)](https://travis-ci.org/mosbth/cimage)
[![Build Status](https://scrutinizer-ci.com/g/mosbth/cimage/badges/build.png?b=master)](https://scrutinizer-ci.com/g/mosbth/cimage/build-status/master)
--->

v0.8.6 (2023-10-27)
-------------------------------------

* Fix deprecation notice on "Creation of dynamic property" for PHP 8.2.



v0.8.5 (2022-11-17)
-------------------------------------

* Enable configuration fix for solving Windows 2 WSL2 issue with is_readable/is_writable #189.
* Update CHttpGet.php for php 8.1 deprecated notice #188.
* Remove build status from README (since it is not up to date).



v0.8.4 (2022-05-30)
-------------------------------------

* Support PHP 8.1 and remove (more) deprecated messages when run in in development mode.



v0.8.3 (2022-05-24)
-------------------------------------

* Support PHP 8.1 and remove deprecated messages when run in in development mode.
* Generate prebuilt all include files for various settings
* Fix deprecated for PHP 8.1
* Fix deprecated for PHP 8.1
* Add php version as output in verbose mode
* Add PHP 81 as test environment



v0.8.2 (2021-10-27)
-------------------------------------

* Remove bad configuration.



v0.8.1 (2020-06-08)
-------------------------------------

* Updated version number in define.php.



v0.8.0 (2020-06-08)
-------------------------------------

* Enable to set JPEG image as interlaced, implement feature #177.
* Add function getValue() to read from querystring.
* Set PHP 7.0 as precondition (to prepare to update the codebase).



v0.7.23 (2020-05-06)
-------------------------------------

* Fix error in composer.json



v0.7.22 (2020-05-06)
-------------------------------------

* Update composer.json and move ext-gd from required to suggested to ease installation where cli does not have all extensions installed.



v0.7.21 (2020-01-15)
-------------------------------------

* Support PHP 7.4, some minor fixes with notices.


v0.7.20 (2017-11-06)
-------------------------------------

* Remove webroot/img/{round8.PNG,wider.JPEG,wider.JPG} to avoid unzip warning message when installing with composer.
* Adding docker-compose.yml #169.


v0.7.19 (2017-03-31)
-------------------------------------

* Move exception handler from functions.php to img.php #166.
* Correct XSS injection in `check_system.php`.
* Composer suggests ext-imagick and ext-curl.


v0.7.18 (2016-08-09)
-------------------------------------

* Made `&lossless` a requirement to not use the original image.


v0.7.17 (2016-08-09)
-------------------------------------

* Made `&lossless` part of the generated cache filename.


v0.7.16 (2016-08-09)
-------------------------------------

* Fix default mode to be production.
* Added pngquant as extra postprocessing utility for PNG-images, #154.
* Bug `&status` wrong variable name for fast track cache.


v0.7.15 (2016-08-09)
-------------------------------------

* Added the [Lenna/Lena sample image](http://www.cs.cmu.edu/~chuck/lennapg/) as tif and created a png, jpeg and webp version using Imagick convert `convert lena.tif lena.{png,jpg,webp}`, #152.
* Limited and basic support for WEBP format, se #132.


v0.7.14 (2016-08-08)
-------------------------------------

* Re-add removed cache directory.
* Make fast track cache disabled by default in the config file.


v0.7.13 (2016-08-08)
-------------------------------------

* Moved functions from img.php to `functions.php`.
* Added function `trace()` to measure speed and memory consumption, only for development.
* Added fast cache #149.
* Added `imgf.php` as shortcut to check for fast cache, before loading `img.php` as usual, adding `imgf_config.php` as symlink to `img_config.php`.
* Created `defines.php` and moved definition av version there.
* Fixed images in README, #148.
* Initiated dependency injection to `CImage`, class names can be set in config file and will be injected to `CImage` from `img.php`. Not implemented for all classes. #151.
* Enabled debug mode to make it easier to trace what actually happens while processing the image, #150.


v0.7.12 (2016-06-01)
-------------------------------------

* Fixed to correctly display image when using a resize strategy without height or width.
* Fixed background color for option `no-upscale`, #144.


v0.7.11 (2016-04-18)
-------------------------------------

* Add option for `skip_original` to config file to always skip original, #118.


v0.7.10 (2016-04-01)
-------------------------------------

* Add backup option for images `src-alt`, #141.
* Add require of ext-gd in composer.json, #133.
* Fix strict mode only reporting 404 when failure, #127.


v0.7.9 (2015-12-07)
-------------------------------------

* Strict mode only reporting 404 when failure, #127.
* Added correct CImage version to remote agent string, #131.
* Adding CCache to improve cache handling of caching for dummy, remote and srgb. #130.


v0.7.8 (2015-12-06)
-------------------------------------

* HTTP error messages now 403, 404 and 500 as in #128 and #127.
* More examples on dealing with cache through bash `bin/cache.bash`, #129.
* Added conversion to sRGB using option `?srgb`. #120.
* Added Gitter badge to README, #126.
* Fix proper download url in README, #125.
* Change path in `webroot/htaccess` to make it work in current environment.


v0.7.7 (2015-10-21)
-------------------------------------

* One can now add a HTTP header for Cache-Control in the config file, #109.
* Added hook in img,php before CImage is called, #123.
* Added configuration for default jpeg quality and png compression in the config file, #107.
* Strip comments and whitespace in imgs.php, #115.
* Bundle imgs.php did not have the correct mode.
* Adding option &status to get an overview of the installed on configured utilities, #116.
* Bug, all files saved as png-files, when not saving as specific file.
* Removed saving filename extension for alias images.
* Added option to decide if resample or resize when copying images internally. `&no-resample` makes resize, instead of resample as is default.
* Verbose now correctly states if transparent color is detected.
* Compare-tool now supports 6 images.
* Added option for dark background in the compare-tool.
* Removed that source png-files, containing less than 255 colors, is always saved as palette images since this migth depend on processing of the image.
* Adding save-as as part of the generated cache filename, #121.
* Add extra fields to json-response, #114.
* Add header for Content-Length, #111.
* Add check for postprocessing tools in path in `webroot/check_system.php`, #104.


v0.7.6 (2015-10-18)
-------------------------------------

* Adding testpage for dummy images `webroot/test/test_issue101-dummy.php`.
* Adding width and height when creating dummy image.


v0.7.5 (2015-10-18)
-------------------------------------

* Adding feature for creating dummy images `src=dummy`, #101.
* Add png compression to generated cache filename, fix #103.
* Removed file prefix from storing images in cache, breaking filenamestructure for cache images.
* Code cleaning in `CImage.php`.


v0.7.4 (2015-09-15)
-------------------------------------

* Add CAsciiArt.php to composer for autoloading, fix #102.
* Generate filename with filters, does not work on Windows, fix #100.


v0.7.3 (2015-09-01)
-------------------------------------

* Support output of ascii images, #67.


v0.7.2 (2015-08-17)
-------------------------------------

* Allow space in remote filenames, fix #98.


v0.7.1 (2015-07-25)
-------------------------------------

* Support for password hashes using `text`, `md5` and `hash`, fix #77.
* Using `CWhitelist` for checking hotlinking to images, fix #88.
* Added mode for `test` which enables logging verbose mode to file, fix #97.
* Improved codestyle and added `phpcs.xml` to start using phpcs to check code style, fix #95.
* Adding `composer.json` for publishing on packagist.
* Add permalink to setup for comparing images with `webroot/compare/compare.php`, fix #92.
* Allow space in filename by using `urlencode()` and allow space as valid filenam character. fix #91.
* Support redirections for remote images, fix #87, fix #90.
* Improving usage of Travis and Scrutinizer.
* Naming cache-file using md5 for remote images, fix #86.
* Loading images without depending on filename extension, fix #85.
* Adding unittest with phpunit #84, fix #13
* Adding support for whitelist of remote hostnames, #84
* Adding phpdoc, fix #48.
* Adding travis, fix #15.
* Adding scrutinizer, fix #57.


v0.7.0 (2015-02-10)
-------------------------------------

* Always use password, setting in img_config.php, fix #78.
* Resize gif keeping transparency #81.
* Now returns statuscode 500 when something fails #55.
* Three different modes: strict, production, development #44.
* Three files for all-in-one `imgs.php`, `imgp.php`, `imgd.php` #73.
* Change name of script all-in-one to `webroot/imgs.php` #73.
* Combine all code into one singel script, `webroot/img_single.php` #73.
* Disallow hotlinking/leeching by configuration #46.
* Alias-name is without extension #47.
* Option `alias` now requires `password` to work #47.
* Support for option `password, pwd` to protect usage of `alias` and remote download.
* Added support for option `alias` that creates a link to a cached version  of the image #47.
* Create cache directory for remote download if it does not exists.
* Cleaned up `img_config.php` and introduced default values for almost all options #72.


v0.6.2 (2015-01-14)
-------------------------------------

* Added support for download of remote images #43.
* Added autoloader.


v0.6.1 (2015-01-08)
-------------------------------------

* Adding compare-page for comparing images. Issue #20.
* Added option `no-upscale, nu` as resizing strategy to decline upscaling of smaller images. Fix #61.
* Minor change in `CImage::resize()`, crop now does imagecopy without resamling.
* Correcting internal details for save-as and response json which indicated wrong colors. Fix #62.
* Fixed fill-to-fit that failed when using aspect-ratio. Fix #52.
* JSON returns correct values for resulting image. Fix #58.
* Corrected behaviour for skip-original. Fix #60.


v0.6 (2014-12-06)
-------------------------------------

* Rewrote and added documentation.
* Moved conolution expressesion from `img_config.php` to `CImage`.
* Minor cleaning of properties in `CImage`. Fix #23.
* Adding `webroot/htaccess` to show off how friendly urls can be created for `img.php`. Fix #45.
* Added option `fill-to-fit, ff`. Fix #38.
* Added option `shortcut, sc` to enable configuration of complex expressions. Fix #2.
* Added support for custom convolutions. Fix #49.
* Restructured testprograms. Fix #41.
* Corrected json on PHP 5.3. Fix #42.
* Improving template for tests in `webroot/tests` when testing out #40.
* Adding testcase for #40.
* Adding option `convolve` taking comma-separated list of 11 float-values, wraps and exposes `imageconvoluttion()`. #4
* Adding option `dpr, device-pixel-ratio` which defaults to 1. Set to 2 to get a twice as large image. Useful for Retina displays. Basically a shortcut to enlarge the image.
* Adding utility `cache.bash` to ease gathering stats on cache usage. #21
* Cache-directory can now be readonly and serve all cached files, still failing when need to save files. #5
* Cache now uses same file extension as original image #37.
* Can output image as json format using `json` #11.


v0.5.3 (2014-11-21)
-------------------------------------

* Support filenames of uppercase JPEG, JPG, PNG and GIF, as proposed in #37.
* Changing `CImage::output()` as proposed in #37.
* Adding security check that image filename is always below the path `image_path` as specified in `img_config.php` #37.
* Adding configuration item in `img_config.php` for setting valid characters in image filename.
* Moving `webroot/test*` into directory `webroot/test`.
* `webroot/check_system.php` now outputs if extension for exif is loaded.
* Broke API when `initDimensions()` split into two methods, new `initDimensions()` and `loadImageDetails()`.
* Added `autoRotate, aro` to auto rotate image based on EXIF information.
* Added `bgColor, bgc` to use as backgroundcolor when needing a filler color, for example rotate 45.
* Added `rotateBefore, rb` to rotate image a certain angle before processing.
* Added `rotateAfter, ra` to rotate image a certain angle after processing.
* Cleaned up code formatting, removed trailing spaces.
* Removed @ from opening images, better to display correct warning when failing #34, but put it back again.
* Setting gd.jpeg_ignore_warning to true as default #34.
* `webroot/check_system.php` now outputs version of PHP and GD.
* #32 correctly send 404 header when serving an error message.
* Trying to verify issue #29, but can not.
* Adding structure for testprograms together with, use `webroot/test_issue29.php` as sample.
* Improving code formatting.
* Moving parts of verbose output from img.php to CImage.php.


v0.5.2 (2014-04-01)
-------------------------------------

* Correcting issue #26 providing error message when not using postprocessing.
* Correcting issue #27 warning of default timezone.
* Removed default $config options in `img.php`, was not used, all configuration should be in `img_config.php`.
* Verified known bug - sharpen acts as blur in PHP 5.5.9 and 5.5.10 #28


v0.5.1 (2014-02-12)
-------------------------------------

* Display image in README-file.
* Create an empty `cache` directory as part of repo.


v0.5 (2014-02-12)
-------------------------------------

* Change constant name `CImage::PNG_QUALITY_DEFAULT` to `CImage::PNG_COMPRESSION_DEFAULT`.
* Split JPEG quality and PNG compression, `CImage->quality` and `CImage->compression`
* Changed `img.php` parameter name `d, deflate` to `co, compress`.
* Separating configuration issues from `img.php` to `img_config.php`.
* Format code according to PSR-2.
* Disabled post-processing JPEG and PNG as default.
* This version is supporting PHP 5.3, later versions will require 5.5 or later.
* Using GitHub issue tracking for feature requests and planning.
* Rewrote [the manual](http://dbwebb.se/opensource/cimage).
* Created directory `webroot` and moved some files there.


v0.4.1 (2014-01-27)
-------------------------------------

* Changed => to == on Modified-Since.
* Always send Last-Modified-Header.
* Added `htmlentities()` to verbose output.
* Fixed support for jpeg, not only jpg.
* Fixed crop whole image by setting crop=0,0,0,0
* Use negative values for crop width & height to base calulation on original width/height and withdraw selected amount.
* Correcting jpeg when setting quality.
* Removed obsolete reference to `$newName` in `CImage::__construct()` (issue 1).


v0.4 (2013-10-08)
-------------------------------------

* Improved support for pre-defined sizes.
* Adding grid column size as predefined size, c1-c24 for a 24 column grid. Configure in `img.php`.
* Corrected error on naming cache-files using subdir.
* Corrected calculation error on width & height for crop-to-fit.
* Adding effects for sharpen, emboss and blur through imageconvolution using matrixes.
* crop-to-fit, add parameter for offset x and y to enable to define which area is the, implemented as area.
* Support for resizing opaque images.
* Center of the image from which the crop is done. Improved usage of area to crop.
* Added support for % in width & height.
* Added aspect-ratio.
* Added scale.
* Quality for PNG images is now knows as deflate.
* Added palette to create images with max 256 colors.
* Added usage of all parameters to README.md
* Added documentation here http://dbwebb.se/opensource/cimage
* Adding `.gitignore`
* Re-adding `cache` directory


v0.3 (2012-10-02)
-------------------------------------

* Added crop. Can crop a area (`width`, `height`, `start_x`, `start_y`) from the original
image.
* Corrected to make the 304 Not Modified header work.
* Predefined sizes can be configured for width in `img.php`.
* Corrected to make crop work with width or height in combination with crop-to-fit.


v0.2 (2012-05-09)
-------------------------------------

* Implemented filters as in http://php.net/manual/en/function.imagefilter.php
* Changed `crop` to `crop_to_fit`, works the same way.
* Changed arguments and sends them in array.
* Added quality-setting.
* Added testcases for above.


v0.1.1 (2012-04-27)
-------------------------------------

* Corrected calculation where both width and height were set.


v0.1 (2012-04-25)
-------------------------------------

* Initial release after rewriting some older code doing the same, but not that good and flexible.


================================================
FILE: SECURITY.md
================================================
Security policy
======================

To report security vulnerabilities in the project, send en email to mikael.t.h.roos@gmail.com.

For other security related issues, please open an issue on the project.


================================================
FILE: autoload.php
================================================
<?php
/**
 * Autoloader for CImage and related class files.
 *
 */
require_once __DIR__ . "/defines.php";
require_once __DIR__ . "/functions.php";



/**
 * Autoloader for classes.
 *
 * @param string $class the fully-qualified class name.
 *
 * @return void
 */
spl_autoload_register(function ($class) {
    //$path = CIMAGE_SOURCE_PATH . "/{$class}.php";
    $path = __DIR__ . "/{$class}.php";
    if (is_file($path)) {
        require($path);
    }
});


================================================
FILE: bin/cache.bash
================================================
#!/bin/bash
#
# ls -ult list and sorts fils by its access time
#
#
# Main, start by checking basic usage
#
if [ $# -lt 1 ]
then
    echo "Usage: $0 [cache-dir]"
    exit 1
elif [ ! -d "$1" ]; then
    echo "Usage: $0 [cache-dir]"
    echo "$1 is not a directory."
    exit 1
fi



#
# Print out details on cache-directory
#
echo "# Size"
echo "Total size:       $( du -sh $1 | cut -f1 )"
echo "Number of files:  $( find $1 -type f | wc -l )"
echo "Number of dirs:   $( find $1 -type d | wc -l )"
echo
echo "# Top-5 largest files/dirs:"
echo "$( du -s $1/* | sort -nr | head -5 )"
echo 
echo "# Last-5 created files:"
echo "$( find $1 -type f -printf '%TY-%Tm-%Td %TH:%TM %p\n' | sort -r | head -5 )"
echo
echo "# Last-5 accessed files:"
echo "$( find $1 -type f -printf '%AY-%Am-%Ad %AH:%AM %f\n' | sort -r | head -5 )"
echo
echo "# 5 Oldest files:"
echo "$( find $1 -type f -printf '%AY-%Am-%Ad %AH:%AM %f\n' | sort | head -5 )"
echo
echo "# Files not accessed within the last 30 days"
echo "Number of files: $( find $1 -type f -atime +30 | wc -l )"
echo "Total file size: $( find $1 -type f -atime +30 -exec du {} \; | cut -f1 | paste -sd+ | bc )"
echo


================================================
FILE: bin/create-img-single.bash
================================================
#!/bin/bash

#
# Paths and settings
#
TARGET_D="webroot/imgd.php"
TARGET_P="webroot/imgp.php"
TARGET_S="webroot/imgs.php"
NEWLINES="\n\n\n"



#
# Specify the utilities used
#
ECHO="printf"



#
# Main, start by checking basic usage
#
if [ $# -gt 0 ]
then
    $ECHO "Usage: $0\n"
    exit 1
fi



#
# Print out details on cache-directory
#
$ECHO "Creating '$TARGET_D', '$TARGET_P' and '$TARGET_S' by combining the following files:"
$ECHO "\n"
$ECHO "\n webroot/img_header.php"
$ECHO "\n defines.php"
$ECHO "\n functions.php"
$ECHO "\n CHttpGet.php"
$ECHO "\n CRemoteImage.php"
$ECHO "\n CWhitelist.php"
$ECHO "\n CAsciiArt.php"
$ECHO "\n CImage.php"
$ECHO "\n CCache.php"
$ECHO "\n CFastTrackCache.php"
$ECHO "\n webroot/img.php"
$ECHO "\n"
$ECHO "\n'$TARGET_D' is for development mode."
$ECHO "\n'$TARGET_P' is for production mode (default mode)."
$ECHO "\n'$TARGET_S' is for strict mode."
$ECHO "\n"

$ECHO "\nPress enter to continue. "
read answer


#
# Create the $TARGET_? files
#
cat webroot/img_header.php > $TARGET_P
cat webroot/img_header.php | sed "s|//'mode'         => 'production',|'mode'         => 'development',|" > $TARGET_D
cat webroot/img_header.php | sed "s|//'mode'         => 'production',|'mode'         => 'strict',|" > $TARGET_S

$ECHO "$NEWLINES" | tee -a $TARGET_D $TARGET_P $TARGET_S > /dev/null

tail -n +2 defines.php | tee -a $TARGET_D $TARGET_P $TARGET_S > /dev/null
$ECHO "$NEWLINES" | tee -a $TARGET_D $TARGET_P $TARGET_S > /dev/null

tail -n +2 functions.php | tee -a $TARGET_D $TARGET_P $TARGET_S > /dev/null
$ECHO "$NEWLINES" | tee -a $TARGET_D $TARGET_P $TARGET_S > /dev/null

tail -n +2 CHttpGet.php | tee -a $TARGET_D $TARGET_P $TARGET_S > /dev/null
$ECHO "$NEWLINES" | tee -a $TARGET_D $TARGET_P $TARGET_S > /dev/null

tail -n +2 CRemoteImage.php | tee -a $TARGET_D $TARGET_P $TARGET_S > /dev/null
$ECHO "$NEWLINES" | tee -a $TARGET_D $TARGET_P $TARGET_S > /dev/null

tail -n +2 CWhitelist.php | tee -a $TARGET_D $TARGET_P $TARGET_S > /dev/null
$ECHO "$NEWLINES" | tee -a $TARGET_D $TARGET_P $TARGET_S > /dev/null

tail -n +2 CAsciiArt.php | tee -a $TARGET_D $TARGET_P $TARGET_S > /dev/null
$ECHO "$NEWLINES" | tee -a $TARGET_D $TARGET_P $TARGET_S > /dev/null

tail -n +2 CImage.php | tee -a $TARGET_D $TARGET_P $TARGET_S > /dev/null
$ECHO "$NEWLINES" | tee -a $TARGET_D $TARGET_P $TARGET_S > /dev/null

tail -n +2 CCache.php | tee -a $TARGET_D $TARGET_P $TARGET_S > /dev/null
$ECHO "$NEWLINES" | tee -a $TARGET_D $TARGET_P $TARGET_S > /dev/null

tail -n +2 CFastTrackCache.php | tee -a $TARGET_D $TARGET_P $TARGET_S > /dev/null
$ECHO "$NEWLINES" | tee -a $TARGET_D $TARGET_P $TARGET_S > /dev/null

tail -n +2 webroot/img.php | tee -a $TARGET_D $TARGET_P $TARGET_S > /dev/null
$ECHO "$NEWLINES" | tee -a $TARGET_D $TARGET_P $TARGET_S > /dev/null

php -w $TARGET_S > tmp && mv tmp $TARGET_S

$ECHO "\nDone."
$ECHO "\n"
$ECHO "\n"


================================================
FILE: cache/.gitignore
================================================
# Ignore everything in this directory
*
# Except this file
!.gitignore


================================================
FILE: composer.json
================================================
{
    "name": "mos/cimage",
    "type": "library",
    "description": "Process, scale, resize, crop and filter images.",
    "keywords": ["image", "imageprocessing", "gd"],
    "homepage": "http://dbwebb.se/opensource/cimage",
    "license": "MIT",
    "authors": [
        {
            "name": "Mikael Roos",
            "email": "me@mikaelroos.se",
            "homepage": "http://mikaelroos.se",
            "role": "Developer"
        }
    ],
    "support": {
        "issues": "https://github.com/mosbth/cimage/issues",
        "docs": "http://dbwebb.se/opensource/cimage"
    },
    "require": {
        "php": ">=7.0"
    },
    "suggest": {
        "ext-curl": "*",
        "ext-exif": "*",
        "ext-gd": "*",
        "ext-imagick": "*"
    },
    "autoload": {
       "files": [
           "defines.php",
           "functions.php"
       ],
       "classmap": [
            "CImage.php",
            "CHttpGet.php",
            "CRemoteImage.php",
            "CWhitelist.php",
            "CAsciiArt.php",
            "CCache.php",
            "CFastTrackCache.php"
        ]
    }
}


================================================
FILE: defines.php
================================================
<?php
// Version of cimage and img.php
define("CIMAGE_VERSION", "v0.8.6 (2023-10-27)");

// For CRemoteImage
define("CIMAGE_USER_AGENT", "CImage/" . CIMAGE_VERSION);

// Image type IMG_WEBP is only defined from 5.6.25
if (!defined("IMG_WEBP")) {
    define("IMG_WEBP", -1);
}


================================================
FILE: docker-compose.yaml
================================================
version: "3"
services:
    cli:
        image: anax/dev
        volumes: [ ".:/home/anax/repo" ]

    apache:
        image: anax/dev:apache
        volumes: [ ".:/home/anax/repo" ]
        ports: [ "11000:80" ]

    remserver:
        image: anax/dev:apache
        ports:
            - "8090:80"
        volumes: [ ".:/home/anax/repo" ]

    php82:
        image: anax/dev:php82
        volumes: [ ".:/home/anax/repo" ]

    php82-apache:
        image: anax/dev:php82-apache
        ports: [ "11082:80" ]
        volumes: [ ".:/home/anax/repo" ]

    php81:
        image: anax/dev:php81
        volumes: [ ".:/home/anax/repo" ]

    php81-apache:
        image: anax/dev:php81-apache
        ports: [ "11081:80" ]
        volumes: [ ".:/home/anax/repo" ]

    php80:
        image: anax/dev:php80
        volumes: [ ".:/home/anax/repo" ]

    php80-apache:
        image: anax/dev:php80-apache
        ports: [ "11080:80" ]
        volumes: [ ".:/home/anax/repo" ]

    php74:
        image: anax/dev:php74
        volumes: [ ".:/home/anax/repo" ]

    php74-apache:
        image: anax/dev:php74-apache
        ports: [ "11074:80" ]
        volumes: [ ".:/home/anax/repo" ]

    php73:
        image: anax/dev:php73
        volumes: [ ".:/home/anax/repo" ]

    php73-apache:
        image: anax/dev:php73-apache
        ports: [ "11073:80" ]
        volumes: [ ".:/home/anax/repo" ]

    php72:
        image: anax/dev:php72
        volumes: [ ".:/home/anax/repo" ]

    php72-apache:
        image: anax/dev:php72-apache
        ports: [ "11072:80" ]
        volumes: [ ".:/home/anax/repo" ]

    php71:
        image: anax/dev:php71
        volumes: [ ".:/home/anax/repo" ]

    php71-apache:
        image: anax/dev:php71-apache
        ports: [ "11071:80" ]
        volumes: [ ".:/home/anax/repo" ]

    php70:
        image: anax/dev:php70
        volumes: [ ".:/home/anax/repo" ]

    php70:
        image: anax/dev:php70-apache
        ports: [ "11070:80" ]
        volumes: [ ".:/home/anax/repo" ]

    php56:
        image: anax/dev:php56
        volumes: [ ".:/home/anax/repo" ]

    php56:
        image: anax/dev:php56-apache
        ports: [ "11056:80" ]
        volumes: [ ".:/home/anax/repo" ]


================================================
FILE: docs/api/.htaccess
================================================
# Fixes a vulnerability in CentOS: http://stackoverflow.com/questions/20533279/prevent-php-from-parsing-non-php-files-such-as-somefile-php-txt
<FilesMatch \.php\.txt$>
    RemoveHandler .php
    ForceType text/plain
</FilesMatch>

================================================
FILE: docs/api/classes/CAsciiArt.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"/>
    <meta charset="utf-8"/>
    <title>CImage API Documentaion</title>
    <meta name="author" content=""/>
    <meta name="description" content=""/>

    <link href="../css/bootstrap-combined.no-icons.min.css" rel="stylesheet">
    <link href="../css/font-awesome.min.css" rel="stylesheet">
    <link href="../css/prism.css" rel="stylesheet" media="all"/>
    <link href="../css/template.css" rel="stylesheet" media="all"/>
    
    <!--[if lt IE 9]>
    <script src="../js/html5.js"></script>
    <![endif]-->
    <script src="../js/jquery-1.11.0.min.js"></script>
    <script src="../js/ui/1.10.4/jquery-ui.min.js"></script>
    <script src="../js/bootstrap.min.js"></script>
    <script src="../js/jquery.smooth-scroll.js"></script>
    <script src="../js/prism.min.js"></script>
    <!-- TODO: Add http://jscrollpane.kelvinluck.com/ to style the scrollbars for browsers not using webkit-->
    <script type="text/javascript">
    function loadExternalCodeSnippets() {
        Array.prototype.slice.call(document.querySelectorAll('pre[data-src]')).forEach(function (pre) {
            var src = pre.getAttribute('data-src');
            var extension = (src.match(/\.(\w+)$/) || [, ''])[1];
            var language = 'php';

            var code = document.createElement('code');
            code.className = 'language-' + language;

            pre.textContent = '';

            code.textContent = 'Loading…';

            pre.appendChild(code);

            var xhr = new XMLHttpRequest();

            xhr.open('GET', src, true);

            xhr.onreadystatechange = function () {
                if (xhr.readyState == 4) {

                    if (xhr.status < 400 && xhr.responseText) {
                        code.textContent = xhr.responseText;

                        Prism.highlightElement(code);
                    }
                    else if (xhr.status >= 400) {
                        code.textContent = '✖ Error ' + xhr.status + ' while fetching file: ' + xhr.statusText;
                    }
                    else {
                        code.textContent = '✖ Error: File does not exist or is empty';
                    }
                }
            };

            xhr.send(null);
        });
    }

    $(document).ready(function(){
        loadExternalCodeSnippets();
    });
    $('#source-view').on('shown', function () {
        loadExternalCodeSnippets();
    })
</script>

    <link rel="shortcut icon" href="../images/favicon.ico"/>
    <link rel="apple-touch-icon" href="../images/apple-touch-icon.png"/>
    <link rel="apple-touch-icon" sizes="72x72" href="../images/apple-touch-icon-72x72.png"/>
    <link rel="apple-touch-icon" sizes="114x114" href="../images/apple-touch-icon-114x114.png"/>
</head>
<body>

<div class="navbar navbar-fixed-top">
    <div class="navbar-inner">
        <div class="container">
            <a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
                <i class="icon-ellipsis-vertical"></i>
            </a>
            <a class="brand" href="../index.html">CImage API Documentaion</a>

            <div class="nav-collapse">
                <ul class="nav pull-right">
                                        <li class="dropdown" id="charts-menu">
                        <a href="#" class="dropdown-toggle" data-toggle="dropdown">
                            Charts <b class="caret"></b>
                        </a>
                        <ul class="dropdown-menu">
                            <li>
                                <a href="../graphs/class.html">
                                    <i class="icon-list-alt"></i>&#160;Class hierarchy diagram
                                </a>
                            </li>
                        </ul>
                    </li>
                    <li class="dropdown" id="reports-menu">
                        <a href="#" class="dropdown-toggle" data-toggle="dropdown">
                            Reports <b class="caret"></b>
                        </a>
                        <ul class="dropdown-menu">
                            <li>
                                <a href="../reports/errors.html">
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            <i class="icon-list-alt"></i>&#160;Errors <span class="label label-info pull-right">40</span>
                                </a>
                            </li>
                            <li>
                                <a href="../reports/markers.html">
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            <i class="icon-list-alt"></i>&#160;Markers <span class="label label-info pull-right">2</span>
                                </a>
                            </li>
                            <li>
                                <a href="../reports/deprecated.html">
                                                                                                            <i class="icon-list-alt"></i>&#160;Deprecated <span class="label label-info pull-right">0</span>
                                </a>
                            </li>
                        </ul>
                    </li>
                </ul>
            </div>
        </div>
    </div>
    <!--<div class="go_to_top">-->
    <!--<a href="#___" style="color: inherit">Back to top&#160;&#160;<i class="icon-upload icon-white"></i></a>-->
    <!--</div>-->
</div>

<div id="___" class="container-fluid">
        <section class="row-fluid">
        <div class="span2 sidebar">
                                <div class="accordion" style="margin-bottom: 0">
        <div class="accordion-group">
            <div class="accordion-heading">
                                    <a class="accordion-toggle " data-toggle="collapse" data-target="#namespace-1847469476"></a>
                                <a href="../namespaces/default.html" style="margin-left: 30px; padding-left: 0">\</a>
            </div>
            <div id="namespace-1847469476" class="accordion-body collapse in">
                <div class="accordion-inner">

                    
                    <ul>
                                                                                                    <li class="class"><a href="../classes/CAsciiArt.html">CAsciiArt</a></li>
                                                    <li class="class"><a href="../classes/CHttpGet.html">CHttpGet</a></li>
                                                    <li class="class"><a href="../classes/CImage.html">CImage</a></li>
                                                    <li class="class"><a href="../classes/CRemoteImage.html">CRemoteImage</a></li>
                                                    <li class="class"><a href="../classes/CWhitelist.html">CWhitelist</a></li>
                                            </ul>
                </div>
            </div>
        </div>
    </div>

        </div>
    </section>
    <section class="row-fluid">
        <div class="span10 offset2">
            <div class="row-fluid">
                <div class="span8 content class">
                    <nav>
                                                <a href="../namespaces/default.html">\</a> <i class="icon-level-up"></i>
                                            </nav>
                    <a href="#source-view" role="button" class="pull-right btn" data-toggle="modal"><i class="icon-code"></i></a>

                    <h1><small>\</small>CAsciiArt</h1>
                    <p><em>Create an ASCII version of an image.</em></p>
                    
                    
                                        
                    <section id="summary">
                        <h2>Summary</h2>
                        <section class="row-fluid heading">
                            <section class="span4">
                                <a href="#methods">Methods</a>
                            </section>
                            <section class="span4">
                                <a href="#properties">Properties</a>
                            </section>
                            <section class="span4">
                                <a href="#constants">Constants</a>
                            </section>
                        </section>
                        <section class="row-fluid public">
                            <section class="span4">
                                                                    <a href="../classes/CAsciiArt.html#method___construct" class="">__construct()</a><br />
                                                                    <a href="../classes/CAsciiArt.html#method_addCharacterSet" class="">addCharacterSet()</a><br />
                                                                    <a href="../classes/CAsciiArt.html#method_setOptions" class="">setOptions()</a><br />
                                                                    <a href="../classes/CAsciiArt.html#method_createFromFile" class="">createFromFile()</a><br />
                                                                    <a href="../classes/CAsciiArt.html#method_luminanceAreaAverage" class="">luminanceAreaAverage()</a><br />
                                                                    <a href="../classes/CAsciiArt.html#method_getLuminance" class="">getLuminance()</a><br />
                                                                    <a href="../classes/CAsciiArt.html#method_luminance2character" class="">luminance2character()</a><br />
                                                            </section>
                            <section class="span4">
                                                                    <em>No public properties found</em>
                                                            </section>
                            <section class="span4">
                                                                    <em>No constants found</em>
                                                            </section>
                        </section>
                        <section class="row-fluid protected">
                            <section class="span4">
                                                                    <em>No protected methods found</em>
                                                            </section>
                            <section class="span4">
                                                                    <em>No protected properties found</em>
                                                            </section>
                            <section class="span4">
                                <em>N/A</em>
                            </section>
                        </section>
                        <section class="row-fluid private">
                            <section class="span4">
                                                                    <em>No private methods found</em>
                                                            </section>
                            <section class="span4">
                                                                    <a href="../classes/CAsciiArt.html#property_characterSet" class="">$characterSet</a><br />
                                                                    <a href="../classes/CAsciiArt.html#property_characters" class="">$characters</a><br />
                                                                    <a href="../classes/CAsciiArt.html#property_charCount" class="">$charCount</a><br />
                                                                    <a href="../classes/CAsciiArt.html#property_scale" class="">$scale</a><br />
                                                                    <a href="../classes/CAsciiArt.html#property_luminanceStrategy" class="">$luminanceStrategy</a><br />
                                                            </section>
                            <section class="span4">
                                <em>N/A</em>
                            </section>
                        </section>
                    </section>
                </div>
                <aside class="span4 detailsbar">
                                        
                    
                    <dl>
                        <dt>File</dt>
                            <dd><a href="../files/CAsciiArt.html"><div class="path-wrapper">CAsciiArt.php</div></a></dd>
                                                <dt>Package</dt>
                            <dd><div class="namespace-wrapper">Default</div></dd>
                                                <dt>Class hierarchy</dt>
                            <dd class="hierarchy">
                                                                                                                                                                    <div class="namespace-wrapper">\CAsciiArt</div>
                            </dd>

                        
                        
                        
                        
                                                                        </dl>
                    <h2>Tags</h2>
                    <table class="table table-condensed">
                                            <tr><td colspan="2"><em>None found</em></td></tr>
                                        </table>
                </aside>
            </div>

                        
                                    <a id="properties" name="properties"></a>
            <div class="row-fluid">
                <div class="span8 content class">
                    <h2>Properties</h2>
                </div>
                <aside class="span4 detailsbar"></aside>
            </div>

                                                                    <div class="row-fluid">
        <div class="span8 content class">
            <a id="property_characterSet" name="property_characterSet" class="anchor"></a>
            <article class="property">
                <h3 class="private ">$characterSet</h3>
                <pre class="signature">$characterSet : </pre>
                <p><em>Character set to use.</em></p>
                

                                <h4>Type</h4>
                
                                            </article>
        </div>
        <aside class="span4 detailsbar">
            <h1><i class="icon-arrow-down"></i></h1>
                        <dl>
                                                            </dl>
            <h2>Tags</h2>
            <table class="table table-condensed">
                                    <tr><td colspan="2"><em>None found</em></td></tr>
                            </table>
        </aside>
    </div>

                                    <div class="row-fluid">
        <div class="span8 content class">
            <a id="property_characters" name="property_characters" class="anchor"></a>
            <article class="property">
                <h3 class="private ">$characters</h3>
                <pre class="signature">$characters : </pre>
                <p><em>Current character set.</em></p>
                

                                <h4>Type</h4>
                
                                            </article>
        </div>
        <aside class="span4 detailsbar">
            <h1><i class="icon-arrow-down"></i></h1>
    
Download .txt
gitextract_499basvb/

├── .gitignore
├── .scrutinizer.yml
├── .travis.yml
├── CAsciiArt.php
├── CCache.php
├── CFastTrackCache.php
├── CHttpGet.php
├── CImage.php
├── CRemoteImage.php
├── CWhitelist.php
├── LICENSE.txt
├── README.md
├── REVISION.md
├── SECURITY.md
├── autoload.php
├── bin/
│   ├── cache.bash
│   └── create-img-single.bash
├── cache/
│   └── .gitignore
├── composer.json
├── defines.php
├── docker-compose.yaml
├── docs/
│   └── api/
│       ├── .htaccess
│       ├── classes/
│       │   ├── CAsciiArt.html
│       │   ├── CHttpGet.html
│       │   ├── CImage.html
│       │   ├── CImage_RemoteDownloadTest.html
│       │   ├── CRemoteImage.html
│       │   ├── CWhitelist.html
│       │   └── CWhitelistTest.html
│       ├── css/
│       │   ├── jquery.iviewer.css
│       │   ├── phpdocumentor-clean-icons/
│       │   │   ├── Read Me.txt
│       │   │   ├── lte-ie7.js
│       │   │   └── style.css
│       │   ├── prism.css
│       │   └── template.css
│       ├── files/
│       │   ├── CAsciiArt.html
│       │   ├── CAsciiArt.php.txt
│       │   ├── CHttpGet.html
│       │   ├── CHttpGet.php.txt
│       │   ├── CImage.html
│       │   ├── CImage.php.txt
│       │   ├── CRemoteImage.html
│       │   ├── CRemoteImage.php.txt
│       │   ├── CWhitelist.html
│       │   ├── CWhitelist.php.txt
│       │   ├── autoload.html
│       │   ├── autoload.php.txt
│       │   ├── test%2FCImage_RemoteDownloadTest.php.txt
│       │   ├── test%2FCWhitelistTest.php.txt
│       │   ├── test%2Fconfig.php.txt
│       │   ├── test.CImage_RemoteDownloadTest.html
│       │   ├── test.CWhitelistTest.html
│       │   ├── test.config.html
│       │   ├── webroot/
│       │   │   ├── img.php.txt
│       │   │   └── img_config.php.txt
│       │   ├── webroot%2Fcheck_system.php.txt
│       │   ├── webroot%2Fcompare%2Fcompare-test.php.txt
│       │   ├── webroot%2Fcompare%2Fcompare.php.txt
│       │   ├── webroot%2Fimg.php.txt
│       │   ├── webroot%2Fimg_config.php.txt
│       │   ├── webroot%2Fimg_header.php.txt
│       │   ├── webroot%2Fimgd.php.txt
│       │   ├── webroot%2Fimgp.php.txt
│       │   ├── webroot%2Fimgs.php.txt
│       │   ├── webroot%2Ftest%2Fconfig.php.txt
│       │   ├── webroot%2Ftest%2Ftemplate.php.txt
│       │   ├── webroot%2Ftest%2Ftest.php.txt
│       │   ├── webroot%2Ftest%2Ftest_issue29.php.txt
│       │   ├── webroot%2Ftest%2Ftest_issue36_aro.php.txt
│       │   ├── webroot%2Ftest%2Ftest_issue36_rb-ra-180.php.txt
│       │   ├── webroot%2Ftest%2Ftest_issue36_rb-ra-270.php.txt
│       │   ├── webroot%2Ftest%2Ftest_issue36_rb-ra-45.php.txt
│       │   ├── webroot%2Ftest%2Ftest_issue36_rb-ra-90.php.txt
│       │   ├── webroot%2Ftest%2Ftest_issue38.php.txt
│       │   ├── webroot%2Ftest%2Ftest_issue40.php.txt
│       │   ├── webroot%2Ftest%2Ftest_issue49.php.txt
│       │   ├── webroot%2Ftest%2Ftest_issue52-cf.php.txt
│       │   ├── webroot%2Ftest%2Ftest_issue52-stretch.php.txt
│       │   ├── webroot%2Ftest%2Ftest_issue52.php.txt
│       │   ├── webroot%2Ftest%2Ftest_issue58.php.txt
│       │   ├── webroot%2Ftest%2Ftest_issue60.php.txt
│       │   ├── webroot%2Ftest%2Ftest_option-crop.php.txt
│       │   ├── webroot%2Ftest%2Ftest_option-no-upscale.php.txt
│       │   ├── webroot%2Ftest%2Ftest_option-save-as.php.txt
│       │   ├── webroot.check_system.html
│       │   ├── webroot.compare.compare-test.html
│       │   ├── webroot.compare.compare.html
│       │   ├── webroot.img.html
│       │   ├── webroot.img_config.html
│       │   ├── webroot.img_header.html
│       │   ├── webroot.imgd.html
│       │   ├── webroot.imgp.html
│       │   ├── webroot.imgs.html
│       │   ├── webroot.test.config.html
│       │   ├── webroot.test.template.html
│       │   ├── webroot.test.test.html
│       │   ├── webroot.test.test_issue29.html
│       │   ├── webroot.test.test_issue36_aro.html
│       │   ├── webroot.test.test_issue36_rb-ra-180.html
│       │   ├── webroot.test.test_issue36_rb-ra-270.html
│       │   ├── webroot.test.test_issue36_rb-ra-45.html
│       │   ├── webroot.test.test_issue36_rb-ra-90.html
│       │   ├── webroot.test.test_issue38.html
│       │   ├── webroot.test.test_issue40.html
│       │   ├── webroot.test.test_issue49.html
│       │   ├── webroot.test.test_issue52-cf.html
│       │   ├── webroot.test.test_issue52-stretch.html
│       │   ├── webroot.test.test_issue52.html
│       │   ├── webroot.test.test_issue58.html
│       │   ├── webroot.test.test_issue60.html
│       │   ├── webroot.test.test_option-crop.html
│       │   ├── webroot.test.test_option-no-upscale.html
│       │   └── webroot.test.test_option-save-as.html
│       ├── font/
│       │   └── FontAwesome.otf
│       ├── graphs/
│       │   └── class.html
│       ├── images/
│       │   └── iviewer/
│       │       ├── grab.cur
│       │       └── hand.cur
│       ├── index.html
│       ├── js/
│       │   ├── html5.js
│       │   ├── jquery.dotdotdot-1.5.9.js
│       │   ├── jquery.iviewer.js
│       │   ├── jquery.mousewheel.js
│       │   └── jquery.smooth-scroll.js
│       ├── namespaces/
│       │   └── default.html
│       └── reports/
│           ├── deprecated.html
│           ├── errors.html
│           └── markers.html
├── functions.php
├── icc/
│   └── sRGB_IEC61966-2-1_black_scaled.icc
├── phpcs.xml
├── phpdoc.xml
├── phpunit.xml
├── test/
│   ├── CCacheTest.php
│   ├── CImageDummyTest.php
│   ├── CImageRemoteDownloadTest.php
│   ├── CImageSRGBTest.php
│   ├── CWhitelistTest.php
│   └── config.php
└── webroot/
    ├── check_system.php
    ├── compare/
    │   ├── compare-test.php
    │   ├── compare.php
    │   └── issue117-PNG24.php
    ├── htaccess
    ├── img/
    │   ├── car-gif
    │   ├── car-jpg
    │   ├── car-png
    │   └── lena.tif
    ├── img.php
    ├── img_config.php
    ├── img_header.php
    ├── imgd.php
    ├── imgf.php
    ├── imgp.php
    ├── imgs.php
    ├── js/
    │   └── cimage.js
    ├── test/
    │   ├── config.php
    │   ├── template.php
    │   ├── test.php
    │   ├── test_issue101-dummy.php
    │   ├── test_issue29.php
    │   ├── test_issue36_aro.php
    │   ├── test_issue36_rb-ra-180.php
    │   ├── test_issue36_rb-ra-270.php
    │   ├── test_issue36_rb-ra-45.php
    │   ├── test_issue36_rb-ra-90.php
    │   ├── test_issue38.php
    │   ├── test_issue40.php
    │   ├── test_issue49.php
    │   ├── test_issue52-cf.php
    │   ├── test_issue52-stretch.php
    │   ├── test_issue52.php
    │   ├── test_issue58.php
    │   ├── test_issue60.php
    │   ├── test_issue85.php
    │   ├── test_option-crop.php
    │   ├── test_option-no-upscale.php
    │   └── test_option-save-as.php
    └── tests.php
Download .txt
SYMBOL INDEX (559 symbols across 24 files)

FILE: CAsciiArt.php
  class CAsciiArt (line 6) | class CAsciiArt
    method __construct (line 50) | public function __construct()
    method addCharacterSet (line 65) | public function addCharacterSet($key, $value)
    method setOptions (line 80) | public function setOptions($options = array())
    method createFromFile (line 112) | public function createFromFile($filename)
    method luminanceAreaAverage (line 147) | public function luminanceAreaAverage($img, $x1, $y1, $x2, $y2)
    method getLuminance (line 176) | public function getLuminance($red, $green, $blue)
    method luminance2character (line 206) | public function luminance2character($luminance)

FILE: CCache.php
  class CCache (line 6) | class CCache
    method setDir (line 24) | public function setDir($path)
    method getPathToSubdir (line 46) | public function getPathToSubdir($subdir, $create = true)
    method getStatusOfSubdir (line 83) | public function getStatusOfSubdir($subdir)
    method removeSubdir (line 106) | public function removeSubdir($subdir)

FILE: CFastTrackCache.php
  class CFastTrackCache (line 6) | class CFastTrackCache
    method enable (line 43) | public function enable($enabled)
    method setCacheDir (line 60) | public function setCacheDir($path)
    method setFilename (line 81) | public function setFilename($clear)
    method addHeader (line 111) | public function addHeader($header)
    method addHeaderOnOutput (line 126) | public function addHeaderOnOutput($header)
    method setSource (line 141) | public function setSource($source)
    method setLastModified (line 156) | public function setLastModified($lastModified)
    method getFilename (line 169) | public function getFilename()
    method writeToCache (line 181) | public function writeToCache()
    method output (line 202) | public function output()

FILE: CHttpGet.php
  class CHttpGet (line 6) | class CHttpGet
    method __construct (line 17) | public function __construct()
    method buildUrl (line 33) | public function buildUrl($baseUrl, $merge)
    method setUrl (line 58) | public function setUrl($url)
    method setHeader (line 86) | public function setHeader($field, $value)
    method parseHeader (line 102) | public function parseHeader()
    method doGet (line 139) | public function doGet($debug = false)
    method getStatus (line 186) | public function getStatus()
    method getLastModified (line 200) | public function getLastModified()
    method getContentType (line 214) | public function getContentType()
    method getDate (line 236) | public function getDate($default = false)
    method getMaxAge (line 253) | public function getMaxAge($default = false)
    method getBody (line 286) | public function getBody()

FILE: CImage.php
  class CImage (line 9) | #[AllowDynamicProperties]
    method __construct (line 470) | public function __construct($imageSrc = null, $imageFolder = null, $sa...
    method injectDependency (line 486) | public function injectDependency($property, $object)
    method setVerbose (line 505) | public function setVerbose($mode = true)
    method setSaveFolder (line 522) | public function setSaveFolder($path)
    method useCache (line 537) | public function useCache($use = true)
    method createDummyImage (line 554) | public function createDummyImage($width = null, $height = null)
    method setRemoteDownload (line 575) | public function setRemoteDownload($allow, $cache, $pattern = null)
    method isRemoteSource (line 600) | public function isRemoteSource($src)
    method setRemoteHostWhitelist (line 617) | public function setRemoteHostWhitelist($whitelist = null)
    method isRemoteSourceOnWhitelist (line 637) | public function isRemoteSourceOnWhitelist($src)
    method checkFileExtension (line 664) | private function checkFileExtension($extension)
    method normalizeFileExtension (line 683) | private function normalizeFileExtension($extension = "")
    method downloadRemoteSource (line 703) | public function downloadRemoteSource($src)
    method setSource (line 736) | public function setSource($src, $dir = null)
    method setTarget (line 772) | public function setTarget($src = null, $dir = null)
    method getTarget (line 799) | public function getTarget()
    method setOptions (line 813) | public function setOptions($args)
    method mapFilter (line 930) | private function mapFilter($name)
    method loadImageDetails (line 964) | public function loadImageDetails($file = null)
    method getMimeType (line 1015) | protected function getMimeType()
    method initDimensions (line 1033) | public function initDimensions()
    method calculateNewWidthAndHeight (line 1108) | public function calculateNewWidthAndHeight()
    method reCalculateDimensions (line 1259) | public function reCalculateDimensions()
    method setSaveAsExtension (line 1282) | public function setSaveAsExtension($saveAs = null)
    method setJpegQuality (line 1305) | public function setJpegQuality($quality = null)
    method setPngCompression (line 1332) | public function setPngCompression($compress = null)
    method useOriginalIfPossible (line 1359) | public function useOriginalIfPossible($useOrig = true)
    method generateFilename (line 1403) | public function generateFilename($base = null, $useSubdir = true, $pre...
    method useCacheIfPossible (line 1498) | public function useCacheIfPossible($useCache = true)
    method load (line 1536) | public function load($src = null, $dir = null)
    method getPngType (line 1590) | public function getPngType($filename = null)
    method getPngTypeAsString (line 1615) | private function getPngTypeAsString($pngType = null, $filename = null)
    method colorsTotal (line 1666) | private function colorsTotal($im)
    method preResize (line 1692) | public function preResize()
    method setCopyResizeStrategy (line 1734) | public function setCopyResizeStrategy($strategy)
    method imageCopyResampled (line 1747) | public function imageCopyResampled($dst_image, $src_image, $dst_x, $ds...
    method resize (line 1765) | public function resize()
    method postResize (line 1945) | public function postResize()
    method rotate (line 2029) | public function rotate($angle, $bgColor)
    method rotateExif (line 2051) | public function rotateExif()
    method trueColorToPalette (line 2095) | public function trueColorToPalette()
    method sharpenImage (line 2120) | public function sharpenImage()
    method embossImage (line 2133) | public function embossImage()
    method blurImage (line 2146) | public function blurImage()
    method createConvolveArguments (line 2162) | public function createConvolveArguments($expression)
    method addConvolveExpressions (line 2207) | public function addConvolveExpressions($options)
    method imageConvolution (line 2222) | public function imageConvolution($options = null)
    method setDefaultBackgroundColor (line 2250) | public function setDefaultBackgroundColor($color)
    method getBackgroundColor (line 2302) | private function getBackgroundColor($img = null)
    method createImageKeepTransparency (line 2336) | private function createImageKeepTransparency($width, $height)
    method setPostProcessingOptions (line 2375) | public function setPostProcessingOptions($options)
    method getTargetImageExtension (line 2413) | protected function getTargetImageExtension()
    method save (line 2436) | public function save($src = null, $base = null, $overwrite = true)
    method convert2sRGBColorSpace (line 2575) | public function convert2sRGBColorSpace($src, $dir, $cache, $iccFile, $...
    method linkToCacheFile (line 2638) | public function linkToCacheFile($alias)
    method addHTTPHeader (line 2670) | public function addHTTPHeader($type, $value)
    method output (line 2687) | public function output($file = null, $format = null)
    method json (line 2790) | public function json($file = null)
    method setAsciiOptions (line 2845) | public function setAsciiOptions($options = array())
    method ascii (line 2859) | public function ascii($file = null)
    method log (line 2877) | public function log($message)
    method setVerboseToFile (line 2895) | public function setVerboseToFile($fileName)
    method verboseOutput (line 2908) | private function verboseOutput()
    method raiseError (line 2952) | private function raiseError($message)

FILE: CRemoteImage.php
  class CRemoteImage (line 6) | class CRemoteImage
    method getStatus (line 76) | public function getStatus()
    method getDetails (line 88) | public function getDetails()
    method setCache (line 102) | public function setCache($path)
    method isCacheWritable (line 117) | public function isCacheWritable()
    method useCache (line 135) | public function useCache($use = true)
    method setHeaderFields (line 148) | public function setHeaderFields()
    method save (line 173) | public function save()
    method updateCacheDetails (line 210) | public function updateCacheDetails()
    method download (line 238) | public function download($url)
    method loadCacheDetails (line 278) | public function loadCacheDetails()
    method getCachedSource (line 295) | public function getCachedSource()

FILE: CWhitelist.php
  class CWhitelist (line 6) | class CWhitelist
    method set (line 24) | public function set($whitelist = array())
    method check (line 44) | public function check($item, $whitelist = null)

FILE: docs/api/css/phpdocumentor-clean-icons/lte-ie7.js
  function addIcon (line 4) | function addIcon(el, entity) {

FILE: docs/api/js/html5.js
  function m (line 4) | function m(){var a=e.elements;return"string"==typeof a?a.split(" "):a}
  function i (line 4) | function i(a){var b=n[a[o]];b||(b={},h++,a[o]=h,n[h]=b);return b}
  function p (line 4) | function p(a,b,c){b||(b=f);if(g)return b.createElement(a);c||(c=i(b));b=...
  function t (line 4) | function t(a,b){if(!b.cache)b.cache={},b.createElem=a.createElement,b.cr...
  function q (line 5) | function q(a){a||(a=f);var b=i(a);if(e.shivCSS&&!j&&!b.hasCSS){var c,d=a...

FILE: docs/api/js/jquery.dotdotdot-1.5.9.js
  function children (line 280) | function children( $elem, o, after )
  function ellipsis (line 311) | function ellipsis( $elem, $d, $i, o, after )
  function ellipsisElement (line 362) | function ellipsisElement( $e, $d, $i, o, after )
  function test (line 441) | function test( $i, o )
  function addEllipsis (line 445) | function addEllipsis( txt, o )
  function getSizes (line 457) | function getSizes( $d )
  function setTextContent (line 464) | function setTextContent( e, content )
  function getTextContent (line 480) | function getTextContent( e )
  function getElement (line 499) | function getElement( e, $i )
  function getTrueInnerHeight (line 524) | function getTrueInnerHeight( $el )
  function debug (line 539) | function debug( d, m )

FILE: docs/api/js/jquery.iviewer.js
  function makeMouseEvent (line 28) | function makeMouseEvent (event) {
  function div (line 667) | function div(val1,val2) { return val1 / val2 }
  function mul (line 668) | function mul(val1,val2) { return val1 * val2 }

FILE: docs/api/js/jquery.mousewheel.js
  function handler (line 84) | function handler(event) {
  function nullLowestDelta (line 186) | function nullLowestDelta() {
  function shouldAdjustOldDeltas (line 190) | function shouldAdjustOldDeltas(orgEvent, absDelta) {

FILE: docs/api/js/jquery.smooth-scroll.js
  function filterPath (line 2) | function filterPath(string) {

FILE: functions.php
  function trace (line 15) | function trace($msg)
  function errorPage (line 39) | function errorPage($msg, $type = 500)
  function get (line 78) | function get($key, $default = null)
  function getDefined (line 103) | function getDefined($key, $defined, $undefined)
  function getValue (line 118) | function getValue($key, $undefined)
  function getConfig (line 137) | function getConfig($key, $default)
  function verbose (line 154) | function verbose($msg = null, $arg = "")
  function checkExternalCommand (line 187) | function checkExternalCommand($what, $enabled, $commandString)

FILE: test/CCacheTest.php
  class CCacheTest (line 6) | class CCacheTest extends \PHPUnit_Framework_TestCase
    method testSetCacheDir (line 13) | public function testSetCacheDir()
    method testSetWrongCacheDir (line 32) | public function testSetWrongCacheDir()
    method testCreateSubdir (line 45) | public function testCreateSubdir()

FILE: test/CImageDummyTest.php
  class CImageDummyTest (line 6) | class CImageDummyTest extends \PHPUnit_Framework_TestCase
    method setUp (line 18) | protected function setUp()
    method removeFilesInCacheDir (line 32) | protected function removeFilesInCacheDir()
    method tearDown (line 49) | protected function tearDown()
    method testCreate1 (line 64) | public function testCreate1()
    method testCreate2 (line 86) | public function testCreate2()

FILE: test/CImageRemoteDownloadTest.php
  class CImageRemoteDownloadTest (line 6) | class CImageRemoteDownloadTest extends \PHPUnit_Framework_TestCase
    method providerValidRemoteSource (line 24) | public function providerValidRemoteSource()
    method providerInvalidRemoteSource (line 41) | public function providerInvalidRemoteSource()
    method testAllowRemoteDownloadDefaultPatternValid (line 61) | public function testAllowRemoteDownloadDefaultPatternValid($source)
    method testAllowRemoteDownloadDefaultPatternInvalid (line 79) | public function testAllowRemoteDownloadDefaultPatternInvalid($source)
    method providerHostnameMatch (line 95) | public function providerHostnameMatch()
    method testRemoteHostWhitelistMatch (line 118) | public function testRemoteHostWhitelistMatch($hostname)
    method providerHostnameNoMatch (line 134) | public function providerHostnameNoMatch()
    method testRemoteHostWhitelistNoMatch (line 157) | public function testRemoteHostWhitelistNoMatch($hostname)
    method testRemoteHostWhitelistNotConfigured (line 174) | public function testRemoteHostWhitelistNotConfigured()

FILE: test/CImageSRGBTest.php
  class CImageSRGBTest (line 6) | class CImageSRGBTest extends \PHPUnit_Framework_TestCase
    method setUp (line 19) | protected function setUp()
    method testCreate1 (line 36) | public function testCreate1()
    method testCreate2 (line 61) | public function testCreate2()

FILE: test/CWhitelistTest.php
  class CWhitelistTest (line 6) | class CWhitelistTest extends \PHPUnit_Framework_TestCase
    method providerHostnameMatch (line 24) | public function providerHostnameMatch()
    method providerHostnameNoMatch (line 42) | public function providerHostnameNoMatch()
    method testRemoteHostWhitelistMatch (line 65) | public function testRemoteHostWhitelistMatch($hostname)
    method testRemoteHostWhitelistNoMatch (line 86) | public function testRemoteHostWhitelistNoMatch($hostname)
    method testInvalidFormat (line 105) | public function testInvalidFormat()
    method testCheckEmpty (line 119) | public function testCheckEmpty()

FILE: webroot/compare/compare.php
  function e (line 7) | function e($str) {

FILE: webroot/imgd.php
  function trace (line 66) | function trace($msg)
  function errorPage (line 90) | function errorPage($msg, $type = 500)
  function get (line 129) | function get($key, $default = null)
  function getDefined (line 154) | function getDefined($key, $defined, $undefined)
  function getValue (line 169) | function getValue($key, $undefined)
  function getConfig (line 188) | function getConfig($key, $default)
  function verbose (line 205) | function verbose($msg = null, $arg = "")
  function checkExternalCommand (line 238) | function checkExternalCommand($what, $enabled, $commandString)
  class CHttpGet (line 256) | class CHttpGet
    method __construct (line 267) | public function __construct()
    method buildUrl (line 283) | public function buildUrl($baseUrl, $merge)
    method setUrl (line 308) | public function setUrl($url)
    method setHeader (line 336) | public function setHeader($field, $value)
    method parseHeader (line 352) | public function parseHeader()
    method doGet (line 389) | public function doGet($debug = false)
    method getStatus (line 436) | public function getStatus()
    method getLastModified (line 450) | public function getLastModified()
    method getContentType (line 464) | public function getContentType()
    method getDate (line 486) | public function getDate($default = false)
    method getMaxAge (line 503) | public function getMaxAge($default = false)
    method getBody (line 536) | public function getBody()
  class CRemoteImage (line 548) | class CRemoteImage
    method getStatus (line 618) | public function getStatus()
    method getDetails (line 630) | public function getDetails()
    method setCache (line 644) | public function setCache($path)
    method isCacheWritable (line 659) | public function isCacheWritable()
    method useCache (line 677) | public function useCache($use = true)
    method setHeaderFields (line 690) | public function setHeaderFields()
    method save (line 715) | public function save()
    method updateCacheDetails (line 752) | public function updateCacheDetails()
    method download (line 780) | public function download($url)
    method loadCacheDetails (line 820) | public function loadCacheDetails()
    method getCachedSource (line 837) | public function getCachedSource()
  class CWhitelist (line 865) | class CWhitelist
    method set (line 883) | public function set($whitelist = array())
    method check (line 903) | public function check($item, $whitelist = null)
  class CAsciiArt (line 929) | class CAsciiArt
    method __construct (line 973) | public function __construct()
    method addCharacterSet (line 988) | public function addCharacterSet($key, $value)
    method setOptions (line 1003) | public function setOptions($options = array())
    method createFromFile (line 1035) | public function createFromFile($filename)
    method luminanceAreaAverage (line 1070) | public function luminanceAreaAverage($img, $x1, $y1, $x2, $y2)
    method getLuminance (line 1099) | public function getLuminance($red, $green, $blue)
    method luminance2character (line 1129) | public function luminance2character($luminance)
  class CImage (line 1146) | #[AllowDynamicProperties]
    method __construct (line 1607) | public function __construct($imageSrc = null, $imageFolder = null, $sa...
    method injectDependency (line 1623) | public function injectDependency($property, $object)
    method setVerbose (line 1642) | public function setVerbose($mode = true)
    method setSaveFolder (line 1659) | public function setSaveFolder($path)
    method useCache (line 1674) | public function useCache($use = true)
    method createDummyImage (line 1691) | public function createDummyImage($width = null, $height = null)
    method setRemoteDownload (line 1712) | public function setRemoteDownload($allow, $cache, $pattern = null)
    method isRemoteSource (line 1737) | public function isRemoteSource($src)
    method setRemoteHostWhitelist (line 1754) | public function setRemoteHostWhitelist($whitelist = null)
    method isRemoteSourceOnWhitelist (line 1774) | public function isRemoteSourceOnWhitelist($src)
    method checkFileExtension (line 1801) | private function checkFileExtension($extension)
    method normalizeFileExtension (line 1820) | private function normalizeFileExtension($extension = "")
    method downloadRemoteSource (line 1840) | public function downloadRemoteSource($src)
    method setSource (line 1873) | public function setSource($src, $dir = null)
    method setTarget (line 1909) | public function setTarget($src = null, $dir = null)
    method getTarget (line 1936) | public function getTarget()
    method setOptions (line 1950) | public function setOptions($args)
    method mapFilter (line 2067) | private function mapFilter($name)
    method loadImageDetails (line 2101) | public function loadImageDetails($file = null)
    method getMimeType (line 2152) | protected function getMimeType()
    method initDimensions (line 2170) | public function initDimensions()
    method calculateNewWidthAndHeight (line 2245) | public function calculateNewWidthAndHeight()
    method reCalculateDimensions (line 2396) | public function reCalculateDimensions()
    method setSaveAsExtension (line 2419) | public function setSaveAsExtension($saveAs = null)
    method setJpegQuality (line 2442) | public function setJpegQuality($quality = null)
    method setPngCompression (line 2469) | public function setPngCompression($compress = null)
    method useOriginalIfPossible (line 2496) | public function useOriginalIfPossible($useOrig = true)
    method generateFilename (line 2540) | public function generateFilename($base = null, $useSubdir = true, $pre...
    method useCacheIfPossible (line 2635) | public function useCacheIfPossible($useCache = true)
    method load (line 2673) | public function load($src = null, $dir = null)
    method getPngType (line 2727) | public function getPngType($filename = null)
    method getPngTypeAsString (line 2752) | private function getPngTypeAsString($pngType = null, $filename = null)
    method colorsTotal (line 2803) | private function colorsTotal($im)
    method preResize (line 2829) | public function preResize()
    method setCopyResizeStrategy (line 2871) | public function setCopyResizeStrategy($strategy)
    method imageCopyResampled (line 2884) | public function imageCopyResampled($dst_image, $src_image, $dst_x, $ds...
    method resize (line 2902) | public function resize()
    method postResize (line 3082) | public function postResize()
    method rotate (line 3166) | public function rotate($angle, $bgColor)
    method rotateExif (line 3188) | public function rotateExif()
    method trueColorToPalette (line 3232) | public function trueColorToPalette()
    method sharpenImage (line 3257) | public function sharpenImage()
    method embossImage (line 3270) | public function embossImage()
    method blurImage (line 3283) | public function blurImage()
    method createConvolveArguments (line 3299) | public function createConvolveArguments($expression)
    method addConvolveExpressions (line 3344) | public function addConvolveExpressions($options)
    method imageConvolution (line 3359) | public function imageConvolution($options = null)
    method setDefaultBackgroundColor (line 3387) | public function setDefaultBackgroundColor($color)
    method getBackgroundColor (line 3439) | private function getBackgroundColor($img = null)
    method createImageKeepTransparency (line 3473) | private function createImageKeepTransparency($width, $height)
    method setPostProcessingOptions (line 3512) | public function setPostProcessingOptions($options)
    method getTargetImageExtension (line 3550) | protected function getTargetImageExtension()
    method save (line 3573) | public function save($src = null, $base = null, $overwrite = true)
    method convert2sRGBColorSpace (line 3712) | public function convert2sRGBColorSpace($src, $dir, $cache, $iccFile, $...
    method linkToCacheFile (line 3775) | public function linkToCacheFile($alias)
    method addHTTPHeader (line 3807) | public function addHTTPHeader($type, $value)
    method output (line 3824) | public function output($file = null, $format = null)
    method json (line 3927) | public function json($file = null)
    method setAsciiOptions (line 3982) | public function setAsciiOptions($options = array())
    method ascii (line 3996) | public function ascii($file = null)
    method log (line 4014) | public function log($message)
    method setVerboseToFile (line 4032) | public function setVerboseToFile($fileName)
    method verboseOutput (line 4045) | private function verboseOutput()
    method raiseError (line 4089) | private function raiseError($message)
  class CCache (line 4101) | class CCache
    method setDir (line 4119) | public function setDir($path)
    method getPathToSubdir (line 4141) | public function getPathToSubdir($subdir, $create = true)
    method getStatusOfSubdir (line 4178) | public function getStatusOfSubdir($subdir)
    method removeSubdir (line 4201) | public function removeSubdir($subdir)
  class CFastTrackCache (line 4219) | class CFastTrackCache
    method enable (line 4256) | public function enable($enabled)
    method setCacheDir (line 4273) | public function setCacheDir($path)
    method setFilename (line 4294) | public function setFilename($clear)
    method addHeader (line 4324) | public function addHeader($header)
    method addHeaderOnOutput (line 4339) | public function addHeaderOnOutput($header)
    method setSource (line 4354) | public function setSource($source)
    method setLastModified (line 4369) | public function setLastModified($lastModified)
    method getFilename (line 4382) | public function getFilename()
    method writeToCache (line 4394) | public function writeToCache()
    method output (line 4415) | public function output()

FILE: webroot/imgp.php
  function trace (line 66) | function trace($msg)
  function errorPage (line 90) | function errorPage($msg, $type = 500)
  function get (line 129) | function get($key, $default = null)
  function getDefined (line 154) | function getDefined($key, $defined, $undefined)
  function getValue (line 169) | function getValue($key, $undefined)
  function getConfig (line 188) | function getConfig($key, $default)
  function verbose (line 205) | function verbose($msg = null, $arg = "")
  function checkExternalCommand (line 238) | function checkExternalCommand($what, $enabled, $commandString)
  class CHttpGet (line 256) | class CHttpGet
    method __construct (line 267) | public function __construct()
    method buildUrl (line 283) | public function buildUrl($baseUrl, $merge)
    method setUrl (line 308) | public function setUrl($url)
    method setHeader (line 336) | public function setHeader($field, $value)
    method parseHeader (line 352) | public function parseHeader()
    method doGet (line 389) | public function doGet($debug = false)
    method getStatus (line 436) | public function getStatus()
    method getLastModified (line 450) | public function getLastModified()
    method getContentType (line 464) | public function getContentType()
    method getDate (line 486) | public function getDate($default = false)
    method getMaxAge (line 503) | public function getMaxAge($default = false)
    method getBody (line 536) | public function getBody()
  class CRemoteImage (line 548) | class CRemoteImage
    method getStatus (line 618) | public function getStatus()
    method getDetails (line 630) | public function getDetails()
    method setCache (line 644) | public function setCache($path)
    method isCacheWritable (line 659) | public function isCacheWritable()
    method useCache (line 677) | public function useCache($use = true)
    method setHeaderFields (line 690) | public function setHeaderFields()
    method save (line 715) | public function save()
    method updateCacheDetails (line 752) | public function updateCacheDetails()
    method download (line 780) | public function download($url)
    method loadCacheDetails (line 820) | public function loadCacheDetails()
    method getCachedSource (line 837) | public function getCachedSource()
  class CWhitelist (line 865) | class CWhitelist
    method set (line 883) | public function set($whitelist = array())
    method check (line 903) | public function check($item, $whitelist = null)
  class CAsciiArt (line 929) | class CAsciiArt
    method __construct (line 973) | public function __construct()
    method addCharacterSet (line 988) | public function addCharacterSet($key, $value)
    method setOptions (line 1003) | public function setOptions($options = array())
    method createFromFile (line 1035) | public function createFromFile($filename)
    method luminanceAreaAverage (line 1070) | public function luminanceAreaAverage($img, $x1, $y1, $x2, $y2)
    method getLuminance (line 1099) | public function getLuminance($red, $green, $blue)
    method luminance2character (line 1129) | public function luminance2character($luminance)
  class CImage (line 1146) | #[AllowDynamicProperties]
    method __construct (line 1607) | public function __construct($imageSrc = null, $imageFolder = null, $sa...
    method injectDependency (line 1623) | public function injectDependency($property, $object)
    method setVerbose (line 1642) | public function setVerbose($mode = true)
    method setSaveFolder (line 1659) | public function setSaveFolder($path)
    method useCache (line 1674) | public function useCache($use = true)
    method createDummyImage (line 1691) | public function createDummyImage($width = null, $height = null)
    method setRemoteDownload (line 1712) | public function setRemoteDownload($allow, $cache, $pattern = null)
    method isRemoteSource (line 1737) | public function isRemoteSource($src)
    method setRemoteHostWhitelist (line 1754) | public function setRemoteHostWhitelist($whitelist = null)
    method isRemoteSourceOnWhitelist (line 1774) | public function isRemoteSourceOnWhitelist($src)
    method checkFileExtension (line 1801) | private function checkFileExtension($extension)
    method normalizeFileExtension (line 1820) | private function normalizeFileExtension($extension = "")
    method downloadRemoteSource (line 1840) | public function downloadRemoteSource($src)
    method setSource (line 1873) | public function setSource($src, $dir = null)
    method setTarget (line 1909) | public function setTarget($src = null, $dir = null)
    method getTarget (line 1936) | public function getTarget()
    method setOptions (line 1950) | public function setOptions($args)
    method mapFilter (line 2067) | private function mapFilter($name)
    method loadImageDetails (line 2101) | public function loadImageDetails($file = null)
    method getMimeType (line 2152) | protected function getMimeType()
    method initDimensions (line 2170) | public function initDimensions()
    method calculateNewWidthAndHeight (line 2245) | public function calculateNewWidthAndHeight()
    method reCalculateDimensions (line 2396) | public function reCalculateDimensions()
    method setSaveAsExtension (line 2419) | public function setSaveAsExtension($saveAs = null)
    method setJpegQuality (line 2442) | public function setJpegQuality($quality = null)
    method setPngCompression (line 2469) | public function setPngCompression($compress = null)
    method useOriginalIfPossible (line 2496) | public function useOriginalIfPossible($useOrig = true)
    method generateFilename (line 2540) | public function generateFilename($base = null, $useSubdir = true, $pre...
    method useCacheIfPossible (line 2635) | public function useCacheIfPossible($useCache = true)
    method load (line 2673) | public function load($src = null, $dir = null)
    method getPngType (line 2727) | public function getPngType($filename = null)
    method getPngTypeAsString (line 2752) | private function getPngTypeAsString($pngType = null, $filename = null)
    method colorsTotal (line 2803) | private function colorsTotal($im)
    method preResize (line 2829) | public function preResize()
    method setCopyResizeStrategy (line 2871) | public function setCopyResizeStrategy($strategy)
    method imageCopyResampled (line 2884) | public function imageCopyResampled($dst_image, $src_image, $dst_x, $ds...
    method resize (line 2902) | public function resize()
    method postResize (line 3082) | public function postResize()
    method rotate (line 3166) | public function rotate($angle, $bgColor)
    method rotateExif (line 3188) | public function rotateExif()
    method trueColorToPalette (line 3232) | public function trueColorToPalette()
    method sharpenImage (line 3257) | public function sharpenImage()
    method embossImage (line 3270) | public function embossImage()
    method blurImage (line 3283) | public function blurImage()
    method createConvolveArguments (line 3299) | public function createConvolveArguments($expression)
    method addConvolveExpressions (line 3344) | public function addConvolveExpressions($options)
    method imageConvolution (line 3359) | public function imageConvolution($options = null)
    method setDefaultBackgroundColor (line 3387) | public function setDefaultBackgroundColor($color)
    method getBackgroundColor (line 3439) | private function getBackgroundColor($img = null)
    method createImageKeepTransparency (line 3473) | private function createImageKeepTransparency($width, $height)
    method setPostProcessingOptions (line 3512) | public function setPostProcessingOptions($options)
    method getTargetImageExtension (line 3550) | protected function getTargetImageExtension()
    method save (line 3573) | public function save($src = null, $base = null, $overwrite = true)
    method convert2sRGBColorSpace (line 3712) | public function convert2sRGBColorSpace($src, $dir, $cache, $iccFile, $...
    method linkToCacheFile (line 3775) | public function linkToCacheFile($alias)
    method addHTTPHeader (line 3807) | public function addHTTPHeader($type, $value)
    method output (line 3824) | public function output($file = null, $format = null)
    method json (line 3927) | public function json($file = null)
    method setAsciiOptions (line 3982) | public function setAsciiOptions($options = array())
    method ascii (line 3996) | public function ascii($file = null)
    method log (line 4014) | public function log($message)
    method setVerboseToFile (line 4032) | public function setVerboseToFile($fileName)
    method verboseOutput (line 4045) | private function verboseOutput()
    method raiseError (line 4089) | private function raiseError($message)
  class CCache (line 4101) | class CCache
    method setDir (line 4119) | public function setDir($path)
    method getPathToSubdir (line 4141) | public function getPathToSubdir($subdir, $create = true)
    method getStatusOfSubdir (line 4178) | public function getStatusOfSubdir($subdir)
    method removeSubdir (line 4201) | public function removeSubdir($subdir)
  class CFastTrackCache (line 4219) | class CFastTrackCache
    method enable (line 4256) | public function enable($enabled)
    method setCacheDir (line 4273) | public function setCacheDir($path)
    method setFilename (line 4294) | public function setFilename($clear)
    method addHeader (line 4324) | public function addHeader($header)
    method addHeaderOnOutput (line 4339) | public function addHeaderOnOutput($header)
    method setSource (line 4354) | public function setSource($source)
    method setLastModified (line 4369) | public function setLastModified($lastModified)
    method getFilename (line 4382) | public function getFilename()
    method writeToCache (line 4394) | public function writeToCache()
    method output (line 4415) | public function output()

FILE: webroot/imgs.php
  function trace (line 2) | function trace($msg) { $file = CIMAGE_DEBUG_FILE; if (!is_writable($file...
  function errorPage (line 2) | function errorPage($msg, $type = 500) { global $mode; switch ($type) { c...
  function get (line 2) | function get($key, $default = null) { if (is_array($key)) { foreach ($ke...
  function getDefined (line 2) | function getDefined($key, $defined, $undefined) { return get($key) === n...
  function getValue (line 2) | function getValue($key, $undefined) { $val = get($key); if (is_null($val...
  function getConfig (line 2) | function getConfig($key, $default) { global $config; return isset($confi...
  function verbose (line 2) | function verbose($msg = null, $arg = "") { global $verbose, $verboseFile...
  function checkExternalCommand (line 2) | function checkExternalCommand($what, $enabled, $commandString) { $no = $...
  class CHttpGet (line 2) | class CHttpGet { private $request = array(); private $response = array()...
    method __construct (line 2) | public function __construct() { $this->request['header'] = array(); }
    method buildUrl (line 2) | public function buildUrl($baseUrl, $merge) { $parts = parse_url($baseU...
    method setUrl (line 2) | public function setUrl($url) { $parts = parse_url($url); $path = ""; i...
    method setHeader (line 2) | public function setHeader($field, $value) { $this->request['header'][]...
    method parseHeader (line 2) | public function parseHeader() { $rawHeaders = rtrim($this->response['h...
    method doGet (line 2) | public function doGet($debug = false) { $options = array( CURLOPT_URL ...
    method getStatus (line 2) | public function getStatus() { return isset($this->response['header']['...
    method getLastModified (line 2) | public function getLastModified() { return isset($this->response['head...
    method getContentType (line 2) | public function getContentType() { $type = isset($this->response['head...
    method getDate (line 2) | public function getDate($default = false) { return isset($this->respon...
    method getMaxAge (line 2) | public function getMaxAge($default = false) { $cacheControl = isset($t...
    method getBody (line 2) | public function getBody() { return $this->response['body']; }
  class CRemoteImage (line 2) | class CRemoteImage { private $saveFolder = null; private $useCache = tru...
    method getStatus (line 2) | public function getStatus() { return $this->status; }
    method getDetails (line 2) | public function getDetails() { return $this->cache; }
    method setCache (line 2) | public function setCache($path) { $this->saveFolder = rtrim($path, "/"...
    method isCacheWritable (line 2) | public function isCacheWritable() { if (!is_writable($this->saveFolder...
    method useCache (line 2) | public function useCache($use = true) { $this->useCache = $use; return...
    method setHeaderFields (line 2) | public function setHeaderFields() { $cimageVersion = "CImage"; if (def...
    method save (line 2) | public function save() { $this->cache = array(); $date = $this->http->...
    method updateCacheDetails (line 2) | public function updateCacheDetails() { $date = $this->http->getDate(ti...
    method download (line 2) | public function download($url) { $this->http = new CHttpGet(); $this->...
    method loadCacheDetails (line 2) | public function loadCacheDetails() { $cacheFile = md5($this->url); $th...
    method getCachedSource (line 2) | public function getCachedSource() { $imageExists = is_readable($this->...
  class CWhitelist (line 2) | class CWhitelist { private $whitelist = array(); public function set($wh...
    method set (line 2) | public function set($whitelist = array()) { if (!is_array($whitelist))...
    method check (line 2) | public function check($item, $whitelist = null) { if ($whitelist !== n...
  class CAsciiArt (line 2) | class CAsciiArt { private $characterSet = array( 'one' => "#0XT|:,.' ", ...
    method __construct (line 2) | public function __construct() { $this->setOptions(); }
    method addCharacterSet (line 2) | public function addCharacterSet($key, $value) { $this->characterSet[$k...
    method setOptions (line 2) | public function setOptions($options = array()) { $default = array( "ch...
    method createFromFile (line 2) | public function createFromFile($filename) { $img = imagecreatefromstri...
    method luminanceAreaAverage (line 2) | public function luminanceAreaAverage($img, $x1, $y1, $x2, $y2) { $numP...
    method getLuminance (line 2) | public function getLuminance($red, $green, $blue) { switch ($this->lum...
    method luminance2character (line 2) | public function luminance2character($luminance) { $position = (int) ro...
  class CImage (line 2) | #[AllowDynamicProperties] class CImage { const PNG_GREYSCALE = 0; const ...
    method __construct (line 2) | public function __construct($imageSrc = null, $imageFolder = null, $sa...
    method injectDependency (line 2) | public function injectDependency($property, $object) { if (!property_e...
    method setVerbose (line 2) | public function setVerbose($mode = true) { $this->verbose = $mode; ret...
    method setSaveFolder (line 2) | public function setSaveFolder($path) { $this->saveFolder = $path; retu...
    method useCache (line 2) | public function useCache($use = true) { $this->useCache = $use; return...
    method createDummyImage (line 2) | public function createDummyImage($width = null, $height = null) { $thi...
    method setRemoteDownload (line 2) | public function setRemoteDownload($allow, $cache, $pattern = null) { $...
    method isRemoteSource (line 2) | public function isRemoteSource($src) { $remote = preg_match($this->rem...
    method setRemoteHostWhitelist (line 2) | public function setRemoteHostWhitelist($whitelist = null) { $this->rem...
    method isRemoteSourceOnWhitelist (line 2) | public function isRemoteSourceOnWhitelist($src) { if (is_null($this->r...
    method checkFileExtension (line 2) | private function checkFileExtension($extension) { $valid = array('jpg'...
    method normalizeFileExtension (line 2) | private function normalizeFileExtension($extension = "") { $extension ...
    method downloadRemoteSource (line 2) | public function downloadRemoteSource($src) { if (!$this->isRemoteSourc...
    method setSource (line 2) | public function setSource($src, $dir = null) { if (!isset($src)) { $th...
    method setTarget (line 2) | public function setTarget($src = null, $dir = null) { if (!isset($src)...
    method getTarget (line 2) | public function getTarget() { return $this->cacheFileName; }
    method setOptions (line 2) | public function setOptions($args) { $this->log("Set new options for pr...
    method mapFilter (line 3) | private function mapFilter($name) { $map = array( 'negate' => array('i...
    method loadImageDetails (line 3) | public function loadImageDetails($file = null) { $file = $file ? $file...
    method getMimeType (line 3) | protected function getMimeType() { if ($this->fileType === IMG_WEBP) {...
    method initDimensions (line 3) | public function initDimensions() { $this->log("Init dimension (before)...
    method calculateNewWidthAndHeight (line 3) | public function calculateNewWidthAndHeight() { $this->log("Calculate n...
    method reCalculateDimensions (line 3) | public function reCalculateDimensions() { $this->log("Re-calculate ima...
    method setSaveAsExtension (line 3) | public function setSaveAsExtension($saveAs = null) { if (isset($saveAs...
    method setJpegQuality (line 3) | public function setJpegQuality($quality = null) { if ($quality) { $thi...
    method setPngCompression (line 3) | public function setPngCompression($compress = null) { if ($compress) {...
    method useOriginalIfPossible (line 3) | public function useOriginalIfPossible($useOrig = true) { if ($useOrig ...
    method generateFilename (line 3) | public function generateFilename($base = null, $useSubdir = true, $pre...
    method useCacheIfPossible (line 3) | public function useCacheIfPossible($useCache = true) { if ($useCache &...
    method load (line 3) | public function load($src = null, $dir = null) { if (isset($src)) { $t...
    method getPngType (line 3) | public function getPngType($filename = null) { $filename = $filename ?...
    method getPngTypeAsString (line 3) | private function getPngTypeAsString($pngType = null, $filename = null)...
    method colorsTotal (line 3) | private function colorsTotal($im) { if (imageistruecolor($im)) { $this...
    method preResize (line 3) | public function preResize() { $this->log("### Pre-process before resiz...
    method setCopyResizeStrategy (line 3) | public function setCopyResizeStrategy($strategy) { $this->copyStrategy...
    method imageCopyResampled (line 3) | public function imageCopyResampled($dst_image, $src_image, $dst_x, $ds...
    method resize (line 3) | public function resize() { $this->log("### Starting to Resize()"); $th...
    method postResize (line 3) | public function postResize() { $this->log("### Post-process after resi...
    method rotate (line 3) | public function rotate($angle, $bgColor) { $this->log("Rotate image " ...
    method rotateExif (line 3) | public function rotateExif() { if (!in_array($this->fileType, array(IM...
    method trueColorToPalette (line 3) | public function trueColorToPalette() { $img = imagecreatetruecolor($th...
    method sharpenImage (line 3) | public function sharpenImage() { $this->imageConvolution('sharpen'); r...
    method embossImage (line 3) | public function embossImage() { $this->imageConvolution('emboss'); ret...
    method blurImage (line 3) | public function blurImage() { $this->imageConvolution('blur'); return ...
    method createConvolveArguments (line 3) | public function createConvolveArguments($expression) { if (isset($this...
    method addConvolveExpressions (line 4) | public function addConvolveExpressions($options) { $this->convolves = ...
    method imageConvolution (line 4) | public function imageConvolution($options = null) { $options = $option...
    method setDefaultBackgroundColor (line 4) | public function setDefaultBackgroundColor($color) { $this->log("Settin...
    method getBackgroundColor (line 8) | private function getBackgroundColor($img = null) { $img = isset($img) ...
    method createImageKeepTransparency (line 8) | private function createImageKeepTransparency($width, $height) { $this-...
    method setPostProcessingOptions (line 8) | public function setPostProcessingOptions($options) { if (isset($option...
    method getTargetImageExtension (line 8) | protected function getTargetImageExtension() { if (isset($this->extens...
    method save (line 8) | public function save($src = null, $base = null, $overwrite = true) { i...
    method convert2sRGBColorSpace (line 8) | public function convert2sRGBColorSpace($src, $dir, $cache, $iccFile, $...
    method linkToCacheFile (line 8) | public function linkToCacheFile($alias) { if ($alias === null) { $this...
    method addHTTPHeader (line 8) | public function addHTTPHeader($type, $value) { $this->HTTPHeader[$type...
    method output (line 8) | public function output($file = null, $format = null) { if (is_null($fi...
    method json (line 8) | public function json($file = null) { $file = $file ? $file : $this->ca...
    method setAsciiOptions (line 8) | public function setAsciiOptions($options = array()) { $this->asciiOpti...
    method ascii (line 8) | public function ascii($file = null) { $file = $file ? $file : $this->c...
    method log (line 8) | public function log($message) { if ($this->verbose) { $this->log[] = $...
    method setVerboseToFile (line 8) | public function setVerboseToFile($fileName) { $this->log("Setting verb...
    method verboseOutput (line 8) | private function verboseOutput() { $log = null; $this->log("### Summar...
    method raiseError (line 12) | private function raiseError($message) { throw new Exception($message); }
  class CCache (line 12) | class CCache { private $path; public function setDir($path) { if (!is_di...
    method setDir (line 12) | public function setDir($path) { if (!is_dir($path)) { throw new Except...
    method getPathToSubdir (line 12) | public function getPathToSubdir($subdir, $create = true) { $path = rea...
    method getStatusOfSubdir (line 12) | public function getStatusOfSubdir($subdir) { $path = realpath($this->p...
    method removeSubdir (line 12) | public function removeSubdir($subdir) { $path = realpath($this->path ....
  class CFastTrackCache (line 12) | class CFastTrackCache { private $enabled = false; private $path; private...
    method enable (line 12) | public function enable($enabled) { $this->enabled = $enabled; return $...
    method setCacheDir (line 12) | public function setCacheDir($path) { if (!is_dir($path)) { throw new E...
    method setFilename (line 12) | public function setFilename($clear) { $query = $_GET; foreach ($clear ...
    method addHeader (line 12) | public function addHeader($header) { $this->container["header"][] = $h...
    method addHeaderOnOutput (line 12) | public function addHeaderOnOutput($header) { $this->container["header-...
    method setSource (line 12) | public function setSource($source) { $this->container["source"] = $sou...
    method setLastModified (line 12) | public function setLastModified($lastModified) { $this->container["las...
    method getFilename (line 12) | public function getFilename() { return $this->path . "/" . $this->file...
    method writeToCache (line 12) | public function writeToCache() { if (!$this->enabled) { return false; ...
    method output (line 12) | public function output() { $filename = $this->getFilename(); if (!is_r...

FILE: webroot/js/cimage.js
  function updatePermaLink (line 22) | function updatePermaLink() {
  function compareLoadImage (line 53) | function compareLoadImage(event) {
  function compareInit (line 97) | function compareInit(options)
Condensed preview — 178 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (2,645K chars).
[
  {
    "path": ".gitignore",
    "chars": 134,
    "preview": "# Cache files\ncache/*\n\n# Test\ncoverage/\ncoverage.clover\n\n# Composer\ncomposer.lock\nvendor\n\n# Build and test\nbuild/\n\n# Mac"
  },
  {
    "path": ".scrutinizer.yml",
    "chars": 920,
    "preview": "imports:\n    - php\n\nfilter:\n    excluded_paths:\n        - test/\n        - webroot/check*\n        - webroot/imgd.php\n    "
  },
  {
    "path": ".travis.yml",
    "chars": 1832,
    "preview": "language: php\n\nphp:\n    - 5.4\n    - 5.5\n    - 5.6\n    - hhvm\n    - nightly\n    - \"7.0\"\n\n\n\nsudo: false\n\n\n\ngit:\n    submod"
  },
  {
    "path": "CAsciiArt.php",
    "chars": 5449,
    "preview": "<?php\n/**\n * Create an ASCII version of an image.\n *\n */\nclass CAsciiArt\n{\n    /**\n     * Character set to use.\n     */\n"
  },
  {
    "path": "CCache.php",
    "chars": 2479,
    "preview": "<?php\n/**\n * Deal with the cache directory and cached items.\n *\n */\nclass CCache\n{\n    /**\n     * Path to the cache dire"
  },
  {
    "path": "CFastTrackCache.php",
    "chars": 4682,
    "preview": "<?php\n/**\n * Enable a fast track cache with a json representation of the image delivery.\n *\n */\nclass CFastTrackCache\n{\n"
  },
  {
    "path": "CHttpGet.php",
    "chars": 7251,
    "preview": "<?php\n/**\n * Get a image from a remote server using HTTP GET and If-Modified-Since.\n *\n */\nclass CHttpGet\n{\n    private "
  },
  {
    "path": "CImage.php",
    "chars": 90307,
    "preview": "<?php\n/**\n * Resize and crop images on the fly, store generated images in a cache.\n *\n * @author  Mikael Roos mos@dbwebb"
  },
  {
    "path": "CRemoteImage.php",
    "chars": 7245,
    "preview": "<?php\n/**\n * Get a image from a remote server using HTTP GET and If-Modified-Since.\n *\n */\nclass CRemoteImage\n{\n    /**\n"
  },
  {
    "path": "CWhitelist.php",
    "chars": 1428,
    "preview": "<?php\n/**\n * Act as whitelist (or blacklist).\n *\n */\nclass CWhitelist\n{\n    /**\n     * Array to contain the whitelist op"
  },
  {
    "path": "LICENSE.txt",
    "chars": 1123,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2012 - 2016 Mikael Roos, https://mikaelroos.se, mos@dbwebb.se\n\nPermission is hereby"
  },
  {
    "path": "README.md",
    "chars": 32111,
    "preview": "Image conversion on the fly using PHP\n=====================================\n\n[![Join the chat at https://gitter.im/mosbt"
  },
  {
    "path": "REVISION.md",
    "chars": 16754,
    "preview": "Revision history\n=====================================\n\n<!--\n[![Build Status](https://travis-ci.org/mosbth/cimage.svg?br"
  },
  {
    "path": "SECURITY.md",
    "chars": 208,
    "preview": "Security policy\n======================\n\nTo report security vulnerabilities in the project, send en email to mikael.t.h.r"
  },
  {
    "path": "autoload.php",
    "chars": 456,
    "preview": "<?php\n/**\n * Autoloader for CImage and related class files.\n *\n */\nrequire_once __DIR__ . \"/defines.php\";\nrequire_once _"
  },
  {
    "path": "bin/cache.bash",
    "chars": 1155,
    "preview": "#!/bin/bash\n#\n# ls -ult list and sorts fils by its access time\n#\n#\n# Main, start by checking basic usage\n#\nif [ $# -lt 1"
  },
  {
    "path": "bin/create-img-single.bash",
    "chars": 2870,
    "preview": "#!/bin/bash\n\n#\n# Paths and settings\n#\nTARGET_D=\"webroot/imgd.php\"\nTARGET_P=\"webroot/imgp.php\"\nTARGET_S=\"webroot/imgs.php"
  },
  {
    "path": "cache/.gitignore",
    "chars": 71,
    "preview": "# Ignore everything in this directory\n*\n# Except this file\n!.gitignore\n"
  },
  {
    "path": "composer.json",
    "chars": 1101,
    "preview": "{\n    \"name\": \"mos/cimage\",\n    \"type\": \"library\",\n    \"description\": \"Process, scale, resize, crop and filter images.\","
  },
  {
    "path": "defines.php",
    "chars": 276,
    "preview": "<?php\n// Version of cimage and img.php\ndefine(\"CIMAGE_VERSION\", \"v0.8.6 (2023-10-27)\");\n\n// For CRemoteImage\ndefine(\"CIM"
  },
  {
    "path": "docker-compose.yaml",
    "chars": 2215,
    "preview": "version: \"3\"\nservices:\n    cli:\n        image: anax/dev\n        volumes: [ \".:/home/anax/repo\" ]\n\n    apache:\n        im"
  },
  {
    "path": "docs/api/.htaccess",
    "chars": 229,
    "preview": "# Fixes a vulnerability in CentOS: http://stackoverflow.com/questions/20533279/prevent-php-from-parsing-non-php-files-su"
  },
  {
    "path": "docs/api/classes/CAsciiArt.html",
    "chars": 37603,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/classes/CHttpGet.html",
    "chars": 40230,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/classes/CImage.html",
    "chars": 223248,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/classes/CImage_RemoteDownloadTest.html",
    "chars": 38758,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/classes/CRemoteImage.html",
    "chars": 45216,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/classes/CWhitelist.html",
    "chars": 21748,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/classes/CWhitelistTest.html",
    "chars": 30947,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/css/jquery.iviewer.css",
    "chars": 1292,
    "preview": ".viewer {\n    -ms-touch-action: none;\n}\n\n.iviewer_common {\n    position:absolute;\n    bottom:10px;\n    border: 1px  soli"
  },
  {
    "path": "docs/api/css/phpdocumentor-clean-icons/Read Me.txt",
    "chars": 342,
    "preview": "To modify your generated font, use the *dev.svg* file, located in the *fonts* folder in this package. You can import thi"
  },
  {
    "path": "docs/api/css/phpdocumentor-clean-icons/lte-ie7.js",
    "chars": 731,
    "preview": "/* Load this script using conditional IE comments if you need to support IE 7 and IE 6. */\n\nwindow.onload = function() {"
  },
  {
    "path": "docs/api/css/phpdocumentor-clean-icons/style.css",
    "chars": 1343,
    "preview": "@font-face {\n\tfont-family: 'phpdocumentor-clean-icons';\n\tsrc:url('fonts/phpdocumentor-clean-icons.eot');\n\tsrc:url('fonts"
  },
  {
    "path": "docs/api/css/prism.css",
    "chars": 3540,
    "preview": "/**\n * prism.js default theme for JavaScript, CSS and HTML\n * Based on dabblet (http://dabblet.com)\n * @author Lea Verou"
  },
  {
    "path": "docs/api/css/template.css",
    "chars": 8466,
    "preview": "@import url(https://fonts.googleapis.com/css?family=Source+Sans+Pro);\n@import url('phpdocumentor-clean-icons/style.css')"
  },
  {
    "path": "docs/api/files/CAsciiArt.html",
    "chars": 12665,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/files/CAsciiArt.php.txt",
    "chars": 5449,
    "preview": "<?php\n/**\n * Create an ASCII version of an image.\n *\n */\nclass CAsciiArt\n{\n    /**\n     * Character set to use.\n     */\n"
  },
  {
    "path": "docs/api/files/CHttpGet.html",
    "chars": 12697,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/files/CHttpGet.php.txt",
    "chars": 7254,
    "preview": "<?php\n/**\n * Get a image from a remote server using HTTP GET and If-Modified-Since.\n *\n */\nclass CHttpGet\n{\n    private "
  },
  {
    "path": "docs/api/files/CImage.html",
    "chars": 12688,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/files/CImage.php.txt",
    "chars": 81938,
    "preview": "<?php\n/**\n * Resize and crop images on the fly, store generated images in a cache.\n *\n * @author  Mikael Roos mos@dbwebb"
  },
  {
    "path": "docs/api/files/CRemoteImage.html",
    "chars": 12713,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/files/CRemoteImage.php.txt",
    "chars": 7080,
    "preview": "<?php\n/**\n * Get a image from a remote server using HTTP GET and If-Modified-Since.\n *\n */\nclass CRemoteImage\n{\n    /**\n"
  },
  {
    "path": "docs/api/files/CWhitelist.html",
    "chars": 12667,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/files/CWhitelist.php.txt",
    "chars": 1429,
    "preview": "<?php\n/**\n * Act as whitelist (or blacklist).\n *\n */\nclass CWhitelist\n{\n    /**\n     * Array to contain the whitelist op"
  },
  {
    "path": "docs/api/files/autoload.html",
    "chars": 12300,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/files/autoload.php.txt",
    "chars": 497,
    "preview": "<?php\n/**\n * Autoloader for CImage and related class files.\n *\n */\n//include __DIR__ . \"/../CHttpGet.php\";\n//include __D"
  },
  {
    "path": "docs/api/files/test%2FCImage_RemoteDownloadTest.php.txt",
    "chars": 3401,
    "preview": "<?php\n/**\n * A testclass\n * \n */\nclass CImage_RemoteDownloadTest extends \\PHPUnit_Framework_TestCase\n{\n    /*\n     * rem"
  },
  {
    "path": "docs/api/files/test%2FCWhitelistTest.php.txt",
    "chars": 1903,
    "preview": "<?php\n/**\n * A testclass\n * \n */\nclass CWhitelistTest extends \\PHPUnit_Framework_TestCase\n{\n    /*\n     * remote_whiteli"
  },
  {
    "path": "docs/api/files/test%2Fconfig.php.txt",
    "chars": 127,
    "preview": "<?php\n/**\n * Get all configuration details to be able to execute the test suite.\n *\n */\nrequire __DIR__ . \"/../autoload."
  },
  {
    "path": "docs/api/files/test.CImage_RemoteDownloadTest.html",
    "chars": 18407,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/files/test.CWhitelistTest.html",
    "chars": 18363,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/files/test.config.html",
    "chars": 17955,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/files/webroot/img.php.txt",
    "chars": 28398,
    "preview": "<?php\n/**\n * Resize and crop images on the fly, store generated images in a cache.\n *\n * @author  Mikael Roos mos@dbwebb"
  },
  {
    "path": "docs/api/files/webroot/img_config.php.txt",
    "chars": 11348,
    "preview": "<?php\n/**\n * Configuration for img.php, name the config file the same as your img.php and\n * append _config. If you are "
  },
  {
    "path": "docs/api/files/webroot%2Fcheck_system.php.txt",
    "chars": 371,
    "preview": "<?php\n\necho 'Current PHP version: ' . phpversion() . '<br><br>';\n\necho 'Running on: ' . $_SERVER['SERVER_SOFTWARE'] . '<"
  },
  {
    "path": "docs/api/files/webroot%2Fcompare%2Fcompare-test.php.txt",
    "chars": 301,
    "preview": "<?php\n$script = <<<EOD\nCImage.compare({\n    \"input1\": \"../img.php?src=car.png\",\n    \"input2\": \"../img.php?src=car.png&sh"
  },
  {
    "path": "docs/api/files/webroot%2Fcompare%2Fcompare.php.txt",
    "chars": 2330,
    "preview": "<!doctype html>\n<html lang=en>\n<head>\n<style>\n\nbody {\n}\n\ninput[type=text] {\n    width: 400px;\n}\n\n.hidden {\n    display: "
  },
  {
    "path": "docs/api/files/webroot%2Fimg.php.txt",
    "chars": 21675,
    "preview": "<?php\n/**\n * Resize and crop images on the fly, store generated images in a cache.\n *\n * @author  Mikael Roos mos@dbwebb"
  },
  {
    "path": "docs/api/files/webroot%2Fimg_config.php.txt",
    "chars": 8330,
    "preview": "<?php\n/**\n * Configuration for img.php, name the config file the same as your img.php and\n * append _config. If you are "
  },
  {
    "path": "docs/api/files/webroot%2Fimg_header.php.txt",
    "chars": 1221,
    "preview": "<?php\n/**\n * Resize and crop images on the fly, store generated images in a cache.\n *\n * This version is a all-in-one ve"
  },
  {
    "path": "docs/api/files/webroot%2Fimgd.php.txt",
    "chars": 109832,
    "preview": "<?php\n/**\n * Resize and crop images on the fly, store generated images in a cache.\n *\n * This version is a all-in-one ve"
  },
  {
    "path": "docs/api/files/webroot%2Fimgp.php.txt",
    "chars": 109833,
    "preview": "<?php\n/**\n * Resize and crop images on the fly, store generated images in a cache.\n *\n * This version is a all-in-one ve"
  },
  {
    "path": "docs/api/files/webroot%2Fimgs.php.txt",
    "chars": 109832,
    "preview": "<?php\n/**\n * Resize and crop images on the fly, store generated images in a cache.\n *\n * This version is a all-in-one ve"
  },
  {
    "path": "docs/api/files/webroot%2Ftest%2Fconfig.php.txt",
    "chars": 135,
    "preview": "<?php\n// Use error reporting\nerror_reporting(-1);\nini_set('display_errors', 1);\n\n\n// The link to img.php\n$imgphp = \"../i"
  },
  {
    "path": "docs/api/files/webroot%2Ftest%2Ftemplate.php.txt",
    "chars": 2529,
    "preview": "<!doctype html>\n<head>\n  <meta charset='utf-8'/>\n  <title><?=$title?></title>\n  <style>\n  body {background-color: #ccc;}"
  },
  {
    "path": "docs/api/files/webroot%2Ftest%2Ftest.php.txt",
    "chars": 3556,
    "preview": "<!doctype html>\n<head>\n  <meta charset='utf-8'/>\n  <title>Testing img resizing using CImage.php</title>\n</head>\n<body>\n<"
  },
  {
    "path": "docs/api/files/webroot%2Ftest%2Ftest_issue29.php.txt",
    "chars": 627,
    "preview": "<?php\n// Include config for all testcases\ninclude __DIR__ . \"/config.php\";\n\n\n\n// The title of the test case\n$title = \"Te"
  },
  {
    "path": "docs/api/files/webroot%2Ftest%2Ftest_issue36_aro.php.txt",
    "chars": 725,
    "preview": "<?php\n// Include config for all testcases\ninclude __DIR__ . \"/config.php\";\n\n\n\n// The title of the test case\n$title = \"Te"
  },
  {
    "path": "docs/api/files/webroot%2Ftest%2Ftest_issue36_rb-ra-180.php.txt",
    "chars": 730,
    "preview": "<?php\n// Include config for all testcases\ninclude __DIR__ . \"/config.php\";\n\n\n\n// The title of the test case\n$angle = 180"
  },
  {
    "path": "docs/api/files/webroot%2Ftest%2Ftest_issue36_rb-ra-270.php.txt",
    "chars": 730,
    "preview": "<?php\n// Include config for all testcases\ninclude __DIR__ . \"/config.php\";\n\n\n\n// The title of the test case\n$angle = 270"
  },
  {
    "path": "docs/api/files/webroot%2Ftest%2Ftest_issue36_rb-ra-45.php.txt",
    "chars": 729,
    "preview": "<?php\n// Include config for all testcases\ninclude __DIR__ . \"/config.php\";\n\n\n\n// The title of the test case\n$angle = 45;"
  },
  {
    "path": "docs/api/files/webroot%2Ftest%2Ftest_issue36_rb-ra-90.php.txt",
    "chars": 729,
    "preview": "<?php\n// Include config for all testcases\ninclude __DIR__ . \"/config.php\";\n\n\n\n// The title of the test case\n$angle = 90;"
  },
  {
    "path": "docs/api/files/webroot%2Ftest%2Ftest_issue38.php.txt",
    "chars": 1317,
    "preview": "<?php\n// Include config for all testcases\ninclude __DIR__ . \"/config.php\";\n\n\n\n// The title of the test case\n$title = \"Te"
  },
  {
    "path": "docs/api/files/webroot%2Ftest%2Ftest_issue40.php.txt",
    "chars": 751,
    "preview": "<?php\n// Include config for all testcases\ninclude __DIR__ . \"/config.php\";\n\n\n\n// The title of the test case\n$title = \"Te"
  },
  {
    "path": "docs/api/files/webroot%2Ftest%2Ftest_issue49.php.txt",
    "chars": 818,
    "preview": "<?php\n// Include config for all testcases\ninclude __DIR__ . \"/config.php\";\n\n\n\n// The title of the test case\n$title = \"Te"
  },
  {
    "path": "docs/api/files/webroot%2Ftest%2Ftest_issue52-cf.php.txt",
    "chars": 880,
    "preview": "<?php\n// Include config for all testcases\ninclude __DIR__ . \"/config.php\";\n\n\n\n// The title of the test case\n$title = \"Te"
  },
  {
    "path": "docs/api/files/webroot%2Ftest%2Ftest_issue52-stretch.php.txt",
    "chars": 844,
    "preview": "<?php\n// Include config for all testcases\ninclude __DIR__ . \"/config.php\";\n\n\n\n// The title of the test case\n$title = \"Te"
  },
  {
    "path": "docs/api/files/webroot%2Ftest%2Ftest_issue52.php.txt",
    "chars": 887,
    "preview": "<?php\n// Include config for all testcases\ninclude __DIR__ . \"/config.php\";\n\n\n\n// The title of the test case\n$title = \"Te"
  },
  {
    "path": "docs/api/files/webroot%2Ftest%2Ftest_issue58.php.txt",
    "chars": 628,
    "preview": "<?php\n// Include config for all testcases\ninclude __DIR__ . \"/config.php\";\n\n\n\n// The title of the test case\n$title = \"Te"
  },
  {
    "path": "docs/api/files/webroot%2Ftest%2Ftest_issue60.php.txt",
    "chars": 547,
    "preview": "<?php\n// Include config for all testcases\ninclude __DIR__ . \"/config.php\";\n\n\n\n// The title of the test case\n$title = \"Te"
  },
  {
    "path": "docs/api/files/webroot%2Ftest%2Ftest_option-crop.php.txt",
    "chars": 793,
    "preview": "<?php\n// Include config for all testcases\ninclude __DIR__ . \"/config.php\";\n\n\n\n// The title of the test case\n$title = \"Te"
  },
  {
    "path": "docs/api/files/webroot%2Ftest%2Ftest_option-no-upscale.php.txt",
    "chars": 1432,
    "preview": "<?php\n// Include config for all testcases\ninclude __DIR__ . \"/config.php\";\n\n\n\n// The title of the test case\n$title = \"Te"
  },
  {
    "path": "docs/api/files/webroot%2Ftest%2Ftest_option-save-as.php.txt",
    "chars": 717,
    "preview": "<?php\n// Include config for all testcases\ninclude __DIR__ . \"/config.php\";\n\n\n\n// The title of the test case\n$title = \"Te"
  },
  {
    "path": "docs/api/files/webroot.check_system.html",
    "chars": 17971,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/files/webroot.compare.compare-test.html",
    "chars": 17989,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/files/webroot.compare.compare.html",
    "chars": 17979,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/files/webroot.img.html",
    "chars": 26165,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/files/webroot.img_config.html",
    "chars": 12456,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/files/webroot.img_header.html",
    "chars": 19246,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/files/webroot.imgd.html",
    "chars": 33061,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/files/webroot.imgp.html",
    "chars": 33063,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/files/webroot.imgs.html",
    "chars": 33063,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/files/webroot.test.config.html",
    "chars": 17969,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/files/webroot.test.template.html",
    "chars": 17975,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/files/webroot.test.test.html",
    "chars": 17965,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/files/webroot.test.test_issue29.html",
    "chars": 17983,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/files/webroot.test.test_issue36_aro.html",
    "chars": 17991,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/files/webroot.test.test_issue36_rb-ra-180.html",
    "chars": 18003,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/files/webroot.test.test_issue36_rb-ra-270.html",
    "chars": 18001,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/files/webroot.test.test_issue36_rb-ra-45.html",
    "chars": 18001,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/files/webroot.test.test_issue36_rb-ra-90.html",
    "chars": 17999,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/files/webroot.test.test_issue38.html",
    "chars": 17983,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/files/webroot.test.test_issue40.html",
    "chars": 17983,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/files/webroot.test.test_issue49.html",
    "chars": 17983,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/files/webroot.test.test_issue52-cf.html",
    "chars": 17989,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/files/webroot.test.test_issue52-stretch.html",
    "chars": 17999,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/files/webroot.test.test_issue52.html",
    "chars": 17981,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/files/webroot.test.test_issue58.html",
    "chars": 17983,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/files/webroot.test.test_issue60.html",
    "chars": 17981,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/files/webroot.test.test_option-crop.html",
    "chars": 17989,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/files/webroot.test.test_option-no-upscale.html",
    "chars": 18001,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/files/webroot.test.test_option-save-as.html",
    "chars": 17995,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/graphs/class.html",
    "chars": 8415,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/index.html",
    "chars": 23920,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/js/html5.js",
    "chars": 2429,
    "preview": "/*\n HTML5 Shiv v3.7.0 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed\n*/\n(function(l,f){function m(){var a=e.elem"
  },
  {
    "path": "docs/api/js/jquery.dotdotdot-1.5.9.js",
    "chars": 10783,
    "preview": "/*\t\n *\tjQuery dotdotdot 1.5.9\n *\t\n *\tCopyright (c) 2013 Fred Heusschen\n *\twww.frebsite.nl\n *\n *\tPlugin website:\n *\tdotdo"
  },
  {
    "path": "docs/api/js/jquery.iviewer.js",
    "chars": 38713,
    "preview": "/*\n * iviewer Widget for jQuery UI\n * https://github.com/can3p/iviewer\n *\n * Copyright (c) 2009 - 2012 Dmitry Petrov\n * "
  },
  {
    "path": "docs/api/js/jquery.mousewheel.js",
    "chars": 7349,
    "preview": "/*! Copyright (c) 2013 Brandon Aaron (http://brandon.aaron.sh)\n * Licensed under the MIT License (LICENSE.txt).\n *\n * Ve"
  },
  {
    "path": "docs/api/js/jquery.smooth-scroll.js",
    "chars": 1081,
    "preview": "$(document).ready(function() {\n    function filterPath(string) {\n        return string\n            .replace(/^\\//,'')\n  "
  },
  {
    "path": "docs/api/namespaces/default.html",
    "chars": 24021,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/reports/deprecated.html",
    "chars": 8128,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/reports/errors.html",
    "chars": 27064,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "docs/api/reports/markers.html",
    "chars": 10414,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximu"
  },
  {
    "path": "functions.php",
    "chars": 4278,
    "preview": "<?php\n/**\n * General functions to use in img.php.\n */\n\n\n\n/**\n * Trace and log execution to logfile, useful for debugging"
  },
  {
    "path": "phpcs.xml",
    "chars": 1158,
    "preview": "<?xml version=\"1.0\"?>\n<ruleset name=\"PHPCS rule set\">\n    <description>Custom rule set.</description>\n\n    <file>.</file"
  },
  {
    "path": "phpdoc.xml",
    "chars": 369,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<phpdoc>\n    <title>CImage API Documentaion</title>\n    <parser>\n        <target"
  },
  {
    "path": "phpunit.xml",
    "chars": 579,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<phpunit\n    bootstrap=\"test/config.php\">\n\n    <testsuites>\n        <testsuite n"
  },
  {
    "path": "test/CCacheTest.php",
    "chars": 1509,
    "preview": "<?php\n/**\n * A testclass\n *\n */\nclass CCacheTest extends \\PHPUnit_Framework_TestCase\n{\n    /**\n     * Test\n     *\n     *"
  },
  {
    "path": "test/CImageDummyTest.php",
    "chars": 2018,
    "preview": "<?php\n/**\n * A testclass\n *\n */\nclass CImageDummyTest extends \\PHPUnit_Framework_TestCase\n{\n    const DUMMY = \"__dummy__"
  },
  {
    "path": "test/CImageRemoteDownloadTest.php",
    "chars": 3705,
    "preview": "<?php\n/**\n * A testclass\n *\n */\nclass CImageRemoteDownloadTest extends \\PHPUnit_Framework_TestCase\n{\n    /*\n     * remot"
  },
  {
    "path": "test/CImageSRGBTest.php",
    "chars": 1447,
    "preview": "<?php\n/**\n * A testclass\n *\n */\nclass CImageSRGBTest extends \\PHPUnit_Framework_TestCase\n{\n    private $srgbDir = \"srgb\""
  },
  {
    "path": "test/CWhitelistTest.php",
    "chars": 2520,
    "preview": "<?php\n/**\n * A testclass\n *\n */\nclass CWhitelistTest extends \\PHPUnit_Framework_TestCase\n{\n    /*\n     * remote_whitelis"
  },
  {
    "path": "test/config.php",
    "chars": 225,
    "preview": "<?php\n/**\n * Get all configuration details to be able to execute the test suite.\n *\n */\nrequire __DIR__ . \"/../autoload."
  },
  {
    "path": "webroot/check_system.php",
    "chars": 828,
    "preview": "<?php\n\necho 'Current PHP version: ' . phpversion() . '<br><br>';\n\necho 'Running on: ' . htmlentities($_SERVER['SERVER_SO"
  },
  {
    "path": "webroot/compare/compare-test.php",
    "chars": 301,
    "preview": "<?php\n$script = <<<EOD\nCImage.compare({\n    \"input1\": \"../img.php?src=car.png\",\n    \"input2\": \"../img.php?src=car.png&sh"
  },
  {
    "path": "webroot/compare/compare.php",
    "chars": 3968,
    "preview": "<!doctype html>\n<html lang=en>\n<head>\n<style>\n\n<?php\nfunction e($str) {\n    return htmlspecialchars($str, ENT_QUOTES, 'U"
  },
  {
    "path": "webroot/compare/issue117-PNG24.php",
    "chars": 515,
    "preview": "<?php\n$script = <<<EOD\nCImage.compare({\n    \"input1\": \"../img.php?src=issue117/tri_original.png\",\n    \"input2\": \"../img."
  },
  {
    "path": "webroot/htaccess",
    "chars": 623,
    "preview": "#\n# Rewrite to have friendly urls to img.php, edit it to suite your environment.\n#\n# The example is set up as following."
  },
  {
    "path": "webroot/img.php",
    "chars": 30796,
    "preview": "<?php\n/**\n * Resize and crop images on the fly, store generated images in a cache.\n *\n * @author  Mikael Roos mos@dbwebb"
  },
  {
    "path": "webroot/img_config.php",
    "chars": 15256,
    "preview": "<?php\n/**\n * Configuration for img.php, name the config file the same as your img.php and\n * append _config. If you are "
  },
  {
    "path": "webroot/img_header.php",
    "chars": 1248,
    "preview": "<?php\n/**\n * Resize and crop images on the fly, store generated images in a cache.\n *\n * This version is a all-in-one ve"
  },
  {
    "path": "webroot/imgd.php",
    "chars": 155411,
    "preview": "<?php\n/**\n * Resize and crop images on the fly, store generated images in a cache.\n *\n * This version is a all-in-one ve"
  },
  {
    "path": "webroot/imgf.php",
    "chars": 2542,
    "preview": "<?php\n/**\n * Fast track cache, read entries from the cache before processing image\n * the ordinary way.\n */\n// Load the "
  },
  {
    "path": "webroot/imgp.php",
    "chars": 155412,
    "preview": "<?php\n/**\n * Resize and crop images on the fly, store generated images in a cache.\n *\n * This version is a all-in-one ve"
  },
  {
    "path": "webroot/imgs.php",
    "chars": 87009,
    "preview": "<?php\n define(\"CIMAGE_BUNDLE\", true); $config = array( 'mode' => 'strict', ); define(\"CIMAGE_VERSION\", \"v0.8.6 (2023-10-"
  },
  {
    "path": "webroot/js/cimage.js",
    "chars": 8101,
    "preview": "/**\n * JavaScript utilities for CImage and img.php.\n */\nwindow.CImage = (function() {\n    \"use strict\";\n\n\n    /**\n     *"
  },
  {
    "path": "webroot/test/config.php",
    "chars": 134,
    "preview": "<?php\n// Use error reporting\nerror_reporting(-1);\nini_set('display_errors', 1);\n\n\n// The link to img.php\n$imgphp = \"../i"
  },
  {
    "path": "webroot/test/template.php",
    "chars": 2562,
    "preview": "<!doctype html>\n<head>\n  <meta charset='utf-8'/>\n  <title><?=$title?></title>\n  <style>\n  body {background-color: #ccc;}"
  },
  {
    "path": "webroot/test/test.php",
    "chars": 3555,
    "preview": "<!doctype html>\n<head>\n  <meta charset='utf-8'/>\n  <title>Testing img resizing using CImage.php</title>\n</head>\n<body>\n<"
  },
  {
    "path": "webroot/test/test_issue101-dummy.php",
    "chars": 566,
    "preview": "<?php\n// Include config for all testcases\ninclude __DIR__ . \"/config.php\";\n\n\n\n// The title of the test case\n$title = \"Te"
  },
  {
    "path": "webroot/test/test_issue29.php",
    "chars": 626,
    "preview": "<?php\n// Include config for all testcases\ninclude __DIR__ . \"/config.php\";\n\n\n\n// The title of the test case\n$title = \"Te"
  },
  {
    "path": "webroot/test/test_issue36_aro.php",
    "chars": 724,
    "preview": "<?php\n// Include config for all testcases\ninclude __DIR__ . \"/config.php\";\n\n\n\n// The title of the test case\n$title = \"Te"
  },
  {
    "path": "webroot/test/test_issue36_rb-ra-180.php",
    "chars": 729,
    "preview": "<?php\n// Include config for all testcases\ninclude __DIR__ . \"/config.php\";\n\n\n\n// The title of the test case\n$angle = 180"
  },
  {
    "path": "webroot/test/test_issue36_rb-ra-270.php",
    "chars": 729,
    "preview": "<?php\n// Include config for all testcases\ninclude __DIR__ . \"/config.php\";\n\n\n\n// The title of the test case\n$angle = 270"
  },
  {
    "path": "webroot/test/test_issue36_rb-ra-45.php",
    "chars": 728,
    "preview": "<?php\n// Include config for all testcases\ninclude __DIR__ . \"/config.php\";\n\n\n\n// The title of the test case\n$angle = 45;"
  },
  {
    "path": "webroot/test/test_issue36_rb-ra-90.php",
    "chars": 728,
    "preview": "<?php\n// Include config for all testcases\ninclude __DIR__ . \"/config.php\";\n\n\n\n// The title of the test case\n$angle = 90;"
  },
  {
    "path": "webroot/test/test_issue38.php",
    "chars": 1316,
    "preview": "<?php\n// Include config for all testcases\ninclude __DIR__ . \"/config.php\";\n\n\n\n// The title of the test case\n$title = \"Te"
  },
  {
    "path": "webroot/test/test_issue40.php",
    "chars": 750,
    "preview": "<?php\n// Include config for all testcases\ninclude __DIR__ . \"/config.php\";\n\n\n\n// The title of the test case\n$title = \"Te"
  },
  {
    "path": "webroot/test/test_issue49.php",
    "chars": 817,
    "preview": "<?php\n// Include config for all testcases\ninclude __DIR__ . \"/config.php\";\n\n\n\n// The title of the test case\n$title = \"Te"
  },
  {
    "path": "webroot/test/test_issue52-cf.php",
    "chars": 887,
    "preview": "<?php\n// Include config for all testcases\ninclude __DIR__ . \"/config.php\";\n\n\n\n// The title of the test case\n$title = \"Te"
  },
  {
    "path": "webroot/test/test_issue52-stretch.php",
    "chars": 851,
    "preview": "<?php\n// Include config for all testcases\ninclude __DIR__ . \"/config.php\";\n\n\n\n// The title of the test case\n$title = \"Te"
  },
  {
    "path": "webroot/test/test_issue52.php",
    "chars": 896,
    "preview": "<?php\n// Include config for all testcases\ninclude __DIR__ . \"/config.php\";\n\n\n\n// The title of the test case\n$title = \"Te"
  },
  {
    "path": "webroot/test/test_issue58.php",
    "chars": 627,
    "preview": "<?php\n// Include config for all testcases\ninclude __DIR__ . \"/config.php\";\n\n\n\n// The title of the test case\n$title = \"Te"
  },
  {
    "path": "webroot/test/test_issue60.php",
    "chars": 546,
    "preview": "<?php\n// Include config for all testcases\ninclude __DIR__ . \"/config.php\";\n\n\n\n// The title of the test case\n$title = \"Te"
  },
  {
    "path": "webroot/test/test_issue85.php",
    "chars": 611,
    "preview": "<?php\n// Include config for all testcases\ninclude __DIR__ . \"/config.php\";\n\n\n\n// The title of the test case\n$title = \"Te"
  },
  {
    "path": "webroot/test/test_option-crop.php",
    "chars": 792,
    "preview": "<?php\n// Include config for all testcases\ninclude __DIR__ . \"/config.php\";\n\n\n\n// The title of the test case\n$title = \"Te"
  },
  {
    "path": "webroot/test/test_option-no-upscale.php",
    "chars": 1639,
    "preview": "<?php\n// Include config for all testcases\ninclude __DIR__ . \"/config.php\";\n\n\n\n// The title of the test case\n$title = \"Te"
  },
  {
    "path": "webroot/test/test_option-save-as.php",
    "chars": 716,
    "preview": "<?php\n// Include config for all testcases\ninclude __DIR__ . \"/config.php\";\n\n\n\n// The title of the test case\n$title = \"Te"
  },
  {
    "path": "webroot/tests.php",
    "chars": 507,
    "preview": "<?php\n\n$links = [\n    \"img.php?src=car.png&v\",\n    \"img.php?src=car.png&w=700&v\",\n];\n\n?><!doctype html>\n<html>\n    <head"
  }
]

// ... and 8 more files (download for full content)

About this extraction

This page contains the full source code of the mosbth/cimage GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 178 files (2.4 MB), approximately 641.4k tokens, and a symbol index with 559 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!