Current oav website

This commit is contained in:
Charlie Root
2023-03-20 12:18:38 +01:00
commit a096ce07cf
3270 changed files with 261778 additions and 0 deletions

View File

@ -0,0 +1,387 @@
<?php
/**
* @class imageMeta
* @brief Image metadata
*
* This class reads EXIF, IPTC and XMP metadata from a JPEG file.
*
* - Contributor: Mathieu Lecarme.
*
* @package Clearbricks
* @subpackage Images
*
* @copyright Olivier Meunier & Association Dotclear
* @copyright GPL-2.0-only
*/
class imageMeta
{
protected $xmp = []; ///< array: Internal XMP array
protected $iptc = []; ///< array: Internal IPTC array
protected $exif = []; ///< array: Internal EXIF array
/**
* Read metadata
*
* Returns all image metadata in an array as defined in {@link $properties}.
*
* @param string $f Image file path
* @return array
*/
public static function readMeta($f)
{
$o = new self;
$o->loadFile($f);
return $o->getMeta();
}
/**
* Get metadata
*
* Returns all image metadata in an array as defined in {@link $properties}.
* Should call {@link loadFile()} before.
*
* @return array
*/
public function getMeta()
{
foreach ($this->properties as $k => $v) {
if (!empty($this->xmp[$k])) {
$this->properties[$k] = $this->xmp[$k];
} elseif (!empty($this->iptc[$k])) {
$this->properties[$k] = $this->iptc[$k];
} elseif (!empty($this->exif[$k])) {
$this->properties[$k] = $this->exif[$k];
}
}
# Fix date format
$this->properties['DateTimeOriginal'] = preg_replace(
'/^(\d{4}):(\d{2}):(\d{2})/', '$1-$2-$3',
$this->properties['DateTimeOriginal']
);
return $this->properties;
}
/**
* Load file
*
* Loads a file and read its metadata.
*
* @param string $f Image file path
*/
public function loadFile($f)
{
if (!is_file($f) || !is_readable($f)) {
throw new Exception('Unable to read file');
}
$this->readXMP($f);
$this->readIPTC($f);
$this->readExif($f);
}
/**
* Read XMP
*
* Reads XML metadata and assigns values to {@link $xmp}.
*
* @param string $f Image file path
*/
protected function readXMP($f)
{
if (($fp = @fopen($f, 'rb')) === false) {
throw new Exception('Unable to open image file');
}
$inside = false;
$done = false;
$xmp = null;
while (!feof($fp)) {
$buffer = fgets($fp, 4096);
$xmp_start = strpos($buffer, '<x:xmpmeta');
if ($xmp_start !== false) {
$buffer = substr($buffer, $xmp_start);
$inside = true;
}
if ($inside) {
$xmp_end = strpos($buffer, '</x:xmpmeta>');
if ($xmp_end !== false) {
$buffer = substr($buffer, $xmp_end, 12);
$inside = false;
$done = true;
}
$xmp .= $buffer;
}
if ($done) {
break;
}
}
fclose($fp);
if (!$xmp) {
return;
}
foreach ($this->xmp_reg as $code => $patterns) {
foreach ($patterns as $p) {
if (preg_match($p, $xmp, $m)) {
$this->xmp[$code] = $m[1];
break;
}
}
}
if (preg_match('%<dc:subject>\s*<rdf:Bag>(.+?)</rdf:Bag%msu', $xmp, $m)
&& preg_match_all('%<rdf:li>(.+?)</rdf:li>%msu', $m[1], $m)) {
$this->xmp['Keywords'] = implode(',', $m[1]);
}
foreach ($this->xmp as $k => $v) {
$this->xmp[$k] = html::decodeEntities(text::toUTF8($v));
}
}
/**
* Read IPTC
*
* Reads IPTC metadata and assigns values to {@link $iptc}.
*
* @param string $f Image file path
*/
protected function readIPTC($f)
{
if (!function_exists('iptcparse')) {
return;
}
$imageinfo = null;
@getimagesize($f, $imageinfo);
if (!is_array($imageinfo) || !isset($imageinfo['APP13'])) {
return;
}
$iptc = @iptcparse($imageinfo['APP13']);
if (!is_array($iptc)) {
return;
}
foreach ($this->iptc_ref as $k => $v) {
if (isset($iptc[$k]) && isset($this->iptc_to_property[$v])) {
$this->iptc[$this->iptc_to_property[$v]] = text::toUTF8(trim(implode(',', $iptc[$k])));
}
}
}
/**
* Read EXIF
*
* Reads EXIF metadata and assigns values to {@link $exif}.
*
* @param string $f Image file path
*/
protected function readEXIF($f)
{
if (!function_exists('exif_read_data')) {
return;
}
$d = @exif_read_data($f, 'ANY_TAG');
if (!is_array($d)) {
return;
}
foreach ($this->exif_to_property as $k => $v) {
if (isset($d[$k])) {
if (is_array($d[$k])) {
foreach ($d[$k] as $kk => $vv) {
$this->exif[$v . '.' . $kk] = text::toUTF8($vv);
}
} else {
$this->exif[$v] = text::toUTF8($d[$k]);
}
}
}
}
/**
* array $properties Final properties array
*/
protected $properties = [
'Title' => null,
'Description' => null,
'Creator' => null,
'Rights' => null,
'Make' => null,
'Model' => null,
'Exposure' => null,
'FNumber' => null,
'MaxApertureValue' => null,
'ExposureProgram' => null,
'ISOSpeedRatings' => null,
'DateTimeOriginal' => null,
'ExposureBiasValue' => null,
'MeteringMode' => null,
'FocalLength' => null,
'Lens' => null,
'CountryCode' => null,
'Country' => null,
'State' => null,
'City' => null,
'Keywords' => null
];
# XMP
protected $xmp_reg = [
'Title' => [
'%<dc:title>\s*<rdf:Alt>\s*<rdf:li.*?>(.+?)</rdf:li>%msu'
],
'Description' => [
'%<dc:description>\s*<rdf:Alt>\s*<rdf:li.*?>(.+?)</rdf:li>%msu'
],
'Creator' => [
'%<dc:creator>\s*<rdf:Seq>\s*<rdf:li>(.+?)</rdf:li>%msu'
],
'Rights' => [
'%<dc:rights>\s*<rdf:Alt>\s*<rdf:li.*?>(.+?)</rdf:li>%msu'
],
'Make' => [
'%<tiff:Make>(.+?)</tiff:Make>%msu',
'%tiff:Make="(.+?)"%msu'
],
'Model' => [
'%<tiff:Model>(.+?)</tiff:Model>%msu',
'%tiff:Model="(.+?)"%msu'
],
'Exposure' => [
'%<exif:ExposureTime>(.+?)</exif:ExposureTime>%msu',
'%exif:ExposureTime="(.+?)"%msu'
],
'FNumber' => [
'%<exif:FNumber>(.+?)</exif:FNumber>%msu',
'%exif:FNumber="(.+?)"%msu'
],
'MaxApertureValue' => [
'%<exif:MaxApertureValue>(.+?)</exif:MaxApertureValue>%msu',
'%exif:MaxApertureValue="(.+?)"%msu'
],
'ExposureProgram' => [
'%<exif:ExposureProgram>(.+?)</exif:ExposureProgram>%msu',
'%exif:ExposureProgram="(.+?)"%msu'
],
'ISOSpeedRatings' => [
'%<exif:ISOSpeedRatings>\s*<rdf:Seq>\s*<rdf:li>(.+?)</rdf:li>%msu'
],
'DateTimeOriginal' => [
'%<exif:DateTimeOriginal>(.+?)</exif:DateTimeOriginal>%msu',
'%exif:DateTimeOriginal="(.+?)"%msu'
],
'ExposureBiasValue' => [
'%<exif:ExposureBiasValue>(.+?)</exif:ExposureBiasValue>%msu',
'%exif:ExposureBiasValue="(.+?)"%msu'
],
'MeteringMode' => [
'%<exif:MeteringMode>(.+?)</exif:MeteringMode>%msu',
'%exif:MeteringMode="(.+?)"%msu'
],
'FocalLength' => [
'%<exif:FocalLength>(.+?)</exif:FocalLength>%msu',
'%exif:FocalLength="(.+?)"%msu'
],
'Lens' => [
'%<aux:Lens>(.+?)</aux:Lens>%msu',
'%aux:Lens="(.+?)"%msu'
],
'CountryCode' => [
'%<Iptc4xmpCore:CountryCode>(.+?)</Iptc4xmpCore:CountryCode>%msu',
'%Iptc4xmpCore:CountryCode="(.+?)"%msu'
],
'Country' => [
'%<photoshop:Country>(.+?)</photoshop:Country>%msu',
'%photoshop:Country="(.+?)"%msu'
],
'State' => [
'%<photoshop:State>(.+?)</photoshop:State>%msu',
'%photoshop:State="(.+?)"%msu'
],
'City' => [
'%<photoshop:City>(.+?)</photoshop:City>%msu',
'%photoshop:City="(.+?)"%msu'
]
];
# IPTC
protected $iptc_ref = [
'1#090' => 'Iptc.Envelope.CharacterSet', // Character Set used (32 chars max)
'2#005' => 'Iptc.ObjectName', // Title (64 chars max)
'2#015' => 'Iptc.Category', // (3 chars max)
'2#020' => 'Iptc.Supplementals', // Supplementals categories (32 chars max)
'2#025' => 'Iptc.Keywords', // (64 chars max)
'2#040' => 'Iptc.SpecialsInstructions', // (256 chars max)
'2#055' => 'Iptc.DateCreated', // YYYYMMDD (8 num chars max)
'2#060' => 'Iptc.TimeCreated', // HHMMSS+/-HHMM (11 chars max)
'2#062' => 'Iptc.DigitalCreationDate', // YYYYMMDD (8 num chars max)
'2#063' => 'Iptc.DigitalCreationTime', // HHMMSS+/-HHMM (11 chars max)
'2#080' => 'Iptc.ByLine', // Author (32 chars max)
'2#085' => 'Iptc.ByLineTitle', // Author position (32 chars max)
'2#090' => 'Iptc.City', // (32 chars max)
'2#092' => 'Iptc.Sublocation', // (32 chars max)
'2#095' => 'Iptc.ProvinceState', // (32 chars max)
'2#100' => 'Iptc.CountryCode', // (32 alpha chars max)
'2#101' => 'Iptc.CountryName', // (64 chars max)
'2#105' => 'Iptc.Headline', // (256 chars max)
'2#110' => 'Iptc.Credits', // (32 chars max)
'2#115' => 'Iptc.Source', // (32 chars max)
'2#116' => 'Iptc.Copyright', // Copyright Notice (128 chars max)
'2#118' => 'Iptc.Contact', // (128 chars max)
'2#120' => 'Iptc.Caption', // Caption/Abstract (2000 chars max)
'2#122' => 'Iptc.CaptionWriter' // Caption Writer/Editor (32 chars max)
];
protected $iptc_to_property = [
'Iptc.ObjectName' => 'Title',
'Iptc.Caption' => 'Description',
'Iptc.ByLine' => 'Creator',
'Iptc.Copyright' => 'Rights',
'Iptc.CountryCode' => 'CountryCode',
'Iptc.CountryName' => 'Country',
'Iptc.ProvinceState' => 'State',
'Iptc.City' => 'City',
'Iptc.Keywords' => 'Keywords'
];
# EXIF
protected $exif_to_property = [
//'' => 'Title',
'ImageDescription' => 'Description',
'Artist' => 'Creator',
'Copyright' => 'Rights',
'Make' => 'Make',
'Model' => 'Model',
'ExposureTime' => 'Exposure',
'FNumber' => 'FNumber',
'MaxApertureValue' => 'MaxApertureValue',
'ExposureProgram' => 'ExposureProgram',
'ISOSpeedRatings' => 'ISOSpeedRatings',
'DateTimeOriginal' => 'DateTimeOriginal',
'ExposureBiasValue' => 'ExposureBiasValue',
'MeteringMode' => 'MeteringMode',
'FocalLength' => 'FocalLength'
//'' => 'Lens',
//'' => 'CountryCode',
//'' => 'Country',
//'' => 'State',
//'' => 'City',
//'' => 'Keywords'
];
}

