aboutsummaryrefslogtreecommitdiffstats
path: root/include/photo/photo_driver.php
diff options
context:
space:
mode:
Diffstat (limited to 'include/photo/photo_driver.php')
-rw-r--r--include/photo/photo_driver.php674
1 files changed, 674 insertions, 0 deletions
diff --git a/include/photo/photo_driver.php b/include/photo/photo_driver.php
new file mode 100644
index 000000000..f5e915402
--- /dev/null
+++ b/include/photo/photo_driver.php
@@ -0,0 +1,674 @@
+<?php /** @file */
+
+function photo_factory($data, $type = null) {
+ $ph = null;
+
+ if(class_exists('Imagick')) {
+ require_once('include/photo/photo_imagick.php');
+ $ph = new photo_imagick($data,$type);
+ }
+ else {
+ require_once('include/photo/photo_gd.php');
+ $ph = new photo_gd($data,$type);
+ }
+
+ return $ph;
+}
+
+
+
+
+abstract class photo_driver {
+
+ protected $image;
+ protected $width;
+ protected $height;
+ protected $valid;
+ protected $type;
+ protected $types;
+
+ abstract function supportedTypes();
+
+ abstract function load($data,$type);
+
+ abstract function destroy();
+
+ abstract function setDimensions();
+
+ abstract function getImage();
+
+ abstract function doScaleImage($new_width,$new_height);
+
+ abstract function rotate($degrees);
+
+ abstract function flip($horiz = true, $vert = false);
+
+ abstract function cropImage($max,$x,$y,$w,$h);
+
+ abstract function imageString();
+
+
+ public function __construct($data, $type='') {
+ $this->types = $this->supportedTypes();
+ if (! array_key_exists($type,$this->types)){
+ $type='image/jpeg';
+ }
+ $this->type = $type;
+ $this->valid = false;
+ $this->load($data,$type);
+ }
+
+ public function __destruct() {
+ if($this->is_valid())
+ $this->destroy();
+ }
+
+ public function is_valid() {
+ return $this->valid;
+ }
+
+ public function getWidth() {
+ if(!$this->is_valid())
+ return FALSE;
+ return $this->width;
+ }
+
+ public function getHeight() {
+ if(!$this->is_valid())
+ return FALSE;
+ return $this->height;
+ }
+
+
+ public function saveImage($path) {
+ if(!$this->is_valid())
+ return FALSE;
+ file_put_contents($path, $this->imageString());
+ }
+
+
+ public function getType() {
+ if(!$this->is_valid())
+ return FALSE;
+
+ return $this->type;
+ }
+
+ public function getExt() {
+ if(!$this->is_valid())
+ return FALSE;
+
+ return $this->types[$this->getType()];
+ }
+
+ public function scaleImage($max) {
+ if(!$this->is_valid())
+ return FALSE;
+
+ $width = $this->width;
+ $height = $this->height;
+
+ $dest_width = $dest_height = 0;
+
+ if((! $width)|| (! $height))
+ return FALSE;
+
+ if($width > $max && $height > $max) {
+
+ // very tall image (greater than 16:9)
+ // constrain the width - let the height float.
+
+ if((($height * 9) / 16) > $width) {
+ $dest_width = $max;
+ $dest_height = intval(( $height * $max ) / $width);
+ }
+
+ // else constrain both dimensions
+
+ elseif($width > $height) {
+ $dest_width = $max;
+ $dest_height = intval(( $height * $max ) / $width);
+ }
+ else {
+ $dest_width = intval(( $width * $max ) / $height);
+ $dest_height = $max;
+ }
+ }
+ else {
+ if( $width > $max ) {
+ $dest_width = $max;
+ $dest_height = intval(( $height * $max ) / $width);
+ }
+ else {
+ if( $height > $max ) {
+
+ // very tall image (greater than 16:9)
+ // but width is OK - don't do anything
+
+ if((($height * 9) / 16) > $width) {
+ $dest_width = $width;
+ $dest_height = $height;
+ }
+ else {
+ $dest_width = intval(( $width * $max ) / $height);
+ $dest_height = $max;
+ }
+ }
+ else {
+ $dest_width = $width;
+ $dest_height = $height;
+ }
+ }
+ }
+ $this->doScaleImage($dest_width,$dest_height);
+ }
+
+ public function scaleImageUp($min) {
+ if(!$this->is_valid())
+ return FALSE;
+
+
+ $width = $this->width;
+ $height = $this->height;
+
+ $dest_width = $dest_height = 0;
+
+ if((! $width)|| (! $height))
+ return FALSE;
+
+ if($width < $min && $height < $min) {
+ if($width > $height) {
+ $dest_width = $min;
+ $dest_height = intval(( $height * $min ) / $width);
+ }
+ else {
+ $dest_width = intval(( $width * $min ) / $height);
+ $dest_height = $min;
+ }
+ }
+ else {
+ if( $width < $min ) {
+ $dest_width = $min;
+ $dest_height = intval(( $height * $min ) / $width);
+ }
+ else {
+ if( $height < $min ) {
+ $dest_width = intval(( $width * $min ) / $height);
+ $dest_height = $min;
+ }
+ else {
+ $dest_width = $width;
+ $dest_height = $height;
+ }
+ }
+ }
+ $this->doScaleImage($dest_width,$dest_height);
+ }
+
+ public function scaleImageSquare($dim) {
+ if(!$this->is_valid())
+ return FALSE;
+ $this->doScaleImage($dim,$dim);
+ }
+
+
+
+
+ public function orient($filename) {
+
+ /**
+ * This function is a bit unusual, because it is operating on a file, but you must
+ * first create an image from that file to initialise the type and check validity
+ * of the image.
+ */
+
+ if(! $this->is_valid())
+ return FALSE;
+
+ if((! function_exists('exif_read_data')) || ($this->getType() !== 'image/jpeg'))
+ return;
+
+ $exif = @exif_read_data($filename);
+ if($exif) {
+ $ort = $exif['Orientation'];
+
+ switch($ort)
+ {
+ case 1: // nothing
+ break;
+
+ case 2: // horizontal flip
+ $this->flip();
+ break;
+
+ case 3: // 180 rotate left
+ $this->rotate(180);
+ break;
+
+ case 4: // vertical flip
+ $this->flip(false, true);
+ break;
+
+ case 5: // vertical flip + 90 rotate right
+ $this->flip(false, true);
+ $this->rotate(-90);
+ break;
+
+ case 6: // 90 rotate right
+ $this->rotate(-90);
+ break;
+
+ case 7: // horizontal flip + 90 rotate right
+ $this->flip();
+ $this->rotate(-90);
+ break;
+
+ case 8: // 90 rotate left
+ $this->rotate(90);
+ break;
+ }
+ }
+ }
+
+
+ public function save($arr) {
+
+ $p = array();
+
+ $p['aid'] = ((intval($arr['aid'])) ? intval($arr['aid']) : 0);
+ $p['uid'] = ((intval($arr['uid'])) ? intval($arr['uid']) : 0);
+ $p['xchan'] = (($arr['xchan']) ? $arr['xchan'] : '');
+ $p['resource_id'] = (($arr['resource_id']) ? $arr['resource_id'] : '');
+ $p['filename'] = (($arr['filename']) ? $arr['filename'] : '');
+ $p['album'] = (($arr['album']) ? $arr['album'] : '');
+ $p['scale'] = ((intval($arr['scale'])) ? intval($arr['scale']) : 0);
+ $p['photo_flags'] = ((intval($arr['photo_flags'])) ? intval($arr['photo_flags']) : 0);
+ $p['allow_cid'] = (($arr['allow_cid']) ? $arr['allow_cid'] : '');
+ $p['allow_gid'] = (($arr['allow_gid']) ? $arr['allow_gid'] : '');
+ $p['deny_cid'] = (($arr['deny_cid']) ? $arr['deny_cid'] : '');
+ $p['deny_gid'] = (($arr['deny_gid']) ? $arr['deny_gid'] : '');
+
+ // temporary until we get rid of photo['profile'] and just use photo['photo_flags']
+ // but this will require updating all existing photos in the DB.
+
+ $p['profile'] = (($p['photo_flags'] & PHOTO_PROFILE) ? 1 : 0);
+
+
+ $x = q("select id from photo where resource_id = '%s' and uid = %d and xchan = '%s' and `scale` = %d limit 1",
+ dbesc($p['resource_id']),
+ intval($p['uid']),
+ dbesc($p['xchan']),
+ intval($p['scale'])
+ );
+ if($x) {
+ $r = q("UPDATE `photo` set
+ `aid` = %d,
+ `uid` = %d,
+ `xchan` = '%s',
+ `resource_id` = '%s',
+ `created` = '%s',
+ `edited` = '%s',
+ `filename` = '%s',
+ `type` = '%s',
+ `album` = '%s',
+ `height` = %d,
+ `width` = %d,
+ `data` = '%s',
+ `size` = %d,
+ `scale` = %d,
+ `profile` = %d,
+ `photo_flags` = %d,
+ `allow_cid` = '%s',
+ `allow_gid` = '%s',
+ `deny_cid` = '%s',
+ `deny_gid` = '%s'
+ where id = %d limit 1",
+
+ intval($p['aid']),
+ intval($p['uid']),
+ dbesc($p['xchan']),
+ dbesc($p['resource_id']),
+ dbesc(datetime_convert()),
+ dbesc(datetime_convert()),
+ dbesc(basename($p['filename'])),
+ dbesc($this->getType()),
+ dbesc($p['album']),
+ intval($this->getHeight()),
+ intval($this->getWidth()),
+ dbesc($this->imageString()),
+ intval(strlen($this->imageString())),
+ intval($p['scale']),
+ intval($p['profile']),
+ intval($p['photo_flags']),
+ dbesc($p['allow_cid']),
+ dbesc($p['allow_gid']),
+ dbesc($p['deny_cid']),
+ dbesc($p['deny_gid']),
+ intval($x[0]['id'])
+ );
+ }
+ else {
+ $r = q("INSERT INTO `photo`
+ ( `aid`, `uid`, `xchan`, `resource_id`, `created`, `edited`, `filename`, type, `album`, `height`, `width`, `data`, `size`, `scale`, `profile`, `photo_flags`, `allow_cid`, `allow_gid`, `deny_cid`, `deny_gid` )
+ VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', %d, %d, %d, %d, '%s', '%s', '%s', '%s' )",
+ intval($p['aid']),
+ intval($p['uid']),
+ dbesc($p['xchan']),
+ dbesc($p['resource_id']),
+ dbesc(datetime_convert()),
+ dbesc(datetime_convert()),
+ dbesc(basename($p['filename'])),
+ dbesc($this->getType()),
+ dbesc($p['album']),
+ intval($this->getHeight()),
+ intval($this->getWidth()),
+ dbesc($this->imageString()),
+ intval(strlen($this->imageString())),
+ intval($p['scale']),
+ intval($p['profile']),
+ intval($p['photo_flags']),
+ dbesc($p['allow_cid']),
+ dbesc($p['allow_gid']),
+ dbesc($p['deny_cid']),
+ dbesc($p['deny_gid'])
+ );
+ }
+ return $r;
+ }
+
+ public function store($aid, $uid, $xchan, $rid, $filename, $album, $scale, $profile = 0, $allow_cid = '', $allow_gid = '', $deny_cid = '', $deny_gid = '') {
+
+ $x = q("select id from photo where `resource_id` = '%s' and uid = %d and `xchan` = '%s' and `scale` = %d limit 1",
+ dbesc($rid),
+ intval($uid),
+ dbesc($xchan),
+ intval($scale)
+ );
+ if(count($x)) {
+ $r = q("UPDATE `photo`
+ set `aid` = %d,
+ `uid` = %d,
+ `xchan` = '%s',
+ `resource_id` = '%s',
+ `created` = '%s',
+ `edited` = '%s',
+ `filename` = '%s',
+ `type` = '%s',
+ `album` = '%s',
+ `height` = %d,
+ `width` = %d,
+ `data` = '%s',
+ `size` = %d,
+ `scale` = %d,
+ `profile` = %d,
+ `allow_cid` = '%s',
+ `allow_gid` = '%s',
+ `deny_cid` = '%s',
+ `deny_gid` = '%s'
+ where id = %d limit 1",
+
+ intval($aid),
+ intval($uid),
+ dbesc($xchan),
+ dbesc($rid),
+ dbesc(datetime_convert()),
+ dbesc(datetime_convert()),
+ dbesc(basename($filename)),
+ dbesc($this->getType()),
+ dbesc($album),
+ intval($this->getHeight()),
+ intval($this->getWidth()),
+ dbesc($this->imageString()),
+ intval(strlen($this->imageString())),
+ intval($scale),
+ intval($profile),
+ dbesc($allow_cid),
+ dbesc($allow_gid),
+ dbesc($deny_cid),
+ dbesc($deny_gid),
+ intval($x[0]['id'])
+ );
+ }
+ else {
+ $r = q("INSERT INTO `photo`
+ ( `aid`, `uid`, `xchan`, `resource_id`, `created`, `edited`, `filename`, type, `album`, `height`, `width`, `data`, `size`, `scale`, `profile`, `allow_cid`, `allow_gid`, `deny_cid`, `deny_gid` )
+ VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', %d, %d, %d, '%s', '%s', '%s', '%s' )",
+ intval($aid),
+ intval($uid),
+ dbesc($xchan),
+ dbesc($rid),
+ dbesc(datetime_convert()),
+ dbesc(datetime_convert()),
+ dbesc(basename($filename)),
+ dbesc($this->getType()),
+ dbesc($album),
+ intval($this->getHeight()),
+ intval($this->getWidth()),
+ dbesc($this->imageString()),
+ intval(strlen($this->imageString())),
+ intval($scale),
+ intval($profile),
+ dbesc($allow_cid),
+ dbesc($allow_gid),
+ dbesc($deny_cid),
+ dbesc($deny_gid)
+ );
+ }
+ return $r;
+ }
+
+}
+
+
+
+
+
+
+
+
+/**
+ * Guess image mimetype from filename or from Content-Type header
+ *
+ * @arg $filename string Image filename
+ * @arg $fromcurl boolean Check Content-Type header from curl request
+ */
+
+function guess_image_type($filename, $headers = '') {
+ logger('Photo: guess_image_type: '.$filename . ($fromcurl?' from curl headers':''), LOGGER_DEBUG);
+ $type = null;
+ if ($headers) {
+ $a = get_app();
+ $hdrs=array();
+ $h = explode("\n",$headers);
+ foreach ($h as $l) {
+ list($k,$v) = array_map("trim", explode(":", trim($l), 2));
+ $hdrs[$k] = $v;
+ }
+ if (array_key_exists('Content-Type', $hdrs))
+ $type = $hdrs['Content-Type'];
+ }
+ if (is_null($type)){
+// FIXME!!!!
+ // Guessing from extension? Isn't that... dangerous?
+ if(class_exists('Imagick') && file_exists($filename) && is_readable($filename)) {
+ /**
+ * Well, this not much better,
+ * but at least it comes from the data inside the image,
+ * we won't be tricked by a manipulated extension
+ */
+ $image = new Imagick($filename);
+ $type = $image->getImageMimeType();
+ } else {
+ $ext = pathinfo($filename, PATHINFO_EXTENSION);
+ $ph = photo_factory('');
+ $types = $ph->supportedTypes();
+ $type = "image/jpeg";
+ foreach ($types as $m=>$e){
+ if ($ext==$e) $type = $m;
+ }
+ }
+ }
+ logger('Photo: guess_image_type: type='.$type, LOGGER_DEBUG);
+ return $type;
+
+}
+
+function import_profile_photo($photo,$xchan,$thing = false) {
+
+ $a = get_app();
+
+ $flags = (($thing) ? PHOTO_THING : PHOTO_XCHAN);
+ $album = (($thing) ? 'Things' : 'Contact Photos');
+
+ logger('import_profile_photo: updating channel photo from ' . $photo . ' for ' . $xchan, LOGGER_DEBUG);
+
+ if($thing)
+ $hash = photo_new_resource();
+ else {
+ $r = q("select resource_id from photo where xchan = '%s' and (photo_flags & %d ) and scale = 4 limit 1",
+ dbesc($xchan),
+ intval(PHOTO_XCHAN)
+ );
+ if($r) {
+ $hash = $r[0]['resource_id'];
+ }
+ else {
+ $hash = photo_new_resource();
+ }
+ }
+
+ $photo_failure = false;
+ $img_str = '';
+
+ if($photo) {
+ $filename = basename($photo);
+ $type = guess_image_type($photo,true);
+
+ if(! $type)
+ $type = 'image/jpeg';
+
+ $result = z_fetch_url($photo,true);
+
+ if($result['success'])
+ $img_str = $result['body'];
+ }
+
+ $img = photo_factory($img_str, $type);
+ if($img->is_valid()) {
+ $width = $img->getWidth();
+ $height = $img->getHeight();
+
+ if($width && $height) {
+ if(($width / $height) > 1.2) {
+ // crop out the sides
+ $margin = $width - $height;
+ $img->cropImage(175,($margin / 2),0,$height,$height);
+ }
+ elseif(($height / $width) > 1.2) {
+ // crop out the bottom
+ $margin = $height - $width;
+ $img->cropImage(175,0,0,$width,$width);
+
+ }
+ else {
+ $img->scaleImageSquare(175);
+ }
+
+ }
+ else
+ $photo_failure = true;
+
+ $p = array('xchan' => $xchan,'resource_id' => $hash, 'filename' => basename($photo), 'album' => $album, 'photo_flags' => $flags, 'scale' => 4);
+
+ $r = $img->save($p);
+
+ if($r === false)
+ $photo_failure = true;
+
+ $img->scaleImage(80);
+ $p['scale'] = 5;
+
+ $r = $img->save($p);
+
+ if($r === false)
+ $photo_failure = true;
+
+ $img->scaleImage(48);
+ $p['scale'] = 6;
+
+ $r = $img->save($p);
+
+ if($r === false)
+ $photo_failure = true;
+
+ $photo = $a->get_baseurl() . '/photo/' . $hash . '-4';
+ $thumb = $a->get_baseurl() . '/photo/' . $hash . '-5';
+ $micro = $a->get_baseurl() . '/photo/' . $hash . '-6';
+ }
+ else {
+ logger('import_profile_photo: invalid image from ' . $photo);
+ $photo_failure = true;
+ }
+ if($photo_failure) {
+ $photo = $a->get_baseurl() . '/' . get_default_profile_photo();
+ $thumb = $a->get_baseurl() . '/' . get_default_profile_photo(80);
+ $micro = $a->get_baseurl() . '/' . get_default_profile_photo(48);
+ $type = 'image/jpeg';
+ }
+
+ return(array($photo,$thumb,$micro,$type,$photo_failure));
+
+}
+
+
+
+function import_channel_photo($photo,$type,$aid,$uid) {
+
+ $a = get_app();
+
+ logger('import_channel_photo: importing channel photo for ' . $uid, LOGGER_DEBUG);
+
+ $hash = photo_new_resource();
+
+ $photo_failure = false;
+
+
+ $filename = $hash;
+
+ $img = photo_factory($photo, $type);
+ if($img->is_valid()) {
+
+ $img->scaleImageSquare(175);
+
+ $p = array('aid' => $aid, 'uid' => $uid, 'resource_id' => $hash, 'filename' => $filename, 'album' => t('Profile Photos'), 'photo_flags' => PHOTO_PROFILE, 'scale' => 4);
+
+ $r = $img->save($p);
+
+ if($r === false)
+ $photo_failure = true;
+
+ $img->scaleImage(80);
+ $p['scale'] = 5;
+
+ $r = $img->save($p);
+
+ if($r === false)
+ $photo_failure = true;
+
+ $img->scaleImage(48);
+ $p['scale'] = 6;
+
+ $r = $img->save($p);
+
+ if($r === false)
+ $photo_failure = true;
+
+ }
+ else {
+ logger('import_channel_photo: invalid image.');
+ $photo_failure = true;
+ }
+
+ return(($photo_failure)? false : true);
+
+}