diderich/metadata

假期 - Metadata / 用于从/向 JPEG 文件读取和写入元数据的 PHP 类

v1.1.1 2022-07-29 09:45 UTC

This package is auto-updated.

Last update: 2024-09-29 06:14:50 UTC


README

Latest Stable Version Minimum PHP version License: MIT Standard PHP Innovation Award

名称

Metadata - 一个用于以透明方式从 JPEG 文件读取和写入 照片元数据 的 PHP 类

版本

查看 CHANGELOG.md 和 php 常量 \Holiday\Metadata::VERSION 获取最新版本号。

奖项

PHP Innovation Award

描述

Metadata 类及其子类基于 IPTC 照片元数据标准 2021.1 实现了透明的读取和写入 IPTC 照片元数据,并从 JPEG 文件中读取 EXIF 数据,专注于对照片数据库或类似应用程序中搜索和管理照片相关的元数据元素。该类独特之处在于它支持多个并发语言用于标题/描述信息。

存在许多从各种格式的照片中读取和/或写入元数据的实现。通常,这些软件包和程序(其中以 exiftool 最为突出)专注于解码原始数据在其来源的上下文中的数据,例如解码 EXIF 数据或解码 IPTC/APP13 数据。本项目采用不同的方法!它采用以用户为中心的方法,并以透明的方式向用户展示 IPTC 和 EXIF 照片元数据,即用户无需担心数据来源和/或编码方式。

该软件包是在 HOLIDAY 照片数据库软件的背景下开发的(请参阅 https://www.cdsp.photo/technology/holiday-database/),作为最终用户。因此,它可能缺乏适用于不同用途的功能。这也解释了使用顶级命名空间前缀 \Holiday 的原因。

将 JPEG 图像文件解码/编码到不同的标题段以及将 IPTC 数据解码/编码的灵感部分来自 PHP JPEG 元数据工具包 (https://www.ozhiker.com/electronics/pjmt/),在此正式致谢。

透明访问 JPEG 图像元数据

\Holiday\Metadata 类允许读取和写入 JPEG 文件中最相关的(从作者的角度看)照片元数据,并以透明的方式访问它们,无论它们存储在哪里/如何。可以使用以下代码从文件 FILENAME 读取字段 FIELD_ID 的数据。如果没有找到数据,则 $data 将等于 false,否则它将包含读取的数据,可能是字符串、整数或数组

$metadata = new \Holiday\Metadata();

$metadata->read(FILENAME);
$data = $metadata->get(\Holiday\Metadata::FIELD_ID);

可以使用以下代码将字段 FIELD_ID 的数据更新为 NEW_DATA 并将其写回到同一文件或新的不同文件 NEW_FILENAME

$metadata->set(\Holiday\Metadata::FIELD_ID, NEW_DATA); 
$metadata->write(NEW_FILENAME);

文件 NEW_FILENAME 将自动覆盖。

将一个文件的元数据的可编辑部分粘贴到名为 PASTE_FILENAME 的另一个文件(必须存在)中,可以使用以下代码完成

$metadata = new \Holiday\Metadata();

$metadata->read(FILENAME);
$metadata->paste(PASTE_FILENAME);

标题/描述字段 \Holiday\Metadata::CAPTION 支持多种语言,默认语言名称根据标准命名 \Holiday\Metadata::LANG_DEFAULT。通过以下代码访问字段数据

// Retrieve the default data
$caption = $metadata->get(\Holiday\Metadata::CAPTION, lang: \Holiday\Metadata::LANG_DEFAULT);

// Retrieve the data in a specific language, e.g., 'de-de' (German)
$caption = $metadata->get(\Holiday\Metadata::CAPTION, lang: 'de-de');

// Retrieve the data in all available languages (an array, indexed with the respective language identifier is returned)
$caption_ary = $metadata->get(\Holiday\Metadata::CAPTION, lang: \Holiday\Metadata::LANG_ALL);

元数据以以下顺序读取,第一个读取的数据具有优先权,即如果标题数据存储在 IPTC/APP13 段以及 XMP/APP1 和 EXIF/APP1 段,则 IPTC/APP13 中的数据将占主导地位。

  1. IPTC/APP13 段中找到的数据
  2. XMP/APP1 段中找到的数据
  3. EXIF/APP1 段中找到的数据

当回写元数据时,IPTC/APP13 和 XMP/APP1 字段都将被更新以确保数据的一致性。在当前实现中,存储在 EXIF/APP1 段的用户可修改数据将被清除,即用 0x00 替换,而不是更新为实际值。这一选择是由于以下原因:a) 认为EXIF/APP1不应包含用户可编辑数据;b) EXIF/APP1 数据格式的复杂性。

