Guest User

Untitled

a guest
Jan 22nd, 2018
82
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 13.04 KB | None | 0 0
  1. <?php
  2.  
  3. $exif = new Exif("test.jpg");
  4. $dt = "2011:08:10 12:00:00";
  5. $exif->add_entry("DateTime", TagEntry::FORMAT_ASCII_STRINGS, 19, $dt);
  6. $exif->add_entry("DateTimeOriginal", TagEntry::FORMAT_ASCII_STRINGS, 19, $dt);
  7. $exif->add_entry("DateTimeDigitized", TagEntry::FORMAT_ASCII_STRINGS, 19, $dt);
  8. file_put_contents("test2.jpg", $exif->to_binary());
  9.  
  10. class TagEntry {
  11.  
  12. const BYTE_SIZE = 12;
  13.  
  14. const FORMAT_UNSIGNED_BYTE = 1;
  15. const FORMAT_ASCII_STRINGS = 2;
  16. const FORMAT_UNSIGNED_SHORT = 3;
  17. const FORMAT_UNSIGNED_LONG = 4;
  18. const FORMAT_UNSIGNED_RATIONAL = 5;
  19. const FORMAT_SIGNED_BYTE = 6;
  20. const FORMAT_UNDEFINED = 7;
  21. const FORMAT_SIGNED_SHORT = 8;
  22. const FORMAT_SIGNED_LONG = 9;
  23. const FORMAT_SIGNED_RATIONAL = 10;
  24. const FORMAT_SIGNED_FLOAT = 11;
  25. const FORMAT_DOUBLE_FLOAT = 12;
  26.  
  27. var $_data_size = array(
  28. self::FORMAT_UNSIGNED_BYTE => 1,
  29. self::FORMAT_ASCII_STRINGS => 1,
  30. self::FORMAT_UNSIGNED_SHORT => 2,
  31. self::FORMAT_UNSIGNED_LONG => 4,
  32. self::FORMAT_UNSIGNED_RATIONAL => 8,
  33. self::FORMAT_SIGNED_BYTE => 1,
  34. self::FORMAT_UNDEFINED => 1,
  35. self::FORMAT_SIGNED_SHORT => 2,
  36. self::FORMAT_SIGNED_LONG => 4,
  37. self::FORMAT_SIGNED_RATIONAL => 8,
  38. self::FORMAT_SIGNED_FLOAT => 4,
  39. self::FORMAT_DOUBLE_FLOAT => 8,
  40. );
  41.  
  42. var $_tag; // 0xXXXX
  43. var $_format;
  44. var $_num;
  45. var $_val;
  46. var $_offset = 0;
  47.  
  48. function __construct($tag, $format, $num, $val){
  49. $this->_tag = $tag;
  50. $this->_format = $format;
  51. $this->_num = $num;
  52. $this->_val = $val;
  53. }
  54.  
  55. function is_over_size(){
  56. $size = $this->size();
  57. return ($size > 4);
  58. }
  59.  
  60. function size(){
  61. return $this->_data_size[$this->_format] * $this->_num;
  62. }
  63.  
  64. function to_binary(){
  65. $data = "";
  66. $data .= pack("v", $this->_tag);
  67. $data .= pack("v", $this->_format);
  68. $data .= pack("V", $this->_num);
  69.  
  70. if($this->_offset){
  71. $data .= pack("V", $this->_offset);
  72. }
  73. else{
  74. $data .= $this->_val;
  75. }
  76. return $data;
  77. }
  78. }
  79.  
  80. class IFD {
  81.  
  82. var $_enabled_tags;
  83. var $_entries;
  84.  
  85. function __construct(){
  86. $this->_enabled_tags = array();
  87. $this->_entries = array();
  88. }
  89.  
  90. function is_enabled_tag($tag){
  91. return ( ! empty($this->_enabled_tags[$tag]));
  92. }
  93.  
  94. function add_entry($tag, $format, $num, $val){
  95. if( ! $this->is_enabled_tag($tag)){
  96. return FALSE;
  97. }
  98. $this->_entries[$tag] = new TagEntry($this->_enabled_tags[$tag], $format, $num, $val);
  99. }
  100.  
  101. function has_entry(){
  102. return (count(array_values($this->_entries)) > 0);
  103. }
  104.  
  105. function to_binary($base, $has_next_ifd = FALSE){
  106. if( ! $this->has_entry()){
  107. return array(0, "", "", "");
  108. }
  109.  
  110. $entries = array_values($this->_entries);
  111. $entries_count = count($entries);
  112. // pointer of base + byte of entry_count_area + byte of entries + byte of next_link_area
  113. $data_section_base = $base + 2 + TagEntry::BYTE_SIZE * count($entries) + 4;
  114.  
  115. $dir_section = "";
  116. $next_link = "";
  117. $data_section = "";
  118.  
  119. // 2byte - number of entry
  120. $dir_section .= pack("v", $entries_count);
  121.  
  122. // 12byte * n - entries
  123. foreach($entries as $entry){
  124. if($entry->is_over_size()){
  125. $size = $entry->size();
  126. $val = $entry->_val;
  127.  
  128. $entry->_offset = $data_section_base;
  129.  
  130. $data_section_base += $size;
  131. $data_section .= $val;
  132. }
  133.  
  134. $dir_section .= $entry->to_binary();
  135. }
  136.  
  137. // 4byte - offset to next ifd
  138. if($has_next_ifd){
  139. $size = strlen($dir_section) + 4 + strlen($data_section);
  140. $next_link .= pack("V", $base + $size);
  141. }
  142. else{
  143. $next_link .= pack("V", 0);
  144. }
  145.  
  146. return array(strlen($dir_section . $next_link . $data_section), $dir_section, $next_link, $data_section);
  147. }
  148. }
  149.  
  150. class IFD0 extends IFD {
  151. function __construct(){
  152. parent::__construct();
  153.  
  154. $this->_enabled_tags = array(
  155. "ImageDescription" => 0x010e,
  156. "Make" => 0x010f,
  157. "Model" => 0x0110,
  158. "Orientation" => 0x0112,
  159. "XResolution" => 0x011a,
  160. "YResolution" => 0x011b,
  161. "ResolutionUnit" => 0x0128,
  162. "Software" => 0x0131,
  163. "DateTime" => 0x0132,
  164. "WhitePoint" => 0x013e,
  165. "PrimaryChromaticities" => 0x013f,
  166. "YCbCrCoefficients" => 0x0211,
  167. "YCbCrPositioning" => 0x0213,
  168. "ReferenceBlackWhite" => 0x0214,
  169. "Copyright" => 0x8298,
  170. "ExifIFDPointer" => 0x8769,
  171. );
  172. }
  173.  
  174. function to_binary($base, $has_next_ifd = FALSE){
  175. if( ! $this->has_entry()){
  176. return array(0, "", "", "");
  177. }
  178.  
  179. // dummy entry
  180. $this->add_entry("ExifIFDPointer", TagEntry::FORMAT_UNSIGNED_LONG, 1, pack("V", 0));
  181.  
  182. list($size, $dir, $link, $data) = parent::to_binary($base, $has_next_ifd);
  183.  
  184. $exif_ifd_base = $base + $size;
  185.  
  186. // set real entry
  187. $this->add_entry("ExifIFDPointer", TagEntry::FORMAT_UNSIGNED_LONG, 1, pack("V", $exif_ifd_base));
  188.  
  189. // recalc
  190. return parent::to_binary($base, $has_next_ifd);
  191. }
  192. }
  193.  
  194. class IFDExif extends IFD {
  195. function __construct(){
  196. parent::__construct();
  197.  
  198. $this->_enabled_tags = array(
  199. "ExposureTime" => 0x829a,
  200. "FNumber" => 0x829d,
  201. "ExposureProgram" => 0x8822,
  202. "ISOSpeedRatings" => 0x8827,
  203. "ExifVersion" => 0x9000,
  204. "DateTimeOriginal" => 0x9003,
  205. "DateTimeDigitized" => 0x9004,
  206. "ComponentsConfiguration" => 0x9101,
  207. "CompressedBitsPerPixel" => 0x9102,
  208. "ShutterSpeedValue" => 0x9201,
  209. "ApertureValue" => 0x9202,
  210. "BrightnessValue" => 0x9203,
  211. "ExposureBiasValue" => 0x9204,
  212. "MaxApertureValue" => 0x9205,
  213. "SubjectDistance" => 0x9206,
  214. "MeteringMode" => 0x9207,
  215. "LightSource" => 0x9208,
  216. "Flash" => 0x9209,
  217. "FocalLength" => 0x920a,
  218. "MakerNote" => 0x927c,
  219. "UserComment" => 0x9286,
  220. "SubsecTime" => 0x9290,
  221. "SubsecTimeOriginal" => 0x9291,
  222. "SubsecTimeDigitized" => 0x9292,
  223. "FlashPixVersion" => 0xa000,
  224. "ColorSpace" => 0xa001,
  225. "ExifImageWidth" => 0xa002,
  226. "ExifImageHeight" => 0xa003,
  227. "RelatedSoundFile" => 0xa004,
  228. "InteroperabilityIFDPointer" => 0xa005,
  229. "FocalPlaneXResolution" => 0xa20e,
  230. "FocalPlaneYResolution" => 0xa20f,
  231. "FocalPlaneResolutionUnit" => 0xa210,
  232. "ExposureIndex" => 0xa215,
  233. "SensingMethod" => 0xa217,
  234. "FileSource" => 0xa300,
  235. "SceneType" => 0xa301,
  236. "CFAPattern" => 0xa302,
  237. );
  238. }
  239. }
  240.  
  241. class IFD1 extends IFD {
  242. function __construct(){
  243. parent::__construct();
  244.  
  245. $this->_enabled_tags = array(
  246. "ImageWidth" => 0x0100,
  247. "ImageLength" => 0x0101,
  248. "BitsPerSample" => 0x0102,
  249. "Compression" => 0x0103,
  250. "PhotometricInterpretation" => 0x0106,
  251. "StripOffsets" => 0x0111,
  252. "Orientation" => 0x0112,
  253. "SamplesPerPixel" => 0x0115,
  254. "RowsPerStrip" => 0x0116,
  255. "StripByteConunts" => 0x0117,
  256. "XResolution" => 0x011a,
  257. "YResolution" => 0x011b,
  258. "PlanarConfiguration" => 0x011c,
  259. "ResolutionUnit" => 0x0128,
  260. "JpegInterchangeFormat" => 0x0201,
  261. "JpegInterchangeFormatLength" => 0x0202,
  262. "YCbCrCoefficients" => 0x0211,
  263. "YCbCrSubSampling" => 0x0212,
  264. "YCbCrPositioning" => 0x0213,
  265. "ReferenceBlackWhite" => 0x0214,
  266. );
  267. }
  268. }
  269.  
  270. class Exif {
  271.  
  272. var $_ifd0;
  273. var $_ifdexif;
  274. var $_ifd1;
  275.  
  276. var $_path;
  277.  
  278. function __construct($path){
  279. $this->_ifd0 = new IFD0();
  280. $this->_ifdexif = new IFDExif();
  281. $this->_ifd1 = new IFD1();
  282. $this->_path = $path;
  283.  
  284.  
  285. $this->_ifd0->add_entry("Make", TagEntry::FORMAT_ASCII_STRINGS, 4, str_pad("", 4));
  286. $this->_ifd0->add_entry("Model", TagEntry::FORMAT_ASCII_STRINGS, 4, str_pad("", 4));
  287. $this->_ifd0->add_entry("DateTime", TagEntry::FORMAT_ASCII_STRINGS, 19, str_pad("", 19));
  288.  
  289. $this->_ifdexif->add_entry("DateTimeOriginal", TagEntry::FORMAT_ASCII_STRINGS, 19, str_pad("", 19));
  290. $this->_ifdexif->add_entry("DateTimeDigitized", TagEntry::FORMAT_ASCII_STRINGS, 19, str_pad("", 19));
  291. }
  292.  
  293. function add_entry($tag, $format, $num, $val){
  294. if($this->_ifd0->is_enabled_tag($tag)){
  295. $this->_ifd0->add_entry($tag, $format, $num, $val);
  296. }
  297. else if($this->_ifdexif->is_enabled_tag($tag)){
  298. $this->_ifdexif->add_entry($tag, $format, $num, $val);
  299. }
  300. else if($this->_ifd1->is_enabled_tag($tag)){
  301. $this->_ifd1->add_entry($tag, $format, $num, $val);
  302. }
  303. else{
  304. return FALSE;
  305. }
  306. }
  307.  
  308. function to_binary(){
  309. $reader = new Reader($this->_path);
  310.  
  311. $data = "";
  312.  
  313. // SOI(2byte)
  314. $data .= $reader->read(2);
  315.  
  316. // APP0
  317. $marker = $reader->read_unsigned_short();
  318. if($marker == 0xFFE0){
  319. $size = $reader->read_unsigned_short();
  320. $data .= pack("n", $marker);
  321. $data .= pack("n", $size);
  322. $data .= $reader->read($size - 2); // exclude size area
  323. }
  324. else{
  325. // back
  326. $reader->seek(-2);
  327. }
  328.  
  329. // APP1
  330. $app1 = "";
  331. // Exif header
  332. $app1 .= "Exif" . pack("n", 0x0000);
  333. // Tiff header - intel format
  334. $base = 8;
  335. $app1 .= "II" . pack("v", 0x002A) . pack("V", $base);
  336.  
  337. $current_base = $base;
  338.  
  339. // IFD0
  340. list($ifd0_size, $ifd0_dir, $ifd0_link, $ifd0_data) =
  341. $this->_ifd0->to_binary($current_base, $this->_ifd1->has_entry());
  342. $current_base += $ifd0_size;
  343. // ExifIFD
  344. list($ifdexif_size, $ifdexif_dir, $ifdexif_link, $ifdexif_data) = $this->_ifdexif->to_binary($current_base);
  345. $current_base += $ifdexif_size;
  346. // IFD1
  347. list($ifd1_size, $ifd1_dir, $ifd1_link, $ifd1_data) = $this->_ifd1->to_binary($current_base);
  348. $current_base += $ifd1_size;
  349.  
  350. $app1 .= $ifd0_dir;
  351. if($this->_ifd1->has_entry()){
  352. $app1 .= pack("V", $base + $ifd0_size + $ifdexif_size);
  353. }
  354. else{
  355. $app1 .= $ifd0_link;
  356. }
  357. $app1 .= $ifd0_data;
  358.  
  359. $app1 .= $ifdexif_dir . $ifdexif_link . $ifdexif_data;
  360.  
  361. $app1 .= $ifd1_dir . $ifd1_link . $ifd1_data;
  362.  
  363. if($ifd0_size + $ifdexif_size + $ifd1_size > 0){
  364. // Marker
  365. $data .= pack("n", 0xFFE1);
  366. // App1 size
  367. $data .= pack("n", 2 + strlen($app1));
  368. // App1 data
  369. $data .= $app1;
  370. }
  371.  
  372.  
  373. // remain data
  374. $data .= $reader->read();
  375.  
  376. $reader->close();
  377. return $data;
  378. }
  379. }
  380.  
  381. class Reader {
  382.  
  383. const BYTE_ORDER_BE = 1;
  384. const BYTE_ORDER_LE = 2;
  385.  
  386. function __construct($path = ""){
  387. $this->_byte_order = self::BYTE_ORDER_BE;
  388.  
  389. if($path){
  390. $this->open($path);
  391. }
  392. }
  393.  
  394. function open($path){
  395. $this->_fp = fopen($path, "r");
  396. }
  397.  
  398. function close(){
  399. fclose($this->_fp);
  400. }
  401.  
  402. function set_byte_order($order){
  403. $this->_byte_order = $order;
  404. }
  405.  
  406. function get_byte_order($order){
  407. return $this->_byte_order;
  408. }
  409.  
  410. function is_be(){
  411. return ($this->_byte_order == self::BYTE_ORDER_BE);
  412. }
  413.  
  414. function is_le(){
  415. return ( ! $this->is_be());
  416. }
  417.  
  418. function seek($offset){
  419. fseek($this->_fp, $offset, SEEK_CUR);
  420. }
  421.  
  422. function read($length = 0){
  423. if(feof($this->_fp)){
  424. return NULL;
  425. }
  426.  
  427. if($length){
  428. return fread($this->_fp, $length);
  429. }
  430. else{
  431. $data = '';
  432. while( ! feof($this->_fp)){
  433. $data .= fread($this->_fp, 4096);
  434. }
  435. return $data;
  436. }
  437. }
  438.  
  439. function read_hex_string($length){
  440. $data = $this->read($length);
  441. $format = "H*";
  442. return array_shift(unpack($format, $data));
  443. }
  444.  
  445. function read_byte(){
  446. $data = $this->read(1);
  447. $format = "c";
  448. return array_shift(unpack($format, $data));
  449. }
  450.  
  451. function read_unsigned_byte(){
  452. $data = $this->read(1);
  453. $format = "C";
  454. return array_shift(unpack($format, $data));
  455. }
  456.  
  457. function read_unsigned_short(){
  458. $data = $this->read(2);
  459. $format = ($this->is_be()) ? "n" : "v";
  460. return array_shift(unpack($format, $data));
  461. }
  462.  
  463. function read_unsigned_long(){
  464. $data = $this->read(4);
  465. $format = ($this->is_be()) ? "N" : "V";
  466. return array_shift(unpack($format, $data));
  467. }
  468.  
  469. }
Add Comment
Please, Sign In to add comment