2024年PHP日本大会

iptcembed

(PHP 4, PHP 5, PHP 7, PHP 8)

iptcembed将二进制IPTC数据嵌入JPEG图像

描述

iptcembed(字符串 $iptc_data, 字符串 $filename, 整数 $spool = 0): 字符串|布尔值

将二进制IPTC数据嵌入JPEG图像。

参数

iptc_data

要写入的数据。

filename

JPEG图像的路径。

spool

Spool标志。如果spool标志小于2,则JPEG将作为字符串返回。否则,JPEG将打印到STDOUT。

返回值

如果spool小于2,则返回JPEG,否则在失败时返回false。成功时返回true,失败时返回false

示例

示例 #1 将IPTC数据嵌入JPEG

<?php

// Thies C. Arntzen编写的iptc_make_tag()函数
function iptc_make_tag($rec, $data, $value)
{
$length = strlen($value);
$retval = chr(0x1C) . chr($rec) . chr($data);

if(
$length < 0x8000)
{
$retval .= chr($length >> 8) . chr($length & 0xFF);
}
else
{
$retval .= chr(0x80) .
chr(0x04) .
chr(($length >> 24) & 0xFF) .
chr(($length >> 16) & 0xFF) .
chr(($length >> 8) & 0xFF) .
chr($length & 0xFF);
}

return
$retval . $value;
}

// JPEG文件的路径
$path = './phplogo.jpg';

// 设置IPTC标签
$iptc = array(
'2#120' => '测试图像',
'2#116' => '版权所有 2008-2009,PHP小组'
);

// 将IPTC标签转换为二进制代码
$data = '';

foreach(
$iptc as $tag => $string)
{
$tag = substr($tag, 2);
$data .= iptc_make_tag(2, $tag, $string);
}

// 嵌入IPTC数据
$content = iptcembed($data, $path);

// 将新的图像数据写入文件。
$fp = fopen($path, "wb");
fwrite($fp, $content);
fclose($fp);
?>

备注

注意:

此函数不需要GD图像库。

添加备注

用户贡献的备注 17条备注

ebashkoff at gmail dot com
10年前
以下代码从源文件嵌入IPTC APP段13和EXIF APP段1数据,并将其嵌入目标文件。这克服了iptcembed语句似乎只嵌入IPTC数据而不嵌入EXIF数据的限制。

