moxiecode / plupload

Plupload is JavaScript API for building file uploaders. It supports multiple file selection, file filtering, chunked upload, client side image downsizing and when necessary can fallback to alternative runtimes, like Flash and Silverlight.
http://www.plupload.com
GNU Affero General Public License v3.0
5.63k stars 1.43k forks source link

Preserve IPTC data #773

Closed FletcherPink closed 11 years ago

FletcherPink commented 11 years ago

Hello, I use Plupload to upload images containing IPTC information. However, once uploaded, the images no longer contain the IPTC information. Is there a way to make to keep the data? I tried to incorporate this:

function iptc_make_tag($rec, $data, $value)
{
    $length = strlen($value);
    $retval = chr(0x1C) . chr($rec) . chr($data);
    if($length < 0x8000)
    {
        $retval .= chr($length >> 8) .  chr($length & 0xFF);
    }
    else
    {
        $retval .= chr(0x80) . 
                        chr(0x04) . 
                        chr(($length >> 24) & 0xFF) . 
                        chr(($length >> 16) & 0xFF) . 
                        chr(($length >> 8) & 0xFF) . 
                        chr($length & 0xFF);
    }
    return $retval . $value;
}
$path = $_FILES['file']['tmp_name'];
getimagesize($path,$info);
$iptc = iptcparse ($info["APP13"]);
$description = $iptc['2#120'][0];

$iptc = array('2#116' => iconv('UTF-8', 'macintosh', '© N. XXXX - xxxx'), '2#120' => iconv('UTF-8', 'macintosh',$description));
$data = '';     
foreach($iptc as $tag => $string)
{
    $tag = substr($tag, 2);
    $data .= iptc_make_tag(2, $tag, $string);
}
$content_iptc = iptcembed($data, $path);
$image = fopen($path, "wb");
fwrite($image, $content_iptc);
close($image);  

But without success. Do you have any idea to help me? thank you

jayarjo commented 11 years ago

Can you share typical image that fails to retain iptc data?

FletcherPink commented 11 years ago

Thank you for your interest. Here, below, is an example of IPTC data within an image. I must tell you that during the upload, several manipulation are made: cropping photos, thumbnail creation, application of watermarking and writing database. Would you like me to post the contents of the treatment?

example

jayarjo commented 11 years ago

Ehm... what you mean during the upload?Do you do those manipulations client-side or after file is uploaded? I'm asking because we actually do preserve IPTC, I just wanted to check why it doesn't get preserved in your case.

FletcherPink commented 11 years ago

These manipulations are performed on the server side using a php file with the following code:

