liberty
[ class tree: liberty ] [ index: liberty ] [ all elements ]

Source for file mime.image.php

Documentation is available at mime.image.php

  1. <?php
  2. /**
  3.  * @version        $Header$
  4.  *
  5.  * @author        xing  <xing@synapse.plus.com>
  6.  * @version        $Revision$
  7.  *  created        Thursday May 08, 2008
  8.  * @package        liberty
  9.  * @subpackage    liberty_mime_handler
  10.  ***/
  11.  
  12. /**
  13.  * setup
  14.  */
  15. global $gLibertySystem, $gBitSystem;
  16.  
  17. /**
  18.  *  This is the name of the plugin - max char length is 16
  19.  * As a naming convention, the liberty mime handler definition should start with:
  20.  * PLUGIN_MIME_GUID_
  21.  */
  22. define( 'PLUGIN_MIME_GUID_IMAGE', 'mimeimage' );
  23.  
  24. $pluginParams = array (
  25.     // Set of functions and what they are called in this paricular plugin
  26.     // Use the GUID as your namespace
  27.     'verify_function'     => 'mime_default_verify',
  28.     'store_function'      => 'mime_image_store',
  29.     'update_function'     => 'mime_image_update',
  30.     'load_function'       => 'mime_image_load',
  31.     'download_function'   => 'mime_default_download',
  32.     'expunge_function'    => 'mime_default_expunge',
  33.     'help_function'       => 'mime_image_help',
  34.     // Brief description of what the plugin does
  35.     'title'               => 'Advanced Image Processing',
  36.     'description'         => 'Extract image meta data and display relevant information to the user and pick individual display options for images.',
  37.     // Templates to display the files
  38.     'view_tpl'            => 'bitpackage:liberty/mime/image/view.tpl',
  39.     'attachment_tpl'      => 'bitpackage:liberty/mime/image/attachment.tpl',
  40.     // url to page with options for this plugin
  41.     'plugin_settings_url' => LIBERTY_PKG_URL.'admin/plugins/mime_image.php',
  42.     // This needs to be specified by plugins that are included by other plugins
  43.     'file_name'          => 'mime.image.php',
  44.     // This should be the same for all mime plugins
  45.     'plugin_type'         => MIME_PLUGIN,
  46.     // Set this to TRUE if you want the plugin active right after installation
  47.     'auto_activate'       => FALSE,
  48.     // Help page on bitweaver.org
  49.     //'help_page'           => 'LibertyMime+Image+Plugin',
  50.     // this should pick up all image
  51.     'mimetypes'           => array(
  52.         '#image/.*#i',
  53.     ),
  54. );
  55. // currently, there's only one option in the image edit file - panorama image setting
  56. if( $gBitSystem->isFeatureActive( 'mime_image_panoramas' )) {
  57.     $pluginParams['edit_tpl'] =  'bitpackage:liberty/mime/image/edit.tpl';
  58. }
  59. $gLibertySystem->registerPlugin( PLUGIN_MIME_GUID_IMAGE, $pluginParams );
  60.  
  61. /**
  62.  * Store the data in the database
  63.  *
  64.  * @param array $pStoreRow File data needed to store details in the database - sanitised and generated in the verify function
  65.  * @access public
  66.  * @return TRUE on success, FALSE on failure - $pStoreRow[errors] will contain reason
  67.  */
  68. function mime_image_store( &$pStoreRow ) {
  69.     // this will set the correct pluign guid, even if we let default handle the store process
  70.     $pStoreRow['attachment_plugin_guid'] = PLUGIN_MIME_GUID_IMAGE;
  71.     $pStoreRow['log'] = array();
  72.  
  73.     // if storing works, we process the image
  74.     if( $ret = mime_default_store( $pStoreRow )) {
  75.         if( !mime_image_store_exif_data( $pStoreRow )) {
  76.             // if it all goes tits up, we'll know why
  77.             $pStoreRow['errors'] = $pStoreRow['log'];
  78.             $ret = FALSE;
  79.         }
  80.     }
  81.     return $ret;
  82. }
  83.  
  84. /**
  85.  * mime_image_update update file information in the database if there were changes.
  86.  *
  87.  * @param array $pStoreRow File data needed to update details in the database
  88.  * @access public
  89.  * @return TRUE on success, FALSE on failure - $pStoreRow[errors] will contain reason
  90.  */
  91. function mime_image_update( &$pStoreRow, $pParams = NULL ) {
  92.     global $gThumbSizes, $gBitSystem;
  93.  
  94.     $ret = TRUE;
  95.  
  96.     // this will set the correct pluign guid, even if we let default handle the store process
  97.     $pStoreRow['attachment_plugin_guid'] = PLUGIN_MIME_GUID_IMAGE;
  98.  
  99.     // if storing works, we process the image
  100.     if( !empty( $pStoreRow['upload'] ) && $ret = mime_default_update( $pStoreRow )) {
  101.         if( !mime_image_store_exif_data( $pStoreRow )) {
  102.             // if it all goes tits up, we'll know why
  103.             $pStoreRow['errors'] = $pStoreRow['log'];
  104.             $ret = FALSE;
  105.         }
  106.     } elseif( $gBitSystem->isFeatureActive( 'mime_image_panoramas' ) && !empty( $pParams['preference']['is_panorama'] ) && empty( $pStoreRow['thumbnail_url']['panorama'] )) {
  107.         if( !mime_image_create_panorama( $pStoreRow )) {
  108.             $ret = FALSE;
  109.         }
  110.     } elseif( empty( $pParams['preference']['is_panorama'] ) && !empty( $pStoreRow['thumbnail_url']['panorama'] )) {
  111.         // we remove the panorama setting in the database and the panorama thumb
  112.         if( LibertyAttachable::validateStoragePath( STORAGE_PKG_PATH.$pStoreRow['thumbnail_url']['panorama'] )) {
  113.             @unlink( STORAGE_PKG_PATH.$pStoreRow['thumbnail_url']['panorama'] );
  114.         }
  115.     }
  116.  
  117.     return $ret;
  118. }
  119.  
  120. /**
  121.  * Load file data from the database
  122.  *
  123.  * @param array $pFileHash Contains all file information
  124.  * @param array $pPrefs Attachment preferences taken liberty_attachment_prefs
  125.  * @param array $pParams Parameters for loading the plugin - e.g.: might contain values such as thumbnail size from the view page
  126.  * @access public
  127.  * @return TRUE on success, FALSE on failure - $pStoreRow[errors] will contain reason
  128.  */
  129. function mime_image_load( &$pFileHash, &$pPrefs, $pParams = NULL ) {
  130.     global $gBitSystem;
  131.     // don't load a mime image if we don't have an image for this file
  132.     if( $ret = mime_default_load( $pFileHash, $pPrefs, $pParams )) {
  133.         // fetch meta data from the db
  134.         $ret['meta'] = LibertyMime::getMetaData( $ret['attachment_id'], "EXIF" );
  135.  
  136.         // if we have GPS data and geo is active, we calculate geo stuff
  137.         if( $gBitSystem->isPackageActive( 'geo' ) && ( $ret['gps'] = LibertyMime::getMetaData( $ret['attachment_id'], "GPS" ))) {
  138.             // longitude
  139.             if( !empty( $ret['gps']['gpslongitude'] )) {
  140.                 $ret['geo']['lng'] = $ret['gps']['gpslongitude'];
  141.                 if( !empty( $ret['gps']['gpslongituderef'] ) && $ret['gps']['gpslongituderef'] == 'W' ) {
  142.                     $ret['geo']['lng'] = 0 - $ret['geo']['lng'];
  143.                 }
  144.             }
  145.             // latitude
  146.             if( !empty( $ret['gps']['gpslatitude'] )) {
  147.                 $ret['geo']['lat'] = $ret['gps']['gpslatitude'];
  148.                 if( !empty( $ret['gps']['gpslatituderef'] ) && $ret['gps']['gpslatituderef'] == 'S' ) {
  149.                     $ret['geo']['lat'] = 0 - $ret['geo']['lat'];
  150.                 }
  151.             }
  152.             // set sea level data when available
  153.             if( !empty( $ret['gps']['gpsaltitude'] )) {
  154.                 list( $dividend, $divisor ) = explode( "/", $ret['gps']['gpsaltitude'] );
  155.                 $ret['geo']['amsl'] = $dividend / $divisor;
  156.                 $ret['geo']['amsl_unit'] = 'm';
  157.             }
  158.  
  159.             // final check to see if we have enough data
  160.             if( empty( $ret['geo']['lng'] ) || empty( $ret['geo']['lat'] )) {
  161.                 unset( $ret['geo'] );
  162.             }
  163.         }
  164.  
  165.         // check for panorama image
  166.         if( isset( $ret['source_file'] ) && is_file( dirname( $ret['source_file'] )."/thumbs/panorama.jpg" )) {
  167.             // if the panorama doesn't have 180⁰ vertical field of view we will restrict up / down movement
  168.             if(( $ret['pano'] = LibertyMime::getMetaData( $ret['attachment_id'], "PANO" )) && !empty( $ret['pano']['aspect'] )) {
  169.                 // calculation based on logarythmic regression curve
  170.                 $ret['pano']['pa'] = round( 40 - 31 * log( $ret['pano']['aspect'] - 1.4 ));
  171.                 if( $ret['pano']['pa'] > 49 ) {
  172.                     $ret['pano']['pa'] = 90;
  173.                 } elseif( $ret['pano']['pa'] < 0 ) {
  174.                     $ret['pano']['pa'] = 0;
  175.                 }
  176.             }
  177.             $ret['thumbnail_url']['panorama'] = storage_path_to_url( dirname( $ret['source_file'] )."/thumbs/panorama.jpg" );
  178.         }
  179.     }
  180.     return $ret;
  181. }
  182.  
  183. /**
  184.  * mime_image_store_exif_data Process a JPEG and store its EXIF data as meta data.
  185.  *
  186.  * @param array $pFileHash file details.
  187.  * @param array $pFileHash[upload] should contain a complete hash from $_FILES
  188.  * @access public
  189.  * @return TRUE on success, FALSE on failure
  190.  */
  191. function mime_image_store_exif_data( $pFileHash ) {
  192.     global $gBitSystem;
  193.     if( !empty( $pFileHash['upload'] )) {
  194.         $upload = &$pFileHash['upload'];
  195.     }
  196.  
  197.     if( @BitBase::verifyId( $pFileHash['attachment_id'] ) && $exifHash = mime_image_get_exif_data( $upload ) ) {
  198.         // only makes sense to store the GPS data if we at least have latitude and longitude
  199.         if( !empty( $exifHash['GPS'] )) {
  200.             LibertyMime::storeMetaData( $pFileHash['attachment_id'], 'GPS', $exifHash['GPS'] );
  201.         }
  202.  
  203.         if( !empty( $exifHash['EXIF'] )) {
  204. //            LibertyMime::storeMetaData( $pFileHash['attachment_id'], 'EXIF', $exifHash['EXIF'] );
  205.         }
  206.     }
  207.  
  208.     return TRUE;
  209. }
  210.  
  211. /**
  212.  * mime_image_get_exif_data fetch meta data from uploaded image
  213.  *
  214.  * @param array $pUpload uploaded file data
  215.  * @access public
  216.  * @return array filled with exif goodies
  217.  */
  218. function mime_image_get_exif_data( $pUpload ) {
  219.     $exifHash = array();
  220.     if( function_exists( 'exif_read_data' ) && !empty( $pUpload['source_file'] ) && is_file( $pUpload['source_file'] ) && preg_match( "#/(jpe?g|tiff)#i", $pUpload['type'] )) {
  221.         // exif_read_data can be noisy due to crappy files, e.g. "Incorrect APP1 Exif Identifier Code" etc...
  222.         $exifHash = @exif_read_data( $pUpload['source_file'], 0, TRUE );
  223.  
  224.         // extract more information if we can find it
  225.         if( ini_get( 'short_open_tag' )) {
  226.             require_once UTIL_PKG_PATH.'jpeg_metadata_tk/JPEG.php';
  227.             require_once UTIL_PKG_PATH.'jpeg_metadata_tk/JFIF.php';
  228.             require_once UTIL_PKG_PATH.'jpeg_metadata_tk/PictureInfo.php';
  229.             require_once UTIL_PKG_PATH.'jpeg_metadata_tk/XMP.php';
  230.             require_once UTIL_PKG_PATH.'jpeg_metadata_tk/EXIF.php';
  231.  
  232.             // Retrieve the header information from the JPEG file
  233.             $jpeg_header_data = get_jpeg_header_data( $pUpload['source_file'] );
  234.  
  235.             // Retrieve EXIF information from the JPEG file
  236.             $Exif_array = get_EXIF_JPEG( $pUpload['source_file'] );
  237.  
  238.             // Retrieve XMP information from the JPEG file
  239.             $XMP_array = read_XMP_array_from_text( get_XMP_text( $jpeg_header_data ) );
  240.  
  241.             // Retrieve Photoshop IRB information from the JPEG file
  242.             $IRB_array = get_Photoshop_IRB( $jpeg_header_data );
  243.             if( !empty( $exifHash['IFD0']['Software'] ) && preg_match( '/photoshop/i', $exifHash['IFD0']['Software'] ) ) {
  244.                 require_once UTIL_PKG_PATH.'jpeg_metadata_tk/Photoshop_File_Info.php';
  245.                 // Retrieve Photoshop File Info from the three previous arrays
  246.                 $psFileInfo = get_photoshop_file_info( $Exif_array, $XMP_array, $IRB_array );
  247.  
  248.                 if( !empty( $psFileInfo['headline'] ) ) {
  249.                     $exifHash['headline'] = $psFileInfo['headline'];
  250.                 }
  251.  
  252.                 if( !empty( $psFileInfo['caption'] ) ) {
  253.                     $exifHash['caption'] = $psFileInfo['caption'];
  254.                 }
  255.             }
  256.         }
  257.  
  258.         // only makes sense to store the GPS data if we at least have latitude and longitude
  259.         if( !empty( $exifHash['GPS'] )) {
  260.             // store GPS coordinates as deg decimal float
  261.             $gpsConv = array( 'GPSLatitude', 'GPSDestLatitude', 'GPSLongitude', 'GPSDestLongitude' );
  262.             foreach( $gpsConv as $conv ) {
  263.                 if( !empty( $exifHash['GPS'][$conv] ) && is_array( $exifHash['GPS'][$conv] )) {
  264.                     $exifHash['GPS'][$conv] = mime_image_convert_exifgps( $exifHash['GPS'][$conv] );
  265.                 }
  266.             }
  267.         }
  268.     }
  269.  
  270.     return $exifHash;
  271. }
  272.  
  273. /**
  274.  * mime_image_convert_exifgps GPS EIXF data is stored as fractions in an array. here we convert this to a degree decimal float value for easy storing
  275.  *
  276.  * @param array $pParams array of positional data in fractions form EXIF tag
  277.  * @access public
  278.  * @return numeric value of positional data
  279.  */
  280. function mime_image_convert_exifgps( $pParams ) {
  281.     $ret = 0;
  282.     if( !empty( $pParams ) && is_array( $pParams ) && count( $pParams ) == 3 ) {
  283.         list( $lng['deg'], $lng['min'], $lng['sec'] ) = array_values( $pParams );
  284.         foreach( $lng as $key => $fraction ) {
  285.             list( $dividend, $divisor ) = explode( "/", $fraction );
  286.             $num = $dividend / $divisor;
  287.             if( $key == 'min' ) {
  288.                 $num = $num / 60;
  289.             } elseif( $key == 'sec' ) {
  290.                 $num = $num / 3600;
  291.             }
  292.             $ret += $num;
  293.         }
  294.     }
  295.     return $ret;
  296. }
  297.  
  298. /**
  299.  * mime_image_create_panorama
  300.  *
  301.  * @param array $pStoreRow 
  302.  * @access public
  303.  * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
  304.  */
  305. function mime_image_create_panorama( &$pStoreRow ) {
  306.     global $gBitSystem, $gThumbSizes;
  307.     // we know the panorama image will be a jpeg, so we don't need the canThumbFunc check here
  308.     if(( $panoramaFunc = liberty_get_function( 'panorama' )) && !empty( $pStoreRow['source_file'] ) && is_file( $pStoreRow['source_file'] )) {
  309.         // the panorama has to be a jpg
  310.         $gBitSystem->setConfig( 'liberty_thumbnail_format', 'jpg' );
  311.         $width = $gBitSystem->getConfig( 'mime_image_panorama_width', 3000 );
  312.         $gThumbSizes['panorama'] = array( $width, $width / 2 );
  313.  
  314.         // for the panorama, we will force a quality lower than 75 to reduce image size
  315.         if( $gBitSystem->getConfig( 'liberty_thumbnail_quality', 85 ) > 75 ) {
  316.             $gBitSystem->setConfig( 'liberty_thumbnail_quality', 75 );
  317.         }
  318.  
  319.         $genHash = array(
  320.             'attachment_id'   => $pStoreRow['attachment_id'],
  321.             'dest_branch'      => liberty_mime_get_storage_branch( array( 'sub_dir' => $pStoreRow['attachment_id'], 'user_id' =>$pStoreRow['user_id'], 'package' => liberty_mime_get_storage_sub_dir_name( $pStoreRow ) ) ),
  322.             'file_name'       => dirname( $pStoreRow['file_name'] )."/",
  323.             'source_file'     => $pStoreRow['source_file'],
  324.             'type'            => $pStoreRow['mime_type'],
  325.             'thumbnail_sizes' => array( 'panorama' ),
  326.         );
  327.         if( liberty_generate_thumbnails( $genHash )) {
  328.             // we want to modify the panorama
  329.             $genHash['source_file'] = $genHash['icon_thumb_path'];
  330.             if( !$panoramaFunc( $genHash )) {
  331.                 $pStoreRow['errors']['panorama'] = $genHash['error'];
  332.             }
  333.         }
  334.  
  335.         return( empty( $pStoreRow['errors'] ));
  336.     }
  337. }
  338.  
  339. /**
  340.  * liberty_magickwand_panorama_image - strictly speaking, this belongs in one of the image processing plugin files, but we'll leave it here for the moment
  341.  *
  342.  * @param array $pFileHash File hash - souce_file is required
  343.  * @param array $pOptions 
  344.  * @access public
  345.  * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure
  346.  */
  347. function liberty_magickwand_panorama_image( &$pFileHash, $pOptions = array() ) {
  348.     $magickWand = NewMagickWand();
  349.     $pFileHash['error'] = NULL;
  350.     if( !empty( $pFileHash['source_file'] ) && is_file( $pFileHash['source_file'] )) {
  351.         if( !$pFileHash['error'] = liberty_magickwand_check_error( MagickReadImage( $magickWand, $pFileHash['source_file'] ), $magickWand )) {
  352.             // calculate border width
  353.             $iwidth  = round( MagickGetImageWidth( $magickWand ));
  354.             $iheight = round( MagickGetImageHeight( $magickWand ));
  355.             $aspect  = $iwidth / $iheight;
  356.             $metaHash = array(
  357.                 'width'  => $iwidth,
  358.                 'height' => $iheight,
  359.                 'aspect' => $aspect,
  360.             );
  361.             // store original file information that we can adjust the viewer
  362.             LibertyMime::storeMetaData( $pFileHash['attachment_id'], 'PANO', $metaHash );
  363.             // we need to pad the image if the aspect ratio is not 2:1 (give it a wee bit of leeway that we don't add annoying borders if not really needed)
  364.             if( $aspect > 2.1 || $aspect < 1.9 ) {
  365.                 $bwidth = $bheight = 0;
  366.                 if( $aspect > 2 ) {
  367.                     $bheight = round((( $iwidth / 2 ) - $iheight ) / 2 );
  368.                 } else {
  369.                     $bwidth = round((( $iheight / 2 ) - $iwidth ) / 2 );
  370.                 }
  371.                 // if the ratio has nothing to do with a panorama image - i.e. gives us a negative number here, we won't generate a panorama image
  372.                 if( $bheight > 0 ) {
  373.                     $pixelWand = NewPixelWand();
  374.                     PixelSetColor( $pixelWand, ( !empty( $pOptions['background'] ) ? $pOptions['background'] : 'black' ));
  375.                     if( !$pFileHash['error'] = liberty_magickwand_check_error( MagickBorderImage( $magickWand, $pixelWand, $bwidth, $bheight ), $magickWand )) {
  376.                         if( !$pFileHash['error'] = liberty_magickwand_check_error( MagickWriteImage( $magickWand, $pFileHash['source_file'] ), $magickWand )) {
  377.                             // yay!
  378.                         }
  379.                     }
  380.                     DestroyPixelWand( $pixelWand );
  381.                 }
  382.             }
  383.         }
  384.     }
  385.     DestroyMagickWand( $magickWand );
  386.     return( empty( $pFileHash['error'] ));
  387. }
  388.  
  389. /**
  390.  * mime_image_help
  391.  *
  392.  * @access public
  393.  * @return string 
  394.  */
  395. function mime_image_help() {
  396.     $help =
  397.         tra( "If you have a panoramic image and you are using <strong>{attachment}</strong> to insert it, you can use <strong>panosize</strong> as you would with the size parameter to specify the size." )."<br />"
  398.         .tra( "Example:" ).' '."{attachment id='13' panosize='small'}";
  399.     return $help;
  400. }
  401. ?>

Documentation generated on Wed, 29 Jul 2015 13:57:07 +0000 by phpDocumentor 1.5.0-lsces