From 36df6fa3069d55db3c35724c4f465bde9c50b53a Mon Sep 17 00:00:00 2001
From: Andreas Gohr <andi@splitbrain.org>
Date: Sun, 31 Jul 2005 18:35:54 +0200
Subject: [PATCH] image metadata editor

This patch adds an meta data editor for JPEG files. Currently title,
caption, artist, copyright and keywords can be saved. The data is saved
to the apropriate IPTC fields. Additional fields can be added in the
appropriate template.

Compatibility testing with other programs is needed!

darcs-hash:20050731163554-7ad00-dd936ec19549203dcf72de8774309c0b17d0b679.gz
---
 inc/JpegMeta.php              |  58 +++++++++++++++++-----
 inc/lang/en/lang.php          |   3 ++
 inc/template.php              |  13 ++++-
 lib/exe/media.php             |  70 ++++++++++++++++++++------
 lib/images/edit.gif           | Bin 0 -> 142 bytes
 lib/tpl/default/design.css    |  22 +++++++++
 lib/tpl/default/mediaedit.php |  90 ++++++++++++++++++++++++++++++++++
 7 files changed, 227 insertions(+), 29 deletions(-)
 create mode 100644 lib/images/edit.gif
 create mode 100644 lib/tpl/default/mediaedit.php

diff --git a/inc/JpegMeta.php b/inc/JpegMeta.php
index b9f0908d4..408f34589 100644
--- a/inc/JpegMeta.php
+++ b/inc/JpegMeta.php
@@ -3,9 +3,10 @@
  * JPEG metadata reader/writer
  *
  * @license    PHP license 2.0 (http://www.php.net/license/2_02.txt)
- * @link       
+ * @link       http://www.zonageek.com/software/php/jpeg/index.php 
  * @author     Sebastian Delmont <sdelmont@zonageek.com>
  * @author     Andreas Gohr <andi@splitbrain.org>
+ * @todo       Add support for Maker Notes, Extend for GIF and PNG metadata
  */
 
 // This class is a modified and enhanced version of the JPEG class by
@@ -163,6 +164,40 @@ class JpegMeta
         return trim($info);
     }
 
+    /**
+     * Convinience function to set nearly all available Data
+     * through one function
+     *
+     * @author Andreas Gohr <andi@splitbrain.org>
+     */
+    function setField($field, $value)
+    {
+        if(strtolower(substr($field,0,5)) == 'iptc.'){
+            return $this->setIPTCField(substr($field,5),$value);
+        }elseif(strtolower(substr($field,0,5)) == 'exif.'){
+            return $this->setExifField(substr($field,5),$value);
+        }else{
+            return $this->setExifField($field,$value);
+        }
+    }
+
+    /**
+     * Convinience function to delete nearly all available Data
+     * through one function
+     *
+     * @author Andreas Gohr <andi@splitbrain.org>
+     */
+    function deleteField($field)
+    {
+        if(strtolower(substr($field,0,5)) == 'iptc.'){
+            return $this->deleteIPTCField(substr($field,5));
+        }elseif(strtolower(substr($field,0,5)) == 'exif.'){
+            return $this->deleteExifField(substr($field,5));
+        }else{
+            return $this->deleteExifField($field);
+        }
+    }
+
     /**
      * Return a date field
      *
@@ -770,18 +805,19 @@ class JpegMeta
      * Save changed Metadata
      *
      * @author Sebastian Delmont <sdelmont@zonageek.com>
+     * @author Andreas Gohr <andi@splitbrain.org>
      */
     function save($fileName = "") {
-      if ($fileName == "") {
-        $tmpName = $this->_fileName . ".tmp";
-        $this->_writeJPEG($tmpName);
-        if (file_exists($tmpName)) {
-          rename($tmpName, $this->_fileName);
-        }
-      }
-      else {
-        $this->_writeJPEG($fileName);
-      }
+        if ($fileName == "") {
+            $tmpName = tempnam(dirname($this->_fileName),'_metatemp_');
+            $this->_writeJPEG($tmpName);
+            if (@file_exists($tmpName)) {
+                return rename($tmpName, $this->_fileName);
+            }
+        } else {
+            return $this->_writeJPEG($fileName);
+        }
+        return false;
     }
     
     /*************************************************************/