异常处理

在出现非致命错误时,所有函数都返回 false。致命错误会触发抛出 \Holiday\Metadata\Exception 异常(该异常扩展了通用 Exception 类)。与错误相关的数值代码($exception->getCode())允许识别错误的类型。常量在异常类 \Holiday\Metadata\Exception 中定义。

\Holiday\Metadata\Exception::getData() 函数以人类可读的形式返回与抛出的异常相关的附加数据。

所有异常消息都可以使用 gettext 库进行翻译。翻译模板在 locale 目录中提供。

示例

以下示例展示了如何以透明的方式读取、修改和写入 JPEG 文件的元数据(另请参阅 test/example.php)。它需要一个符合 PSR-4 的机制来加载类文件。

// Create an instance of the Metadata class
$metadata = new \Holiday\Metadata();

try {
  // Read metadata from the image file FILENAME
  $metadata->read(FILENAME);

  // Read some of the metadata (assuming metadata is available)
  $caption = $metadata->get(\Holiday\Metadata::CAPTION, lang: \Holiday\Metadata::LANG_DEFAULT);
  $caption_ary = $metadata->get(\Holiday\Metadata::CAPTION);
  $date_created = $metadata->get(\Holiday\Metadata::CREATED_DATETIME);
  $credit = $metadata->get(\Holiday\Metadata::CREDIT);
  $city = $metadata->get(\Holiday\Metadata::CITY);
  $country = $metadata->get(\Holiday\Metadata::COUNTRY);
  $people = $metadata->get(\Holiday\Metadata::PEOPLE);
  $keywords = $metadata->get(\Holiday\Metadata::KEYWORDS);
  $event = $metadata->get(\Holiday\Metadata::EVENT);
  if(!empty($caption_ary)) {
    echo "CAPTION:".PHP_EOL;
    foreach($caption_ary as $lang => $text) 
      echo "   $lang: $text".PHP:EOL;
  }
  if($credit !== false) echo "CREDIT:   $credit".PHP_EOL;
  if($city !== false && $country !== false) echo "PLACE:    $city, $country".PHP_EOL;
  if($date_created !== false) echo "CREATED:  ".date('d.m.Y', $date_created).PHP_EOL;
  if($event !== false) echo "EVENT:    $event".PHP_EOL;
  if($keywords !== false) echo "KEYWORDS: ".implode(', ', $keywords).PHP_EOL;
  if($people !== false) echo "PEOPLE:   ".implode(', ', $people).PHP_EOL;

  // Update some data
  if($caption !== false && $date_created !== false && $city !== false && $country !== false && $credit !== false) {
    $caption = strtoupper($city).', '.strtoupper($country).' - '.strtoupper(date('F d', $date_created)).': '.
      $caption.' (Photo by '.$credit.')';
    $metadata->set(\Holiday\Metadata::CAPTION, $caption, lang: \Holiday\Metadata::LANG_DEFAULT);
  }
  if($event !== false) $metadata->set(\Holiday\Metadata::EVENT, strtoupper($event));


  // Write metadata back to the image file
  $metadata->write(FILENAME);

  // Paste user modifiable metadata do a different file
  $metadata->paste(ALTERNATE_FILE);

文件和目录结构

显示的目录结构描述了库中最相关的目录和文件。

所有类文件都按照 phpDocumenter(https://www.phpdoc.org/)兼容的方式注释。

|-- src/                    Directory containing all class files required to use the library
  |-- Metadata.php          Class implementing transparent read/write access to JPEG metadata
    |-- Metadata
      |-- Exception.php     Exception handling class
      |-- Exif.php          Class reading and writing EXIF/APP1 specific raw data
      |-- Iptc.php          Class reading and writing IPTC/APP13 specific raw data
      |-- Jpeg.php          Class reading and writing JPEG header segment data
      |-- Xmp
	    |-- Document.php    Class encoding and decoding Xmp data in a DOMDocument class format
      |-- Xmp.php           Class reading and writing XMP/APP1 specific data
|-- test/
  |-- example.php           Sample program using the metadata class libraries
  |-- example.txt           Sample program output
  |-- img.example.jpg       Sample image including data in all supported fields
  |-- img.mlexample.jpg     Sample image containing caption data in two languages
|-- locale/
  |-- metadata.pot          Untranslated text messages generated by the classes
|-- README.md
|-- CHANGELOG.md
|-- LICENSE
|-- composer.json

安装

可以使用 composer 安装元数据类。

composer require diderich/metadata

测试

这些类已经在多个由不同相机型号编写的非详尽列表的 JPEG 图像上进行了成功测试。由于解码数据高度依赖于编码数据时的选择,该类可能无法解码一些不常见的 JPEG 文件编码。特别是对于使用 XMP 格式(以不兼容的方式存储)的数据。

对于这些类不存在或计划进行详尽的测试概念。

如果您发现无法正确解码的 JPEG 文件,请在 问题 部分提出问题,并包括 JPEG 文件的副本。没有附带 JPEG 数据的问题将被作者关闭,不予考虑。

开放问题

以下限制目前存在,并得到认可:

  • IPTC/APP13:只能读取 Latin 1 和 UTF-8 编码的数据。其他数据格式将抛出异常。
  • XMP/APP1:类 Xmp 和/或 Xmp\Document 可能无法识别所有格式不佳/格式错误的 XMP/APP1 数据。
  • XMP/APP1:如果在命名空间 NS 中更新数据元素 TAG,则它也会在 Iptc4xmpCore、dc、aux、xmp、photoshop 和 photomechanic 等命名空间中更新。如果这些命名空间中存在数据条目。其他命名空间,例如其他应用程序使用的命名空间,不会更新/同步。这可能导致数据不一致。
  • EXIF/APP1:尽管返回了所有读取的数据,但只有被认为相关的数据被解码。例如,缩略图或标记注释不会被解码。未解码的数据以人类可读的十六进制字符串形式返回。
  • EXIF/APP1:由于写入 EXIF/APP1 数据的复杂性,EXIF IFD 中的 IPTC/NAA 记录不会更新。它们被 \x00 覆盖。作者不打算在未来移除这些限制。

重要提示

包含多语言标题/描述数据的文件可能无法被大多数(如果不是所有)照片编辑软件正确读取/保存,如 Photoshop。据作者所知,目前没有现成的软件支持如 XMP 规范(参考为 Lang Alt)中所述的多个并发语言。有关互操作性的更多详细信息,请参阅 https://iptc.org/standards/photo-metadata/interoperability-tests/

支持

免费社区支持可在 github.com 的“问题”和“讨论”部分获得。作者可能会参与提供免费支持,但不保证一定会这样做。作者提供保证的付费支持。

项目状态和路线图

项目正在积极维护。目前没有计划添加新功能。

作者

克劳德·迪德里希(Claude Diderich)(cdiderich@cdsp.photo),https://www.cdsp.photo。作者将根据自己的意愿回复电子邮件。

许可证

MIT https://open-source.org.cn/licenses/mit