PageRenderTime 45ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/_plugins_/metadonnees_photo/trunk/inc/exifWriter.php

https://bitbucket.org/pombredanne/spip-zone-treemap
PHP | 463 lines | 240 code | 69 blank | 154 comment | 31 complexity | dc2b386519189302147ed657c47cbc15 MD5 | raw file
  1. <?php
  2. /**
  3. * PHP Class to write EXIF information back to image
  4. *
  5. * Vinay Yadav < vinay@vinayras.com >
  6. * http://www.vinayras.com/project/phpexifrw.php
  7. * http://www.sanisoft.com/phpexifrw/
  8. * For more information on EXIF
  9. * http://www.exif.org/
  10. *
  11. * Features:
  12. * - Write comments to image
  13. */
  14. if (!defined("_ECRIRE_INC_VERSION")) return;
  15. /**
  16. * PHP Class to read, write and transfer EXIF information
  17. * that most of the digital camera produces
  18. * Currenty it can only read JPEG file.
  19. */
  20. /**
  21. * @author Vinay Yadav (vinayRas) < vinay@vinayras.com >
  22. *
  23. * @version 1.0
  24. * @licence http://opensource.org/licenses/lgpl-license.php GNU LGPL
  25. */
  26. require_once("exifReader.inc");
  27. /**
  28. *
  29. */
  30. class phpExifWriter extends phpExifReader {
  31. /**
  32. * Constructor
  33. */
  34. function phpExifWriter($image) {
  35. $this->phpExifReader($image);
  36. $this->processFile();
  37. }
  38. /**
  39. * Modifies or sets value of specified Tag -
  40. * @param hex Tag, whose value has to be set
  41. * @param string Tags value
  42. *
  43. */
  44. function setExifData($param,$value) {
  45. $this->ImageInfo["$param"] = $value;
  46. }
  47. /**
  48. * This functiion writes back the modifed exif data into the imageinfo array -
  49. * NOTE: This code is still INCOMPLETE and does not work.
  50. *
  51. */
  52. function modifyExifDetails() {
  53. $newData[0] = $this->sections[$this->exifSection]["data"][0];
  54. $newData[1] = $this->sections[$this->exifSection]["data"][1];
  55. $newData[2] = 'E'; $newData[3] = 'x';
  56. $newData[4] = 'i'; $newData[5] = 'f';
  57. $newData[6] = $this->sections[$this->exifSection]["data"][6];
  58. $newData[7] = $this->sections[$this->exifSection]["data"][7];
  59. if($this->MotorolaOrder == 1) {
  60. $newData[8] = 'M';$newData[9] = 'M';
  61. } else {
  62. $newData[8] = 'I';$newData[9] = 'I';
  63. }
  64. $newData[10] = chr(42 >> 8);
  65. $newData[11] = chr(42);
  66. $newData[12] = chr(0);
  67. $newData[13] = chr(0);
  68. $newData[14] = chr(0);
  69. $newData[15] = chr(8);
  70. $newData[16] = 1;
  71. $newData[17] = 1;
  72. $totalLength = 16; $totalElements = 0;
  73. $offset = 10+(15*12);
  74. $otherDataArr = array();
  75. foreach($this->ImageInfo as $tag => $val) {
  76. if(is_array($val)) continue;
  77. if(mb_eregi("0x",$tag)) {
  78. // format
  79. $fmt = $this->getFormat($tag);
  80. if($fmt == -1) continue;
  81. $tmpTag = hexdec($tag);
  82. // tag
  83. $newData[] = chr($tmpTag >> 8);
  84. $newData[] = chr($tmpTag);
  85. $newData[] = chr($fmt >> 8);
  86. $newData[] = chr($fmt);
  87. echo "<br>TAG:$tag - Format:$fmt - Value:$val";
  88. //components
  89. $chars = preg_split('//', $val, -1, PREG_SPLIT_NO_EMPTY);
  90. $ByteCount = count($chars);
  91. $Components = ceil($ByteCount / $this->BytesPerFormat[$fmt]);
  92. $newData[] = chr($Components >> 24);
  93. $newData[] = chr($Components >> 16);
  94. $newData[] = chr($Components >> 8);
  95. $newData[] = chr($Components);
  96. echo "<br>ByteCount: $ByteCount";
  97. if($ByteCount <= 4) {
  98. $newData[] = chr($chars[0] >> 8);
  99. $newData[] = chr($chars[0]);
  100. $newData[] = (isset($chars[2])) ? chr($chars[2]) : '';
  101. $newData[] = (isset($chars[3])) ? chr($chars[3]) : '';
  102. } else {
  103. $newData[] = chr($offset >> 24);
  104. $newData[] = chr($offset >> 16);
  105. $newData[] = chr($offset >> 8);
  106. $newData[] = chr($offset);
  107. if($fmt != FMT_STRING) {
  108. $arr = $this->ConvertAnyFormatBack($val,$fmt);
  109. $chars = $arr;
  110. $ByteCount = 8;
  111. }
  112. $offset+=$ByteCount;
  113. $otherDataArr = array_merge($otherDataArr,$chars);
  114. }
  115. $totalLength += 12+$ByteCount;
  116. $totalElements++;
  117. }
  118. }
  119. $newData = array_merge($newData,$otherDataArr);
  120. /**
  121. * Write the thumbnail back to the exif section
  122. * Dont know if this works -
  123. */
  124. /**
  125. if($this->thumbnail) {
  126. echo "Thumnail Size:".count($this->ImageInfo["ThumbnailPointer"]);
  127. $tmpTag = hexdec();
  128. // tag
  129. $newData[] = chr($tmpTag >> 8);
  130. $newData[] = chr($tmpTag);
  131. // format
  132. $fmt = $this->getFormat($tag);
  133. $newData[] = chr($fmt >> 8);
  134. $newData[] = chr($fmt);
  135. //components
  136. $chars = preg_split('//', $val, -1, PREG_SPLIT_NO_EMPTY);
  137. $ByteCount = count($chars);
  138. $Components = $ByteCount / $this->BytesPerFormat[$fmt];
  139. $newData[] = chr($Components >> 32);
  140. $newData[] = chr($Components >> 16);
  141. $newData[] = chr($Components >> 8);
  142. $newData[] = chr($Components);
  143. //$newData = array_merge($newData,$chars);
  144. $newData = array_merge($newData,$this->ImageInfo["ThumbnailPointer"]);
  145. $totalLength += count($this->ImageInfo["ThumbnailPointer"]);
  146. }
  147. */
  148. $totalLength += 2;
  149. $newData[0] = chr($totalLength >> 8);
  150. $newData[1] = chr($totalLength);
  151. $newData[16] = chr($totalElements >> 8);
  152. $newData[17] = chr($totalElements);
  153. $this->sections[$this->exifSection]["data"] = $newData;
  154. $this->sections[$this->exifSection]["size"] = $totalLength;
  155. }
  156. /**
  157. * Searched for the tag specified in the sections list
  158. *
  159. * @param hex Tag to search for
  160. *
  161. * @return int
  162. -1 - Tag not found
  163. */
  164. function findMarker($marker) {
  165. for($i=0;$i<$this->currSection;$i++) {
  166. if($this->sections[$i]["type"] == $marker) {
  167. return $i;
  168. }
  169. }
  170. return -1;
  171. }
  172. /**
  173. * Adds comment to the image.
  174. * NOTE: Will have to call writeExif for the comments and
  175. * other data to be written back to image.
  176. * @param string Commnent as string
  177. *
  178. */
  179. function addComment($comment) {
  180. /** check if comments already exists! */
  181. $commentSection = $this->findMarker(M_COM);
  182. if($commentSection == -1) {
  183. // make 3rd element as comment section - Push-up all elements
  184. for($i=$this->currSection;$i>2;$i--) {
  185. $this->sections[$i]["type"] = $this->sections[$i-1]["type"];
  186. $this->sections[$i]["data"] = $this->sections[$i-1]["data"];
  187. $this->sections[$i]["size"] = $this->sections[$i-1]["size"];
  188. }
  189. $this->currSection++;
  190. $commentSection = 2;
  191. }
  192. $data[0] = 0; // dummy data
  193. $data[1] = 0; // dummy data
  194. $chars = preg_split('//', $comment, -1, PREG_SPLIT_NO_EMPTY);
  195. $data = array_merge($data,$chars);
  196. $this->sections[$commentSection]["size"] = count($data);
  197. $data[0] = chr($this->sections[$commentSection]["size"] >> 8);
  198. $data[1] = chr($this->sections[$commentSection]["size"]);
  199. $this->sections[$commentSection]["type"] = M_COM;
  200. $this->sections[$commentSection]["data"] = $data;
  201. }
  202. /**
  203. * Return the format of data of any tag.
  204. *
  205. * @param hex Tag whose format has to looked for
  206. *
  207. * @return int Return the format as int
  208. *
  209. */
  210. function getFormat($tag) {
  211. switch($tag) {
  212. //$FMT_BYTE_ARRAY
  213. //$FMT_STRING_ARRAY
  214. case TAG_MAKE:
  215. case TAG_MODEL:
  216. case TAG_SOFTWARE:
  217. case TAG_ARTIST:
  218. case TAG_COPYRIGHT:
  219. case TAG_DATETIME_ORIGINAL:
  220. case TAG_IMAGE_DESC:
  221. return FMT_STRING;
  222. break;
  223. //$FMT_USHORT_ARRAY
  224. case TAG_ORIENTATION:
  225. case TAG_EXPOSURE_PROGRAM:
  226. case TAG_METERING_MODE:
  227. case TAG_FLASH:
  228. case TAG_EXIF_IMAGEWIDTH:
  229. case TAG_EXIF_IMAGELENGTH:
  230. return FMT_USHORT;
  231. break;
  232. //$FMT_ULONG_ARRAY
  233. case TAG_THUMBNAIL_LENGTH:
  234. return FMT_ULONG;
  235. break;
  236. //$FMT_URATIONAL_ARRAY
  237. case TAG_EXPOSURETIME:
  238. case TAG_FNUMBER:
  239. case TAG_COMPRESSION_LEVEL:
  240. case TAG_APERTURE:
  241. case TAG_MAXAPERTURE:
  242. case TAG_FOCALLENGTH:
  243. return FMT_URATIONAL;
  244. break;
  245. //$FMT_SBYTE_ARRAY
  246. //$FMT_UNDEFINED_ARRAY
  247. //$FMT_SSHORT_ARRAY
  248. //$FMT_SLONG_ARRAY
  249. //$FMT_SRATIONAL_ARRAY
  250. case TAG_SHUTTERSPEED:
  251. case TAG_EXPOSURE_BIAS:
  252. return FMT_SRATIONAL;
  253. break;
  254. //$FMT_SINGLE_ARRAY
  255. //$FMT_DOUBLE_ARRAY
  256. default:
  257. $this->debug("UNDEFINED TAG:",$tag);
  258. return -1;
  259. }
  260. }
  261. /**
  262. *
  263. * Reverse of ConvertAnyFormat, - Incomplete
  264. * TODO:
  265. only FMT_URATIONAL, FMT_SRATIONAL works
  266. *
  267. */
  268. function ConvertAnyFormatBack($Value, $Format)
  269. {
  270. //$Value = 0;
  271. switch($Format){
  272. case FMT_SBYTE: $Value = $ValuePtr[0]; break;
  273. case FMT_BYTE: $Value = $ValuePtr[0]; break;
  274. case FMT_USHORT: $Value = $this->Get16u($ValuePtr[0],$ValuePtr[1]); break;
  275. case FMT_ULONG: $Value = $this->Get32u($ValuePtr[0],$ValuePtr[1],$ValuePtr[2],$ValuePtr[3]); break;
  276. case FMT_URATIONAL:
  277. case FMT_SRATIONAL:
  278. {
  279. $num = $Value[1][0];
  280. $Den = $Value[1][1];
  281. $ValuePtr[0] = chr($num >> 24);
  282. $ValuePtr[1] = chr($num >> 16);
  283. $ValuePtr[2] = chr($num >> 8);
  284. $ValuePtr[3] = chr($num);
  285. $ValuePtr[4] = chr($Den >> 24);
  286. $ValuePtr[5] = chr($Den >> 16);
  287. $ValuePtr[6] = chr($Den >> 8);
  288. $ValuePtr[7] = chr($Den);
  289. break;
  290. }
  291. case FMT_SSHORT: $Value = $this->Get16u($ValuePtr[0],$ValuePtr[1]); break;
  292. case FMT_SLONG: $Value = $this->Get32s($ValuePtr[0],$ValuePtr[1],$ValuePtr[2],$ValuePtr[3]); break;
  293. // Not sure if this is correct (never seen float used in Exif format)
  294. case FMT_SINGLE: $Value = $ValuePtr[0]; break;
  295. case FMT_DOUBLE: $Value = $ValuePtr[0]; break;
  296. default:
  297. return -1;
  298. }
  299. return $ValuePtr;
  300. }
  301. /**
  302. * Returns the raw exif information stored
  303. *
  304. */
  305. function getExif() {
  306. if($this->exifSection > -1) {
  307. return $this->sections[$this->exifSection]["data"];
  308. }
  309. /** Exif data does not exists */
  310. return -1;
  311. }
  312. /**
  313. * Addes raw exif information
  314. *
  315. * @param string Exif Data to be added.
  316. *
  317. * NOTE: This function will blindly replace any existing EXIF data
  318. */
  319. function addExif($exifData) {
  320. $exifSection = $this->findMarker(M_EXIF);
  321. if($exifSection == -1) {
  322. // make 3rd element as comment section - Push-up all elements
  323. for($i=$this->currSection;$i>2;$i--) {
  324. $this->sections[$i]["type"] = $this->sections[$i-1]["type"];
  325. $this->sections[$i]["data"] = $this->sections[$i-1]["data"];
  326. $this->sections[$i]["size"] = $this->sections[$i-1]["size"];
  327. }
  328. $exifSection = 2;
  329. $this->currSection++;
  330. }
  331. $this->sections[$exifSection]["type"] = M_EXIF;
  332. $this->sections[$exifSection]["data"] = $exifData;
  333. $this->sections[$exifSection]["size"] = strlen($exifData);
  334. }
  335. /**
  336. * Write the whole image back into a file.
  337. * This function does not write back to the same file.
  338. * You need to specify a filename
  339. *
  340. * @param string filename to save the JPEG content to
  341. */
  342. function writeImage($file) {
  343. $file = trim($file);
  344. if(empty($file)) {
  345. $this->errno = 3;
  346. $this->errstr = "File name not provided!";
  347. debug($this->errstr,1);
  348. }
  349. $fp = fopen($file,"wb");
  350. /** Initial static jpeg marker. */
  351. fwrite($fp,chr(0xff));
  352. fwrite($fp,chr(0xd8));
  353. if ($this->sections[0]["type"] != M_EXIF && $this->sections[0]["type"] != M_JFIF){
  354. $JfifHead = array(
  355. chr(0xff), chr(M_JFIF),
  356. chr(0x00), chr(0x10), 'J' , 'F' , 'I' , 'F' , chr(0x00), chr(0x01),
  357. chr(0x01), chr(0x01), chr(0x01), chr(0x2C), chr(0x01), chr(0x2C), chr(0x00), chr(0x00)
  358. );
  359. fwrite($fp,implode("",$JfifHead));
  360. }
  361. /** write each section back into the file */
  362. for($key=0;$key<$this->currSection-1;$key++) {
  363. if(!empty($this->sections[$key]["data"])) {
  364. fwrite($fp,chr(0xff));
  365. fwrite($fp,chr($this->sections[$key]["type"]));
  366. /**
  367. dat acan be array as well as string. Check the data-type of data.
  368. If it is an array then convert it to string.
  369. */
  370. if(is_array($this->sections[$key]["data"])) {
  371. $this->sections[$key]["data"] = implode("",$this->sections[$key]["data"]);
  372. }
  373. fwrite($fp,$this->sections[$key]["data"]);
  374. }
  375. }
  376. // Write the remaining image data.
  377. if(is_array($this->sections[$key]["data"])) {
  378. $this->sections[$key]["data"] = implode("",$this->sections[$key]["data"]);
  379. }
  380. fwrite($fp,$this->sections[$key]["data"]);
  381. fclose($fp);
  382. }
  383. } // end of class
  384. ?>