function transferIptcExif2File($srcfile, $destfile) {
// 函数将 EXIF (APP1) 和 IPTC (APP13) 元数据从 $srcfile 复制到 $destfile
// JPEG 文件格式为 0xFFD8 + [APP0] + [APP1] + ... [APP15] + <图像数据>,其中 [APPi] 为可选段
// APPi 段 (i = 0x0 到 0xF) 格式为 0xFFEi + 0xMM + 0xLL + <数据> (其中 0xMM 为
// (strlen(<数据>) + 2) 的高 8 位,0xLL 为低 8 位)
// (strlen(<数据>) + 2)

如果 (file_exists($srcfile) && file_exists($destfile)) {
$srcsize = @getimagesize($srcfile, $imageinfo);
// 从源文件准备 EXIF 数据字节
$exifdata = (is_array($imageinfo) && key_exists("APP1", $imageinfo)) ? $imageinfo['APP1'] : null;
如果 ($exifdata) {
$exiflength = strlen($exifdata) + 2;
如果 ($exiflength > 0xFFFF) 返回 false;
// 构造 EXIF 段
$exifdata = chr(0xFF) . chr(0xE1) . chr(($exiflength >> 8) & 0xFF) . chr($exiflength & 0xFF) . $exifdata;
}
// 从源文件准备 IPTC 数据字节
$iptcdata = (is_array($imageinfo) && key_exists("APP13", $imageinfo)) ? $imageinfo['APP13'] : null;
如果 ($iptcdata) {
$iptclength = strlen($iptcdata) + 2;
如果 ($iptclength > 0xFFFF) 返回 false;
// 构造 IPTC 段
$iptcdata = chr(0xFF) . chr(0xED) . chr(($iptclength >> 8) & 0xFF) . chr($iptclength & 0xFF) . $iptcdata;
}
$destfilecontent = @file_get_contents($destfile);
如果 (!$destfilecontent) 返回 false;
如果 (strlen($destfilecontent) > 0) {
$destfilecontent = substr($destfilecontent, 2);
$portiontoadd = chr(0xFF) . chr(0xD8); // 变量累加新的和原始的 IPTC 应用段
$exifadded = !$exifdata;
$iptcadded = !$iptcdata;

当 ((substr($destfilecontent, 0, 2) & 0xFFF0) === 0xFFE0) {
$segmentlen = (substr($destfilecontent, 2, 2) & 0xFFFF);
$iptcsegmentnumber = (substr($destfilecontent, 1, 1) & 0x0F); // 第二个字节的最后 4 位是 IPTC 段号
如果 ($segmentlen <= 2) 返回 false;
$thisexistingsegment = substr($destfilecontent, 0, $segmentlen + 2);
如果 ((1 <= $iptcsegmentnumber) && (!$exifadded)) {
$portiontoadd .= $exifdata;
$exifadded = true;
如果 (1 === $iptcsegmentnumber) $thisexistingsegment = '';
}
如果 ((13 <= $iptcsegmentnumber) && (!$iptcadded)) {
$portiontoadd .= $iptcdata;
$iptcadded = true;
如果 (13 === $iptcsegmentnumber) $thisexistingsegment = '';
}
$portiontoadd .= $thisexistingsegment;
$destfilecontent = substr($destfilecontent, $segmentlen + 2);
}
如果 (!$exifadded) $portiontoadd .= $exifdata; // 如果 EXIF 数据未添加,则添加
如果 (!$iptcadded) $portiontoadd .= $iptcdata; // 如果 IPTC 数据未添加,则添加
$outputfile = fopen($destfile, 'w');
如果 ($outputfile) 返回 fwrite($outputfile, $portiontoadd . $destfilecontent); 否则返回 false;
} 否则 {
返回 false;
}
} 否则 {
返回 false;
}
}
support at image-host-script dot com
16 年前
我最近编写了一个用于操作 JPEG 图片中 IPTC 数据的类。它也可以轻松地编辑现有数据。它只是将示例编译到单个类中。

<?

/************************************************************\

IPTC EASY 1.0 - JPEG 图片的 IPTC 数据操作器

版权所有 www.image-host-script.com

2008 年 9 月 15 日

\************************************************************/

DEFINE('IPTC_OBJECT_NAME', '005');
DEFINE('IPTC_EDIT_STATUS', '007');
DEFINE('IPTC_PRIORITY', '010');
DEFINE('IPTC_CATEGORY', '015');
DEFINE('IPTC_SUPPLEMENTAL_CATEGORY', '020');
DEFINE('IPTC_FIXTURE_IDENTIFIER', '022');
DEFINE('IPTC_KEYWORDS', '025');
DEFINE('IPTC_RELEASE_DATE', '030');
DEFINE('IPTC_RELEASE_TIME', '035');
DEFINE('IPTC_SPECIAL_INSTRUCTIONS', '040');
DEFINE('IPTC_REFERENCE_SERVICE', '045');
DEFINE('IPTC_REFERENCE_DATE', '047');
DEFINE('IPTC_REFERENCE_NUMBER', '050');
DEFINE('IPTC_CREATED_DATE', '055');
DEFINE('IPTC_CREATED_TIME', '060');
DEFINE('IPTC_ORIGINATING_PROGRAM', '065');
DEFINE('IPTC_PROGRAM_VERSION', '070');
DEFINE('IPTC_OBJECT_CYCLE', '075');
DEFINE('IPTC_BYLINE', '080');
DEFINE('IPTC_BYLINE_TITLE', '085');
DEFINE('IPTC_CITY', '090');
DEFINE('IPTC_PROVINCE_STATE', '095');
DEFINE('IPTC_COUNTRY_CODE', '100');
DEFINE('IPTC_COUNTRY', '101');
DEFINE('IPTC_ORIGINAL_TRANSMISSION_REFERENCE', '103');
DEFINE('IPTC_HEADLINE', '105');
DEFINE('IPTC_CREDIT', '110');
DEFINE('IPTC_SOURCE', '115');
DEFINE('IPTC_COPYRIGHT_STRING', '116');
DEFINE('IPTC_CAPTION', '120');
DEFINE('IPTC_LOCAL_CAPTION', '121');

