2

I am in the proces of writing a Joomla extension that utilizes metadata embedded in image files in IPTC, EXIF and XMP chunks. In particular, I want to gain access to "Title", "Tags", "Date Taken" and "GPS coordinates". The code embedded below is written by Lukáš Řádek and appears in https://stackoverflow.com/questions/1578169/how-can-i-read-xmp-data-from-a-jpg-with-php

<?php
    libxml_use_internal_errors(true);

    function getXmpData($filename, $chunk_size = 50000) {      
       $buffer = NULL;
       if (($file_pointer = fopen($filename, 'r')) === FALSE) {
           throw new RuntimeException('Could not open file for reading');
      }

      $chunk = fread($file_pointer, $chunk_size);
      if (($posStart = strpos($chunk, '<x:xmpmeta')) !== FALSE) {
          $buffer = substr($chunk, $posStart);
          $posEnd = strpos($buffer, '</x:xmpmeta>');
          $buffer = substr($buffer, 0, $posEnd + 12);
      }

    fclose($file_pointer);

    // recursion here
    if(!strpos($buffer, '</x:xmpmeta>')){
        $buffer = getXmpData($filename, $chunk_size*2);
    }

    return $buffer;
    }

    $xmp_data = getXmpData('test.jpg',1024);
    $xmp    = simplexml_load_string($xmp_data);
    if ($xmp === false) {
        echo "Failed loading XML\n";
        foreach(libxml_get_errors() as $error) {
            echo "\t", $error->message;
        }
    }

    $mystring = $xmp->asXML();
    echo "$mystring\n";
?>

It works like a charm but it would be hard to use in a general Joomla-extension because such an extension would have to address older as well as coming industry standards for image metadata encoding. Of course you cannot predict future standards but you can make life easier on yourself by utilizing packages/toolkits maintained by others ("Off-the-shelf-products"). Such a toolkit by Adobe is mentioned in Wikipedia: Extensible Metadata Platform but none of the implementations are in PHP.

Question 1) This where my skillset reaches its limit. Surely, there must be ways to compile such a toolset from Adobe into a format where it can be invoked from PHP? Please advise.

Question 2) I know how to build Joomla-extension packages using PHP/HTML/CSS/Javascript/Mysql and to implement MVC architecture etc. thanks to the wonderful lessons at Developing an MVC Component but how would you include the solution from Question 1) into a Joomla extension and make it executable on Linux as well as. Windows web hotels? I can live with the exclusion of Windows. If not possible at all - see Question 3

Question 3 If not possible to embed 3rd party compiled code in Joomla extensions, would you consider the possibility of installing compiled libraries on general web hosting servers and making them available to PHP/Joomla?

Sometimes it is harder to phrase the question than to find the answer. If you do not believe me, read the story of "42" in "Hitchikers guide to the Galaxy" :-)

3
  • I could offer some minor refinements to this script or convert it into a regex-based approach if valuable. Assuming the XMP data is valid XML, then a XML parser will be the best / most reliable approach. If you need help to adjust the code to accommodate known data structures that fail, then I would need to see the sample data to be able to suggest an amendment. For help parsing what is isolated in the $buffer, please include that sample data. Commented Nov 18, 2020 at 22:46
  • Thanks @mickmackusa. It is not really the parsing of the XML that is the problem. I have extended the script above to show how I do it. I think you can make it work with any JPG file that you have. (Just name it "test.jpg" and make sure, it is in the same folder as your script). The problem is that standards for embedding metadata in image files is evolving all the time. I wouldn't know what XML field to address in order to generate code that would work with older as well as never standards. If I tried to implement it myself I would put a heavy maintenance burden on my shoulders. Cont'd... Commented Nov 25, 2020 at 15:23
  • @mickmackusa - con'd. That is why I would utilize libraries/toolboxes maintained by others such as the ones from Adobe. But I do not know how to do it since the toolbox is not available in PHP. It is a maintenance problem. And an architecture problem. Commented Nov 25, 2020 at 15:25

2 Answers 2

1

You'll probably want to look at something like Composer to install libraries in your component or plugin.

Simple search: https://packagist.org/?query=iptc%20exif%20xmp

First one found: https://packagist.org/packages/wikimedia/xmp-reader

README

Reader for XMP data containing properties relevant to images, written in PHP.

Install

composer require wikimedia/xmp-reader

Usage

use Wikimedia\XMPReader\Reader as XMPReader;
    
//...

// Check if the php instance has the required supported libraries to parse XMP
$isXMPSupported = XMPReader::isSupported();

// Create a new instance
$xmp = new XMPReader();
// or
$xmp = new XMPReader( $logger, $filename );
// where $logger is an implementation of Psr\Log\LoggerInterface and $filename a string describing the origin of your XMP content

// To parse XMP data in $string
$xmp->parse( $string );

