daily pastebin goal
71%
SHARE
TWEET

Untitled

a guest Jan 22nd, 2018 52 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  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. }
RAW Paste Data
Top