View File

@ -0,0 +1,300 @@
<?php
/**
* @class imageTools
* @brief Image manipulations
*
* Class to manipulate images. Some methods are based on https://dev.media-box.net/big/
*
* @package Clearbricks
* @subpackage Images
*
* @copyright Olivier Meunier & Association Dotclear
* @copyright GPL-2.0-only
*/
class imageTools
{
public $res; ///< resource: Image resource
public $memory_limit = null;
/**
* Constructor, no parameters.
*/
public function __construct()
{
if (!function_exists('imagegd2')) {
throw new Exception('GD is not installed');
}
$this->res = null;
}
/**
* Close
*
* Destroy image resource
*/
public function close()
{
if (is_resource($this->res)) {
imagedestroy($this->res);
}
if ($this->memory_limit) {
ini_set('memory_limit', $this->memory_limit);
}
}
/**
* Load image
*
* Loads an image content in memory and set {@link $res} property.
*
* @param string $f Image file path
*/
public function loadImage($f)
{
if (!file_exists($f)) {
throw new Exception('Image doest not exists');
}
if (($info = @getimagesize($f)) !== false) {
$this->memoryAllocate(
$info[0], $info[1],
isset($infos['channels']) ? $info['channels'] : 4
);
switch ($info[2]) {
case 3: // PNG
$this->res = @imagecreatefrompng($f);
if (is_resource($this->res)) {
@imagealphablending($this->res, false);
@imagesavealpha($this->res, true);
}
break;
case 2: // JPEG
$this->res = @imagecreatefromjpeg($f);
break;
case 1: // GIF
$this->res = @imagecreatefromgif($f);
break;
case 18: // WEBP
if (function_exists('imagecreatefromwebp')) {
$this->res = @imagecreatefromwebp($f);
if (is_resource($this->res)) {
@imagealphablending($this->res, false);
@imagesavealpha($this->res, true);
}
} else {
throw new Exception('WebP image format not supported');
}
break;
}
}
if (!is_resource($this->res)) {
throw new Exception('Unable to load image');
}
}
/**
* Image width
*
* @return integer Image width
*/
public function getW()
{
return imagesx($this->res);
}
/**
* Image height
*
* @return integer Image height
*/
public function getH()
{
return imagesy($this->res);
}
public function memoryAllocate($w, $h, $bpp = 4)
{
$mem_used = function_exists('memory_get_usage') ? @memory_get_usage() : 4000000;
$mem_limit = @ini_get('memory_limit');
if ($mem_limit && trim($mem_limit) === '-1' || !files::str2bytes($mem_limit)) {
// Cope with memory_limit set to -1 in PHP.ini
return;
}
if ($mem_used && $mem_limit) {
$mem_limit = files::str2bytes($mem_limit);
$mem_avail = $mem_limit - $mem_used - (512 * 1024);
$mem_needed = $w * $h * $bpp;
if ($mem_needed > $mem_avail) {
if (@ini_set('memory_limit', $mem_limit + $mem_needed + $mem_used) === false) {
throw new Exception(__('Not enough memory to open image.'));
}
if (!$this->memory_limit) {
$this->memory_limit = $mem_limit;
}
}
}
}
/**
* Image output
*
* Returns image content in a file or as HTML output (with headers)
*
* @param string $type Image type (png or jpg)
* @param string|null $file Output file. If null, output will be echoed in STDOUT
* @param integer $qual JPEG image quality
*/
public function output($type = 'png', $file = null, $qual = 90)
{
if (!$file) {
header('Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0');
header('Pragma: no-cache');
switch (strtolower($type)) {
case 'png':
header('Content-type: image/png');
imagepng($this->res);
return true;
case 'jpeg':
case 'jpg':
header('Content-type: image/jpeg');
imagejpeg($this->res, null, $qual);
return true;
case 'wepb':
if (function_exists('imagewebp')) {
header('Content-type: image/webp');
imagewebp($this->res, null, $qual);
return true;
}
return false;
default:
return false;
}
} elseif (is_writable(dirname($file))) {
switch (strtolower($type)) {
case 'png':
return imagepng($this->res, $file);
case 'jpeg':
case 'jpg':
return imagejpeg($this->res, $file, $qual);
case 'webp':
if (function_exists('imagewebp')) {
return imagewebp($this->res, $file, $qual);
}
return false;
default:
return false;
}
}
return false;
}
/**
* Resize image
*
* @param mixed $WIDTH Image width (px or percent)
* @param mixed $HEIGHT Image height (px or percent)
* @param string $MODE Crop mode (force, crop, ratio)
* @param boolean $EXPAND Allow resize of image
*/
public function resize($WIDTH, $HEIGHT, $MODE = 'ratio', $EXPAND = false)
{
$imgWidth = $this->getW();
$imgHeight = $this->getH();
if (strpos($WIDTH, '%', 0)) {
$WIDTH = $imgWidth * $WIDTH / 100;
}
if (strpos($HEIGHT, '%', 0)) {
$HEIGHT = $imgHeight * $HEIGHT / 100;
}
$ratio = $imgWidth / $imgHeight;
// guess resize ($_w et $_h)
if ($MODE == 'ratio') {
$_w = 99999;
if ($HEIGHT > 0) {
$_h = $HEIGHT;
$_w = $_h * $ratio;
}
if ($WIDTH > 0 && $_w > $WIDTH) {
$_w = $WIDTH;
$_h = $_w / $ratio;
}
if (!$EXPAND && $_w > $imgWidth) {
$_w = $imgWidth;
$_h = $imgHeight;
}
} else {
// crop source image
$_w = $WIDTH;
$_h = $HEIGHT;
}
if ($MODE == 'force') {
if ($WIDTH > 0) {
$_w = $WIDTH;
} else {
$_w = $HEIGHT * $ratio;
}
if ($HEIGHT > 0) {
$_h = $HEIGHT;
} else {
$_h = $WIDTH / $ratio;
}
if (!$EXPAND && $_w > $imgWidth) {
$_w = $imgWidth;
$_h = $imgHeight;
}
$cropW = $imgWidth;
$cropH = $imgHeight;
$decalW = 0;
$decalH = 0;
} else {
// guess real viewport of image
$innerRatio = $_w / $_h;
if ($ratio >= $innerRatio) {
$cropH = $imgHeight;
$cropW = $imgHeight * $innerRatio;
$decalH = 0;
$decalW = ($imgWidth - $cropW) / 2;
} else {
$cropW = $imgWidth;
$cropH = $imgWidth / $innerRatio;
$decalW = 0;
$decalH = ($imgHeight - $cropH) / 2;
}
}
if ($_w < 1) {
$_w = 1;
}
if ($_h < 1) {
$_h = 1;
}
# truecolor is 24 bit RGB, ie. 3 bytes per pixel.
$this->memoryAllocate($_w, $_h, 3);
$dest = imagecreatetruecolor($_w, $_h);
$fill = imagecolorallocate($dest, 128, 128, 128);
imagefill($dest, 0, 0, $fill);
@imagealphablending($dest, false);
@imagesavealpha($dest, true);
imagecopyresampled($dest, $this->res, 0, 0, $decalW, $decalH, $_w, $_h, $cropW, $cropH);
imagedestroy($this->res);
$this->res = $dest;
return true;
}
}