类 iptc {
var $meta=Array();
var $hasmeta=false;
var $file=false;


函数 iptc($filename) {
$size = getimagesize($filename,$info);
$this->hasmeta = isset($info["APP13"]);
如果($this->hasmeta)
$this->meta = iptcparse ($info["APP13"]);
$this->file = $filename;
}
函数 set($tag, $data) {
$this->meta ["2#$tag"]= Array( $data );
$this->hasmeta=true;
}
函数 get($tag) {
返回 isset($this->meta["2#$tag"]) ? $this->meta["2#$tag"][0] : false;
}

函数 dump() {
print_r($this->meta);
}
函数 binary() {
$iptc_new = '';
循环 (array_keys($this->meta) 为 $s) {
$tag = str_replace("2#", "", $s);
$iptc_new .= $this->iptc_maketag(2, $tag, $this->meta[$s][0]);
}
返回 $iptc_new;
}
函数 iptc_maketag($rec,$dat,$val) {
$len = strlen($val);
如果 ($len < 0x8000) {
返回 chr(0x1c).chr($rec).chr($dat).
chr($len >> 8).
chr($len & 0xff).
$val;
} 否则 {
返回 chr(0x1c).chr($rec).chr($dat).
chr(0x80).chr(0x04).
chr(($len >> 24) & 0xff).
chr(($len >> 16) & 0xff).
chr(($len >> 8 ) & 0xff).
chr(($len ) & 0xff).
$val;

}
}
函数 write() {
如果(!function_exists('iptcembed')) 返回 false;
$mode = 0;
$content = iptcembed($this->binary(), $this->file, $mode);
$filename = $this->file;

@unlink($filename); # 删除现有文件

$fp = fopen($filename, "w");
fwrite($fp, $content);
fclose($fp);
}

# 需要安装 GD 库
函数 removeAllTags() {
$this->hasmeta=false;
$this->meta=Array();
$img = imagecreatefromstring(implode(file($this->file)));
@unlink($this->file); # 删除现有文件
imagejpeg($img,$this->file,100);
}
};


?>

示例:读取版权字符串

$i = new iptc("test.jpg");
echo $i->get(IPTC_COPYRIGHT_STRING);

更新版权声明
$i = new iptc("test.jpg");
echo $i->set(IPTC_COPYRIGHT_STRING,"Here goes the new data");
$i->write();

注意1:数据可以是任何内容,甚至是二进制文件。到目前为止,我已经测试并将 MS-Excel 文件直接嵌入到 jpeg 中,并且工作完美。

注意2:写入功能需要 GD 库。

更多改进/更改可在 www.image-host-script.com 查看

希望对您有所帮助。
Ali..
critto at o2 dot pl
14 年前
如果您只想复制 IPTC 数据(例如,在缩略图创建时),则无需将二进制 IPTC 数据解析为数组再转换回来;只需执行以下操作:

<?php
$fullFilePath
='photo1.jpg';
$fullPathThumb = 'photo1thumb.jpg';
$imagesize = getImageSize($fullFilePath, $info);
如果(isset(
$info['APP13'])){
$content = iptcembed($info['APP13'], $fullPathThumb);
@
unlink($fullPathThumb);
$fw = fopen($fullPathThumb, 'w');
fwrite($fw, $content);
fclose($fw);
}
?>
evan at nospam dot ozhiker dot com
20 年前
您可能已经注意到,Photoshop 中的几个元数据字段无法通过 IPTC 获取。
此外,Photoshop 现在使用 XMP 作为其主要元数据,这意味着只有在没有 XMP 的情况下,Photoshop 才会读取 IPTC。