<?php
    // crops proportionally based on width and height
    function resize($img, $w, $h, $newfilename) 
    {
        //Check if GD extension is loaded
        if (!extension_loaded('gd') && !extension_loaded('gd2')) 
        {
            trigger_error("GD is not loaded", E_USER_WARNING);
            return false;
        }

        //Get Image size info
        $imgInfo = getimagesize($img);
        switch ($imgInfo[2])
        {   
            case 1: $im = imagecreatefromgif($img); break;
            case 2: $im = imagecreatefromjpeg($img);  break;
            case 3: $im = imagecreatefrompng($img); break;
            default:  trigger_error('Unsupported filetype!', E_USER_WARNING);  break;
        }

        //If image dimension is smaller, do not resize
        if ($imgInfo[0] <= $w && $imgInfo[1] <= $h) 
        {
            $nHeight = $imgInfo[1];
            $nWidth = $imgInfo[0];
        }
        else
        {
            // yeah, resize it, but keep it proportional
            if ($w/$imgInfo[0] > $h/$imgInfo[1])
            {
                $nWidth = $imgInfo[0]*($h/$imgInfo[1]);
                $nHeight = $h;           
           }
            else
            {
            $nWidth = $w;
            $nHeight = $imgInfo[1]*($w/$imgInfo[0]);
            }
        }

        $nWidth = round($nWidth);
        $nHeight = round($nHeight);  
        $newImg = imagecreatetruecolor($nWidth, $nHeight);

        //Check if this image is PNG or GIF, then set if Transparent
        if(($imgInfo[2] == 1) OR ($imgInfo[2]==3))
        {
            imagealphablending($newImg, false);
            imagesavealpha($newImg,true);
            $transparent = imagecolorallocatealpha($newImg, 255, 255, 255, 127);
            imagefilledrectangle($newImg, 0, 0, $nWidth, $nHeight, $transparent);
        }

        imagecopyresampled($newImg, $im, 0, 0, 0, 0, $nWidth, $nHeight, $imgInfo[0], $imgInfo[1]);

        //Generate the file, and rename it to $newfilename
        switch ($imgInfo[2]) 
        {
            case 1: imagegif($newImg,$newfilename); break;
            case 2: imagejpeg($newImg,$newfilename,100);  break;
            case 3: imagepng($newImg,$newfilename); break;
            default:  trigger_error('Failed resize image!', E_USER_WARNING);  break;
        }    
        return $newfilename;
    }

    // Watermark
    function WaterMark($img)
    {
        $watermark = imagecreatefrompng('../img/filigranne.png');
    $watermark_largeur = imagesx($watermark);
    $watermark_hauteur = imagesy($watermark);

    $watermark_img = imagecreatetruecolor($watermark_largeur, $watermark_hauteur);
    $img_infos = getimagesize($img);
    $watermark_img = imagecreatefromjpeg($img);

    $x_pos = $img_infos[0] - $watermark_largeur;
    $y_pos = $img_infos[1] - $watermark_hauteur;

    imagecopy($watermark_img, $watermark, 0, 0, 0, 0, $watermark_largeur, $watermark_hauteur);

    imagejpeg($watermark_img, $img);
    imagedestroy($watermark_img);
    imagedestroy($watermark);   
    }

    //Convert Mac OS Roman to Iso
    function macRomanToIso($string)
    {
    return strtr($string,
    "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b
    \x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97
    \x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa1\xa4\xa6\xa7
    \xa8\xab\xac\xae\xaf\xb4\xbb\xbc\xbe\xbf\xc0\xc1
    \xc2\xc7\xc8\xca\xcb\xcc\xd6\xd8\xdb\xe1\xe5\xe6
    \xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf1\xf2\xf3
    \xf4\xf8\xfc\xd2\xd3\xd4\xd5",
    "\xc4\xc5\xc7\xc9\xd1\xd6\xdc\xe1\xe0\xe2\xe4\xe3
    \xe5\xe7\xe9\xe8\xea\xeb\xed\xec\xee\xef\xf1\xf3
    \xf2\xf4\xf6\xf5\xfa\xf9\xfb\xfc\xb0\xa7\xb6\xdf\xae
    \xb4\xa8\xc6\xd8\xa5\xaa\xba\xe6\xf8\xbf\xa1\xac
    \xab\xbb\xa0\xc0\xc3\xf7\xff\xa4\xb7\xc2\xca\xc1
    \xcb\xc8\xcd\xce\xcf\xcc\xd3\xd4\xd2\xda\xdb\xd9
    \xaf\xb8\x22\x22\x27\x27");
    }

    // HTTP headers for no cache etc
    header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
    header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
    header("Cache-Control: no-store, no-cache, must-revalidate");
    header("Cache-Control: post-check=0, pre-check=0", false);
    header("Pragma: no-cache");

    // Settings
    $targetDir = ini_get("upload_tmp_dir") . DIRECTORY_SEPARATOR . "plupload";
    //$targetDir = '../../uploads';

    $cleanupTargetDir = true; // Remove old files
    $maxFileAge = 5 * 3600; // Temp file age in seconds

    // 5 minutes execution time
    @set_time_limit(5 * 60);

    // Get parameters
    $chunk = isset($_REQUEST["chunk"]) ? intval($_REQUEST["chunk"]) : 0;
    $chunks = isset($_REQUEST["chunks"]) ? intval($_REQUEST["chunks"]) : 0;
    $fileName = isset($_REQUEST["name"]) ? $_REQUEST["name"] : '';

    // Clean the fileName for security reasons
    $fileName = preg_replace('/[^\w\._]+/', '_', $fileName);

    // Path image
    $slash = '/';
    $thumbnail_word = 'thumbnail_';
    $targetDir = str_replace(' ', '_', $_REQUEST["name1"]);
    $id_last = $_REQUEST["name2"];
    $repertoire = str_replace('../', '', $targetDir);
    $chemin = $repertoire . $slash . $thumbnail_word . $fileName;
    $photo = $_FILES['file']['tmp_name'];

    //IPTC
    getimagesize($photo,$info);
    $iptc = iptcparse ($info["APP13"]);
    $description = mb_convert_encoding(macRomanToIso($iptc['2#120'][0]),"utf-8", "ISO-8859-1");
    $description = stripcslashes(htmlspecialchars($description,ENT_QUOTES)); 

    // Make sure the fileName is unique but only if chunking is disabled
    if ($chunks < 2 && file_exists($targetDir . DIRECTORY_SEPARATOR . $fileName)) 
    {
        $ext = strrpos($fileName, '.');
    $fileName_a = substr($fileName, 0, $ext);
    $fileName_b = substr($fileName, $ext);

    $count = 1;
    while (file_exists($targetDir . DIRECTORY_SEPARATOR . $fileName_a . '_' . $count . $fileName_b))
        $count++;

    $fileName = $fileName_a . '_' . $count . $fileName_b;
    }

    $filePath = $targetDir . DIRECTORY_SEPARATOR . $fileName;

    // Create target dir
    if (!file_exists($targetDir))
        @mkdir($targetDir);

    // Remove old temp files    
    if ($cleanupTargetDir && is_dir($targetDir) && ($dir = opendir($targetDir))) 
    {
    while (($file = readdir($dir)) !== false) 
        {
            $tmpfilePath = $targetDir . DIRECTORY_SEPARATOR . $file;

        // Remove temp file if it is older than the max age and is not the current file
            if (preg_match('/\.part$/', $file) && (filemtime($tmpfilePath) < time() - $maxFileAge) && ($tmpfilePath != "{$filePath}.part")) 
        {
            @unlink($tmpfilePath);
        }
    }

    closedir($dir);
    } 
    else
    die('{"jsonrpc" : "2.0", "error" : {"code": 100, "message": "Failed to open temp directory."}, "id" : "id"}');

    // Look for the content type header
    if (isset($_SERVER["HTTP_CONTENT_TYPE"]))
        $contentType = $_SERVER["HTTP_CONTENT_TYPE"];

    if (isset($_SERVER["CONTENT_TYPE"]))
    $contentType = $_SERVER["CONTENT_TYPE"];

    // Handle non multipart uploads older WebKit versions didn't support multipart in HTML5
    if (strpos($contentType, "multipart") !== false) 
    {
        if (isset($_FILES['file']['tmp_name']) && is_uploaded_file($_FILES['file']['tmp_name']))
        {
            // Open temp file
            $out = fopen("{$filePath}.part", $chunk == 0 ? "wb" : "ab");
    if ($out)
    {
        // Read binary input stream and append it to temp file
        $in = fopen($_FILES['file']['tmp_name'], "rb");
    if ($in) 
    {
        while ($buff = fread($in, 4096))
        fwrite($out, $buff);
    } 
    else
        die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}');
        fclose($in);
        fclose($out);
        @unlink($_FILES['file']['tmp_name']);
    }
    else
        die('{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Failed to open output stream."}, "id" : "id"}');
    } 
    else
        die('{"jsonrpc" : "2.0", "error" : {"code": 103, "message": "Failed to move uploaded file."}, "id" : "id"}');
    } 
    else
    {
        // Open temp file
        $out = fopen("{$filePath}.part", $chunk == 0 ? "wb" : "ab");
    if ($out) 
    {
        // Read binary input stream and append it to temp file
        $in = fopen("php://input", "rb");
    if ($in)
    {
        while ($buff = fread($in, 4096))
        fwrite($out, $buff);
    }
    else
        die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}');
        fclose($in);
        fclose($out);
    }
    else
        die('{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Failed to open output stream."}, "id" : "id"}');
    }
    // Check if file has been uploaded
    if (!$chunks || $chunk == $chunks - 1)
    {
        // Strip the temp .part suffix off 
        rename("{$filePath}.part", $filePath);
    }

    // function added by me for creating thumbnails
    $tnPath = $targetDir . DIRECTORY_SEPARATOR . "thumb_".$fileName;
    $img_for_watermark = $targetDir.'/'.$fileName;
    $thumb_image = "thumb_".$fileName;
    $copy = copy($filePath, $tnPath);
    $sourcefile = $tnPath;
    $targetfile = $tnPath;
    $dest_x = $largeur_thumb_upload;
    $dest_y = $hauteur_thumb_upload;
    $jpegqual = $qualite_thumb_upload;
    resize($sourcefile, $dest_x, $dest_y, $targetfile);
    WaterMark($img_for_watermark);

    // Return JSON-RPC response
    die('{"jsonrpc" : "2.0", "result" : null, "id" : "id"}');
    ?>

Do you have an idea ?

jayarjo commented 11 years ago

Ok, I see - you do extract the data before you do any further manipulations, so they shouldn't matter. I will check why IPTC gets stripped in your case.

FletcherPink commented 11 years ago

Great! Thank you very much. I'll wait...