// To parse XMP Extended data in $string
$xmp->parseExtended( $string );

// To retrieve the parsed results
$results = $xmp->getResults();

The parsed results are returned in an array of 3 potential groups, which indicate their priority according to the Metadata Working Group guidance. http://www.metadataworkinggroup.org/pdf/mwg_guidance.pdf

[
    'xmp-exif' => [],
    'xmp-general' => [],
    'xmp-deprecated' => [],
]

The PDF in the README is a dead link, but you can see it here: https://web.archive.org/web/20180819023436/http://www.metadataworkinggroup.org/pdf/mwg_guidance.pdf

1
  • Thanks a lot, @James Shaver (sorry, do not know how to tag composite name). Working on it. Lots of new things to learn. Will let you know once I am done installing and testing. Commented Nov 28, 2020 at 22:41
1

Thanks a lot, James Shaver, I had no idea that such a PHP repository as packagist.org existed (tried Pear and Pecl and various Google searches). I found the Wikimedia/XMP-reader (Ver. 0.7) and installed and ran it according to instructions. Works great - see below. Also read your PDF and I learnt that the XMP Data block cannot be trusted to be authorative. Sometimes the authorative data must be found in the EXIF data block. Older image files may contain other formats in the EXIF data block. So if you are to find the authorative data blocks you must create a program that looks up the data blocks depending on image format (JPG, TIFF, PSD, PNG, GIF ...), parse the data blocks according to format and then decide which data in which block is authorative if not the XMP block. That complexity has basically taught me that it would be over ambitious of me to try to create a Joomla extension that would be able to access and use the right meta data in ANY given image file. Instead I am going to state that the XMP block will be considered authorative. You can accomplish that using a number of tools mentioned in Wikipedia: Extensible Metadata Platform. I personally use Photoshop Elements Organizer.

This is the running code tested under Visual Studio Encode and PHP 7.4 on Windows 10. It assumes that the XMP block can be found by looking for the string <x:xmpmeta' no matter what the image format.

<?php
  require __DIR__ . '/vendor/autoload.php';
  use Wikimedia\XMPReader\Reader as XMPReader;

 function getXmpData($filename, $chunk_size = 50000){      
     $buffer = NULL;
     if (($file_pointer = fopen($filename, 'r')) === FALSE) {
        throw new RuntimeException('Could not open file for reading');
     }

     $chunk = fread($file_pointer, $chunk_size);
     if (($posStart = strpos($chunk, '<x:xmpmeta')) !== FALSE) {
         $buffer = substr($chunk, $posStart);
         $posEnd = strpos($buffer, '</x:xmpmeta>');
         $buffer = substr($buffer, 0, $posEnd + 12);
     }

     fclose($file_pointer);

     // recursion here
     if(!strpos($buffer, '</x:xmpmeta>')){
         $buffer = getXmpData($filename, $chunk_size*2);
     }

     return $buffer;
 }
 $xmp_data = getXmpData('cnj.dk.20191018_084035.jpg',50000);

 // Check if the php instance has the required supported libraries to parse XMP
 $isXMPSupported = XMPReader::isSupported();

 if ($isXMPSupported) {
     echo "XMP suppported\n";
 } else {
     echo "XMP not supported\n";
     exit;
 }

 $xmp = new XMPReader();
 $xmp->parse($xmp_data);
 $xmp->parseExtended($xmp_data);
 $results = $xmp->getResults();
 var_dump($results);

 ?>

You can find the JPEG file used at https://cnj.dk/images/blog/2020/cnj.dk.20191018_084035.jpg

Here is a snippet of the output

            XMP suppported
        array(2) {
          ["xmp-exif"]=>
          array(33) {
            ["ImageWidth"]=>
            string(4) "4032"
            ....
            ["DateTimeOriginal"]=>
            string(19) "2019:10:18 10:40:35"
            ....
            ["GPSLatitude"]=>
            float(42.3402535)
            ["GPSLongitude"]=>
            float(-3.8358976833333)
            ....
      }
          ["xmp-general"]=>
          array(6) {
            ["DateTime"]=>
            string(19) "2020:03:30 22:48:09"
            ["Software"]=>
            string(23) "Elements Organizer 16.0"
            ["DateTimeDigitized"]=>
            string(19) "2019:10:18 10:40:35"
            ["DateTimeMetadata"]=>
            string(19) "2020:03:30 22:48:09"
            ["Keywords"]=>
            array(2) {
              [0]=>
              string(23) "Camino de Santiago 2019"
              ["_type"]=>
              string(2) "ul"
            }
            ["ImageDescription"]=>
            array(2) {
              ["x-default"]=>
              string(97) "Day 11: Beautiful wall painting in a small village shortly after Tardajos (Rabé de las Calzadas)"
              ["_type"]=>
              string(4) "lang"
            }
          }
        }

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.