我编写了一个库“PHP JPEG 元数据工具包”,它绕过了这个问题,因为它允许读取、写入和解释几乎任何类型的元数据,包括 XMP、IPTC 和 EXIF。

试试看,并从以下地址下载:
http://www.ozhiker.com/electronics/pjmt/index.html
Christoph Tavan
13 年前
对于所有在 IPTC 字段中遇到 Unicode (UTF-8) 文本问题的人,请设置信封中的“编码字符集”字段 (1:90,参见 http://www.iptc.org/std/IIM/4.1/specification/IIMV4.1.pdf)。

这可以通过以下几行代码在 $data 块的开头实现:

<?php
// 这两行代码确保 UTF8 编码有效(设置信封中的 1:90 字段)
// 请参阅 http://cpanforum.com/threads/2114 获取提示
$utf8seq = chr(0x1b) . chr(0x25) . chr(0x47);
$length = strlen($utf8seq);
$data = chr(0x1C) . chr(1) . chr('090') . chr($length >> 8) . chr($length & 0xFF) . $utf8seq;
?>

之后,您可以像示例中那样继续:

<?php
foreach($iptc as $tag => $string)
{
$tag = substr($tag, 2);
$data .= iptc_make_tag(2, $tag, $string);
}
?>
rupix at rediffmail dot com
22 年前
Windows 区分“文本”和“二进制”文件。因此,如果您在 Windows 平台上运行上述代码,则会生成损坏的图像。要解决此问题,请将 fopen() 中的文件模式设置为“wb”而不是“w”。

干杯!!

Rupinder
soporte at etic dot com dot mx
13 年前
在尝试使用二进制函数写入多个关键字(数组)时,我发现一个错误……请考虑使用以下代码:

<?php
function binary() {
$iptc_new = '';
foreach (
array_keys($this->meta) as $s) {
$tag = str_replace("2#", "", $s);
if(
count($this->meta[$s])>1){
foreach (
$this->meta[$s] as $row){
$iptc_new .= $this->iptc_maketag(2, $tag, $row);
}
}else {
$iptc_new .= $this->iptc_maketag(2, $tag, $this->meta[$s][0]);
}
}
return
$iptc_new;
}
?>
[email protected]
20 年前
我花了一整天调试我的代码(基于下面knut发布的示例),直到我发现,只有当jpeg_file_name中指定的图像已经包含IPTC字段时,iptcembed()才能正常工作。

这意味着你不能在JPEG文件中写入IPTC字段,除非它预先存在IPTC信息。

IPTC字段的更新也只对少数几个文件有效,我不知道它是否有效取决于什么。(当然,它取决于IPTC头,这是肯定的 :-))

我使用的是PHP 4.2.1,也许在较新的版本中已修复此问题,但我并不相信……

尽管如此,这里有一些我尝试过的代码片段

我将下面knut示例中的这一行:

<?
$iptc_old = iptcparse ($info["APP13"]);
?>

替换为:

<?
$iptc_old["2#000"][0] = chr(0) . chr(2);
?>

根据IPTC规范,这只会创建一个空的“Header”。使用iptcparse()获取IPTC信息时,也会包含这个。所以我的目的是创建一个全新的header,但是之后使用iptcembed()时,新文件的尺寸比原始文件略小,但没有存储任何IPTC信息。
[email protected]
20 年前
我在ltrim函数中遇到一个问题:有时它删除的字符比必须删除的字符多,因此最好删除XMP_remove_from_jpeg函数中的以下行:

<?
// $image = rtrim ($image, $endchar);
?>