diff --git a/inc/lang/en/lang.php b/inc/lang/en/lang.php
index 16fc1b738..fcc4ed696 100644
--- a/inc/lang/en/lang.php
+++ b/inc/lang/en/lang.php
@@ -144,6 +144,9 @@ $lang['spell_noerr'] = 'No Mistakes found';
 $lang['spell_nosug'] = 'No Suggestions';
 $lang['spell_change']= 'Change';
 
+$lang['metaedit']    = 'Edit Metadata';
+$lang['metasaveerr'] = 'Writing metadata failed';
+$lang['metasaveok']  = 'Metadata saved';
 $lang['img_backto']  = 'Back to';
 $lang['img_title']   = 'Title';
 $lang['img_caption'] = 'Caption';
diff --git a/inc/template.php b/inc/template.php
index 23123b452..b48a591f8 100644
--- a/inc/template.php
+++ b/inc/template.php
@@ -665,12 +665,21 @@ function tpl_mediafilelist(){
       $t = $item['meta']->getField('IPTC.Headline');
       if($t) print '<b>'.$t.'</b><br />';
 
-      $t = $item['meta']->getField(array('IPTC.Caption','EXIF.UserComment','EXIF.TIFFImageDescription','EXIF.TIFFUserComment'));
+      $t = $item['meta']->getField(array('IPTC.Caption','EXIF.UserComment',
+                                         'EXIF.TIFFImageDescription',
+                                         'EXIF.TIFFUserComment'));
       if($t) print $t.'<br />';
 
       $t = $item['meta']->getField(array('IPTC.Keywords','IPTC.Category'));
       if($t) print '<i>'.$t.'</i><br />';
 
+      //add edit button
+      if($AUTH >= AUTH_UPLOAD && $item['meta']->getField('File.Mime') == 'image/jpeg'){
+        print '<a href="'.DOKU_BASE.'lib/exe/media.php?edit='.urlencode($item['id']).'">';
+        print '<img src="'.DOKU_BASE.'lib/images/edit.gif" alt="'.$lang['metaedit'].'" title="'.$lang['metaedit'].'" />';
+        print '</a>';
+      }
+
       ptln('</div>',6);
     }else{
       ptln ('('.filesize_h($item['size']).')',6);
@@ -771,7 +780,7 @@ function tpl_pagetitle($id=null){
  * and _iptcTagNames() in inc/jpeg.php (You need to prepend IPTC
  * to the names of the latter one)
  *
- * Only allowed in: detail.php
+ * Only allowed in: detail.php, mediaedit.php
  *
  * @author Andreas Gohr <andi@splitbrain.org>
  */
diff --git a/lib/exe/media.php b/lib/exe/media.php
index c8db37153..cddf258db 100644
--- a/lib/exe/media.php
+++ b/lib/exe/media.php
@@ -14,8 +14,12 @@
 
   //get namespace to display (either direct or from deletion order)
   if($_REQUEST['delete']){
-		$DEL = cleanID($_REQUEST['delete']);
+    $DEL = cleanID($_REQUEST['delete']);
     $NS  = getNS($DEL);
+  }elseif($_REQUEST['edit']){
+    $IMG = cleanID($_REQUEST['edit']);
+    $SRC = mediaFN($IMG);
+    $NS  = getNS($IMG);
   }else{
     $NS = $_REQUEST['ns'];
     $NS = cleanID($NS);
@@ -35,9 +39,9 @@
   //handle deletion
   $mediareferences = array();
   if($DEL && $AUTH >= AUTH_DELETE){
-	if($conf['refcheck']){
-  	  search($mediareferences,$conf['datadir'],'search_reference',array('query' => $DEL));
-	}
+    if($conf['refcheck']){
+      search($mediareferences,$conf['datadir'],'search_reference',array('query' => $DEL));
+    }
     if(!count($mediareferences)){
       media_delete($DEL);
     }elseif(!$conf['refshow']){
@@ -45,6 +49,11 @@
     }
   }
 
+  //handle metadatasaving
+  if($UPLOADOK && $SRC && $_REQUEST['save']){
+    media_metasave($SRC,$_REQUEST['meta']);
+  }
+
   //handle upload
   if($_FILES['upload']['tmp_name'] && $UPLOADOK){
     media_upload($NS,$AUTH);
@@ -54,6 +63,8 @@
   header('Content-Type: text/html; charset=utf-8');
   if($conf['refshow'] && count($mediareferences)){
     include(template('mediaref.php'));
+  }elseif($IMG){
+    include(template('mediaedit.php'));
   }else{
     include(template('media.php'));
   }
@@ -87,7 +98,7 @@ function media_delete($delid){
  * @author Andreas Gohr <andi@splitbrain.org>
  */
 function media_upload($NS,$AUTH){
-	require_once(DOKU_INC.'inc/confutils.php');
+  require_once(DOKU_INC.'inc/confutils.php');
   global $lang;
   global $conf;
 
@@ -101,24 +112,24 @@ function media_upload($NS,$AUTH){
   $fn   = mediaFN($id);
 
   // get filetype regexp
-	$types = array_keys(getMimeTypes());
-	$types = array_map(create_function('$q','return preg_quote($q,"/");'),$types);
+  $types = array_keys(getMimeTypes());
+  $types = array_map(create_function('$q','return preg_quote($q,"/");'),$types);
   $regex = join('|',$types);
 
   // we set the umask here but this doesn't really help
   // because a temp file was created already
   umask($conf['umask']);
   if(preg_match('/\.('.$regex.')$/i',$fn)){
-		//check for overwrite
-		if(@file_exists($fn) && (!$_POST['ow'] || $AUTH < AUTH_DELETE)){
-			msg($lang['uploadexist'],0);
-			return false;
-		}
-  	// prepare directory
-  	io_makeFileDir($fn);
+    //check for overwrite
+    if(@file_exists($fn) && (!$_POST['ow'] || $AUTH < AUTH_DELETE)){
+      msg($lang['uploadexist'],0);
+      return false;
+    }
+    // prepare directory
+    io_makeFileDir($fn);
     if(move_uploaded_file($file['tmp_name'], $fn)) {
-			// set the correct permission here
-			chmod($fn, 0777 - $conf['umask']);
+      // set the correct permission here
+      chmod($fn, 0777 - $conf['umask']);
       msg($lang['uploadsucc'],1);
       return true;
     }else{
@@ -146,4 +157,31 @@ function media_html_list_namespaces($item){
   return $ret;
 }
 
+/**
+ * Saves image meta data
+ *
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+function media_metasave($src,$data){
+	global $lang;
+
+  $meta = new JpegMeta($src);
+  $meta->_parseAll();
+
+	foreach($data as $key => $val){
+		$val=trim($val);
+		if(empty($val)){
+			$meta->deleteField($key);
+		}else{
+			$meta->setField($key,$val);
+		}
+	}
+
+	if($meta->save()){
+		msg($lang['metasaveok'],1);
+	}else{
+		msg($lang['metasaveerr'],-1);
+	}
+}
+
 ?>
diff --git a/lib/images/edit.gif b/lib/images/edit.gif
new file mode 100644
index 0000000000000000000000000000000000000000..a2a23de7b31c4f48a051d25272f9b24f8d7ac281
GIT binary patch
literal 142
zcmZ?wbhEHb6k!ly*v!E2|NsB@@84g#bm_o>1DiH&TC`|Ue}8{NLj#bjudmObAkTmb
z6o0ZXf<$yc1jq~q7HfeMo~!p-y!O2u6=UA0_<)oBWaFVp)80r=TCwZsHn-E-0l}?K
f&ptD=G#}=F(cz;vb31GBj;3krZmdydVz34P<N-CY

literal 0
HcmV?d00001

diff --git a/lib/tpl/default/design.css b/lib/tpl/default/design.css
index 48453a736..ee03815ff 100644
--- a/lib/tpl/default/design.css
+++ b/lib/tpl/default/design.css
@@ -733,3 +733,25 @@ div.mediaselect-right li, div.uploadform {
   clear: both;
 }
 
+div.mediaedit div.data label{
+	display: block;
+  text-align: right; 
+  width: 20%;
+  float: left;
+  margin-right: 0.5em;
+}
+
+div.mediaedit div.data input, div.mediaedit div.data textarea{
+  width: 75%;
+  padding: 0.1em;
+  margin: 0.1em;
+}
+
+div.mediaedit div.data input.button {
+  width: 10em;
+  display: block;
+  margin-left: auto;
+  margin-right: auto;
+}
+
+
diff --git a/lib/tpl/default/mediaedit.php b/lib/tpl/default/mediaedit.php
new file mode 100644
index 000000000..53a969e1b
--- /dev/null
+++ b/lib/tpl/default/mediaedit.php
@@ -0,0 +1,90 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<?php
+/**
+ * DokuWiki Default Template
+ *
+ * This is the template for editing image meta data.
+ * It is displayed in the media popup.
+ *
+ * You should leave the doctype at the very top - It should
+ * always be the very first line of a document.
+ *
+ * @link   http://wiki.splitbrain.org/wiki:tpl:templates
+ * @author Andreas Gohr <andi@splitbrain.org>
+ */
+?>
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php echo $conf['lang']?>" lang="<?php echo $conf['lang']?>" dir="ltr">
+<head>
+  <title><?php echo hsc($lang['mediaselect'])?> [<?php echo hsc($conf['title'])?>]</title>
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+  <?php tpl_metaheaders()?>
+
+  <link rel="shortcut icon" href="<?php echo DOKU_BASE?>images/favicon.ico" />
+  <link rel="stylesheet" media="screen" type="text/css" href="<?php echo DOKU_TPL?>layout.css" />
+  <link rel="stylesheet" media="screen" type="text/css" href="<?php echo DOKU_TPL?>design.css" />
+</head>
+
+<body>
+<div class="dokuwiki">
+  <?php html_msgarea()?>
+
+  <h1><?php echo hsc($lang['metaedit'])?> <code><?php echo hsc(noNS($IMG))?></code></h1>
+
+  <div class="mediaedit">
+		<?php/* everything in meta array is tried to save and read */?>
+
+ 		<div class="data">
+			<form action="<?php echo DOKU_BASE?>lib/exe/media.php" accept-charset="utf-8" method="post">
+				<input type="hidden" name="edit" value="<?php echo hsc($IMG)?>" />
+				<input type="hidden" name="save" value="1" />
+
+				<label for="title"><?php echo $lang['img_title']?></label>
+				<input type="text" name="meta[Iptc.Headline]" id="title" class="edit"
+         value="<?php echo hsc(tpl_img_getTag('IPTC.Headline'))?>" /><br />
+
+				<label for="caption"><?php echo $lang['img_caption']?></label>
+				<textarea name="meta[Iptc.Caption]" id="caption" class="edit" rows="5"><?php
+          echo hsc(tpl_img_getTag(array('IPTC.Caption',
+                                        'EXIF.UserComment',
+                                        'EXIF.TIFFImageDescription',
+                                        'EXIF.TIFFUserComment')));
+        ?></textarea><br />
+
+				<label for="artist"><?php echo $lang['img_artist']?></label>
+				<input type="text" name="meta[Iptc.Byline]" id="artist" class="edit"
+         value="<?php echo hsc(tpl_img_getTag(array('Iptc.Byline',
+                                                    'Exif.TIFFArtist',
+                                                    'Exif.Artist',
+                                                    'Iptc.Credit')))?>" /><br />
+
+				<label for="copy"><?php echo $lang['img_copyr']?></label>
+				<input type="text" name="meta[Iptc.CopyrightNotice]" id="copy" class="edit"
+         value="<?php echo hsc(tpl_img_getTag(array('Iptc.CopyrightNotice','Exif.TIFFCopyright','Exif.Copyright')))?>" /><br />
+
+
+				<label for="keywords"><?php echo $lang['img_keywords']?></label>
+        <textarea name="meta[Iptc.Keywords]" id="keywords" class="edit"><?php
+          echo hsc(tpl_img_getTag(array('IPTC.Keywords',
+                                        'EXIF.Category')));
+        ?></textarea><br />
+
+
+				<input type="submit" value="<?php echo $lang['btn_save']?>" title="ALT+S"
+         accesskey="s" class="button" />
+
+			</form>
+ 		</div>
+
+
+    <div class="footer">
+      <hr>
+        <?php tpl_button('backtomedia')?>
+    </div>
+  </div>
+
+</div>
+</body>
+</html>
+
-- 
GitLab