在XMP_remove_from_jpeg函数中
[email protected]
13 年前
使用下面提到的类文件时,你会注意到使用set()函数设置关键字数组将不起作用。请尝试以下方法:
<?php
function set($tag, $data) {
if(
is_array($data)){
$c = count($data);
for (
$i=0; $i <$c; $i++){
$this->meta["2#$tag"][$i] = $data[$i];
}
$this->hasmeta=true;
} else {
$this->meta["2#$tag"]= Array( $data );
$this->hasmeta=true;
}
}
?>
[email protected]
17年前
如果你的文件没有任何IPTC记录,你可以通过使用空字符串作为第一个参数调用iptcembed来添加一个新的空记录,如下所示:

$buffer = iptcembed("",$imagename,0);
$fp = fopen($imagename,"w");
fwrite($fp,$buffer);
fclose($fp);
Bryce Fisher
17年前
在下面Thomas精彩的代码片段中,第二个参数需要更改为“$filename”,否则脚本无法写入文件。因此代码应如下所示:

<?php
//此函数删除JPG中的所有头数据
function remove_XMP($image_in, $filename) {
$filename_in = addslashes($image_in);
list(
$width, $height) = getimagesize($filename_in);
$image_dest = imagecreatetruecolor($width, $height);
$image = imagecreatefromjpeg($filename_in);
imagecopyresampled($image_dest, $image, 0, 0, 0, 0, $width, $height,$width, $height);
imagejpeg($image_dest, $filename);
}
?>
muguran
19年前
注意不要添加值为0的代码;其行为可能很奇怪……
(例如 $iptc_old["2#015"][0] = "")
[email protected]
20 年前
是的,是我 :-) 在花了一些时间寻找其他写入JPEG文件IPTC字段的方法后,我找到了这个:

http://www.zonageek.com/software/php/jpeg/index.php

这个看起来工作得很好,记住你必须安装PEAR才能运行它(Debian软件包php4-pear)。
[email protected]
20 年前
使用PEAR函数读取和更改IPTC没有问题,但最近的Adobe软件在JPEG文件上添加了XMP数据,并读取它们而不是IPTC数据。如果你需要更改JPEG文件上的IPTC,并希望Adobe PS7读取它们,你有两种解决方案:
- 写入XMP和IPTC数据
- 写入IPTC数据并删除XMP数据

因为我没有足够的时间来处理XMP数据,所以我选择了第二种方案。这是这项工作的成果

<?php
// 从JPEG文件中删除XMP数据
// (c) Patrick Premartin 19/02/2004

function XMP_remove_from_jpeg (&$image) {
$xmp_str = "http://ns.adobe.com/xap/1.0/";
$xmp_end = "<?xpacket end='w'?>";
$n_str = strpos ($image, $xmp_str);
$n_end = strpos ($image, $xmp_end);
if ((
$n_str !== false) && ($n_end !== false) && ($n_str < $n_end)) {
$n_str -= 4; // FF E1 .x. .y. (xy是XMP块的长度 -> 此块的第一个字符
$n_end += strlen ($xmp_end)-1; // 此块的最后一个字符
$endchar = $image [$n_str-1];
if (
$endchar == " ") {
$endchar = "A";
} else {
$endchar = " ";
}
$xmp_len = $n_end-$n_str+1;
$img_len = strlen ($image);
$len = $img_len - $xmp_len;
for (
$i = $n_str; $i < $img_len; $i ++) {
if (
$i < $len) {
$image [$i] = $image [$i+$xmp_len];
} else {
$image [$i] = $endchar;
}
}
$image = rtrim ($image, $endchar);
return
true;
} else {
return
false;
}
}

function
XMP_remove_from_jpegfile ($filename_in, $filename_out="") {
if (
""==$filename_out) {
$filename_out = $filename_in;
}
if ((
""!=$filename_in) && (file_exists ($filename_in)) && (($len_in = filesize ($filename_in)) > 0)) {
// 将文件加载到内存中
$f_in = fopen ($filename_in, "rb");
$img = fread ($f_in, $len_in);
fclose ($f_in);
// 删除图像的XMP
if (XMP_remove_from_jpeg ($img)) {
// 将文件保存到磁盘
$f_out = fopen ($filename_out, "wb");
fwrite ($f_out, $img, strlen ($img));
fclose ($f_out);
}
}
}

XMP_remove_from_jpegfile ("ps7_hr.jpg", "ps7_hr_.jpg"); // 创建一张没有XMP数据的新图片

XMP_remove_from_jpegfile ("ps8_hr.jpg"); // 替换现有文件
?>

将来,我将致力于以相同的方式更改XMP数据和IPTC,并将其发布在那里或作为PEAR的一部分。

愿原力与我们同在 :-)
knut dot satre dot NoSpam at No_Spam dot nord dot no
22 年前
使用函数iptcparse和iptcembed读取图像中的IPTC文本,更改文本并写入新文件的示例。

还列出了最常见的IPTC字段。

<?
// 原文件名
$image_name_old = "test.jpg";

// 新文件名
$image_name_new = "test2.jpg";

// 将IPTC文本读取到数组'$iptc'中
// '#'后面的数字是IPTC字段
// 例:$iptc["2#120"][0]是标题
// $iptc["2#055"][0];是创建日期
$size = GetImageSize ("$image_name_old",&$info);
$iptc_old = iptcparse ($info["APP13"]);

// 添加或替换IPTC文本
// 此示例替换原始类别或在不存在时创建它
$iptc_old["2#015"][0] = "Sport";
// .. 并向原始标题添加更多文本
$iptc_old["2#120"][0] .= " More caption text";

// 创建新的IPTC字符串
foreach (array_keys($iptc_old) as $s){
// 查找IPTC编号
$tag = str_replace("2#", "", $s);
// 创建字符串
$iptc_new .= iptc_maketag(2, $tag, $iptc_old[$s][0]);
}

// 将原始文件和新的IPTC文本放入$content中
// 模式0 - 将图像文件放入$content中
// 模式1 - 将图像文件放入$content中并直接发送到Web客户端
// 模式2 - 将图像文件发送到Web客户端
$mode = 0;
$content = iptcembed($iptc_new, $image_name_old, $mode);

// 写入新文件
$fp = fopen($image_name_new, "w");
fwrite($fp, $content);
fclose($fp);

// 用于格式化新的IPTC文本的函数(感谢Thies C. Arntzen)
function iptc_maketag($rec,$dat,$val){
$len = strlen($val);
if ($len < 0x8000)
返回 chr(0x1c).chr($rec).chr($dat).
chr($len >> 8).
chr($len & 0xff).
$val;
else
返回 chr(0x1c).chr($rec).chr($dat).
chr(0x80).chr(0x04).
chr(($len >> 24) & 0xff).
chr(($len >> 16) & 0xff).
chr(($len >> 8 ) & 0xff).
chr(($len ) & 0xff).
$val;
}

?>

--- 最常见的IPTC字段
005 - 对象名称
007 - 编辑状态
010 - 优先级
015 - 类别
020 - 补充类别
022 - 固定标识符
025 - 关键词
030 - 发布日期
035 - 发布時間
040 - 特殊说明
045 - 参考服务
047 - 参考日期
050 - 参考编号
055 - 创建时间
060 - 创建时间
065 - 源程序
070 - 程序版本
075 - 对象周期
080 - 署名
085 - 署名头衔
090 - 城市
095 - 省份/州
100 - 国家代码
101 - 国家
103 - 原始传输参考
105 - 标题
110 - 版权声明
115 - 来源
116 - 版权字符串
120 - 说明
121 - 本地说明
Jarek Milewski
13 年前
要删除jpeg文件中的所有EXIF、XMP等标签,不需要重新采样(顺便说一句,这可能会导致内存问题)。只需重新创建图像,最好使用100%的质量以避免任何损失。代码非常简单:

<?php
$img
= imagecreatefromjpeg ($path);
imagejpeg ($img, $path, 100);
imagedestroy ($img);
?>
To Top