PHP Conference Japan 2024

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

後台處理旗標。如果後台處理旗標小於 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' => 'Copyright 2008-2009, The PHP Group'
);

// 將 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 則註記

13
ebashkoff at gmail dot com
10 年前
以下程式碼會從來源檔案嵌入 IPTC APP 段 13 和 EXIF APP 段 1 資料,並將其嵌入到目的檔案中。這克服了 iptcembed 陳述式似乎不嵌入 EXIF 資料,只嵌入 IPTC 資料的限制。

function transferIptcExif2File($srcfile, $destfile) {
// 函式從 $srcfile 傳輸 EXIF (APP1) 和 IPTC (APP13),並將其新增至 $destfile
// JPEG 檔案格式為 0xFFD8 + [APP0] + [APP1] + ... [APP15] + <影像資料>,其中 [APPi] 為選用
// 段落 APPi(其中 i=0x0 至 0xF)的格式為 0xFFEi + 0xMM + 0xLL + <資料>(其中 0xMM 是
// (strlen(<資料>) + 2) 的最高有效 8 位元,而 0xLL 是最低有效 8 位元
// (strlen(<資料>) + 2)

if (file_exists($srcfile) && file_exists($destfile)) {
$srcsize = @getimagesize($srcfile, $imageinfo);
// 從來源檔案準備 EXIF 資料位元組
$exifdata = (is_array($imageinfo) && key_exists("APP1", $imageinfo)) ? $imageinfo['APP1'] : null;
if ($exifdata) {
$exiflength = strlen($exifdata) + 2;
if ($exiflength > 0xFFFF) return 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;
if ($iptcdata) {
$iptclength = strlen($iptcdata) + 2;
if ($iptclength > 0xFFFF) return false;
// 建構 IPTC 段落
$iptcdata = chr(0xFF) . chr(0xED) . chr(($iptclength >> 8) & 0xFF) . chr($iptclength & 0xFF) . $iptcdata;
}
$destfilecontent = @file_get_contents($destfile);
if (!$destfilecontent) return false;
if (strlen($destfilecontent) > 0) {
$destfilecontent = substr($destfilecontent, 2);
$portiontoadd = chr(0xFF) . chr(0xD8); // 變數會累積新的和原始的 IPTC 應用程式段落
$exifadded = !$exifdata;
$iptcadded = !$iptcdata;

while ((substr($destfilecontent, 0, 2) & 0xFFF0) === 0xFFE0) {
$segmentlen = (substr($destfilecontent, 2, 2) & 0xFFFF);
$iptcsegmentnumber = (substr($destfilecontent, 1, 1) & 0x0F); // 第二個位元組的最後 4 位元是 IPTC 段落 #
if ($segmentlen <= 2) return false;
$thisexistingsegment = substr($destfilecontent, 0, $segmentlen + 2);
if ((1 <= $iptcsegmentnumber) && (!$exifadded)) {
$portiontoadd .= $exifdata;
$exifadded = true;
if (1 === $iptcsegmentnumber) $thisexistingsegment = '';
}
if ((13 <= $iptcsegmentnumber) && (!$iptcadded)) {
$portiontoadd .= $iptcdata;
$iptcadded = true;
if (13 === $iptcsegmentnumber) $thisexistingsegment = '';
}
$portiontoadd .= $thisexistingsegment;
$destfilecontent = substr($destfilecontent, $segmentlen + 2);
}
if (!$exifadded) $portiontoadd .= $exifdata; // 如果尚未加入 EXIF 資料則加入
if (!$iptcadded) $portiontoadd .= $iptcdata; // 如果尚未加入 IPTC 資料則加入
$outputfile = fopen($destfile, 'w');
if ($outputfile) return fwrite($outputfile, $portiontoadd . $destfilecontent); else return false;
} else {
return false;
}
} else {
return false;
}
}
10
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');

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


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

function dump() {
print_r($this->meta);
}
function binary() {
$iptc_new = '';
foreach (array_keys($this->meta) as $s) {
$tag = str_replace("2#", "", $s);
$iptc_new .= $this->iptc_maketag(2, $tag, $this->meta[$s][0]);
}
return $iptc_new;
}
function iptc_maketag($rec,$dat,$val) {
$len = strlen($val);
if ($len < 0x8000) {
return chr(0x1c).chr($rec).chr($dat).
chr($len >> 8).
chr($len & 0xff).
$val;
} else {
return 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;

}
}
function write() {
if(!function_exists('iptcembed')) return 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 函式庫
function 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,"這裡是新的資料");
$i->write();

注意 1:資料可以是任何內容,甚至可以是二進位檔案。我目前測試過並將 MS-Excel 檔案直接嵌入到 JPEG 中,而且運作完美。

注意 2:寫入功能使用 GD 函式庫。

進一步的改進/變更可能會在 www.image-host-script.com 上發布

希望對您有所幫助。
阿里..
4
critto at o2 dot pl
14 年前
如果您只想複製 IPTC 資料,例如在建立縮圖時,您不必將二進位 IPTC 資料解析為陣列再轉回;只要這樣做即可

<?php
$fullFilePath
='photo1.jpg';
$fullPathThumb = 'photo1thumb.jpg';
$imagesize = getImageSize($fullFilePath, $info);
if(isset(
$info['APP13'])){
$content = iptcembed($info['APP13'], $fullPathThumb);
@
unlink($fullPathThumb);
$fw = fopen($fullPathThumb, 'w');
fwrite($fw, $content);
fclose($fw);
}
?>
2
evan at nospam dot ozhiker dot com
20 年前
您可能已經注意到,Photoshop 中的一些中繼資料欄位無法透過 IPTC 使用。
此外,Photoshop 現在使用 XMP 作為其主要中繼資料,這表示只有在 XMP 不存在時,Photoshop 才會讀取 IPTC。

我編寫了一個程式庫「PHP JPEG Metadata Toolkit」,它可以解決這個問題,因為它允許讀取、寫入和解釋幾乎任何類型的中繼資料,包括 XMP、IPTC 和 EXIF。

試用看看,並從這裡下載:
http://www.ozhiker.com/electronics/pjmt/index.html
2
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 欄位)
// @see 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);
}
?>
2
rupix at rediffmail dot com
22 年前
Windows 會區分「文字」和「二進位」檔案。因此,如果您在 Windows 平台上執行上述程式碼,它會產生損毀的圖片。若要克服這個問題,請將 fopen() 中的檔案模式設為 'wb' 而不是 'w'。

乾杯!

Rupinder
1
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;
}
?>
1
dj dot cyberdance at gmx dot at
20 年前
我花了一整天在除錯我的程式碼(該程式碼是基於 knut 發布的以下範例),直到我發現,只有當 jpeg_file_name 中指定的圖片已經包含 IPTC 欄位時,iptcembed() 才會起作用。

這表示,如果 JPEG 檔案中沒有預先存在的 IPTC 資訊,您就無法將 IPTC 欄位寫入該檔案。

更新 IPTC 欄位也只有在少數幾個檔案上有效,我真的不知道它是否有效取決於什麼。(好吧,它肯定取決於 IPTC 標頭 :-)

我使用的是 PHP 4.2.1,也許這個問題在較新的版本中已修復,但我不太相信...

儘管如此,這裡有一些我嘗試過的程式碼片段

我將以下這行

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

從 knut 在下面的範例中改成

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

根據 IPTC 規格,這只會建立一個空的「標頭」。當使用 iptcparse() 取得 IPTC 資訊時,也會包含這個標頭。因此,我的目的是建立一個全新的標頭,但是當之後使用 iptcembed() 時,新檔案的大小比原始檔案小一點,而且沒有儲存任何 IPTC 資訊。
1
pprem at pprem dot net
20 年前
我發現 ltrim 函數有問題:有時它會移除比應該移除的字元還多的字元,所以最好刪除以下這行

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

在 XMP_remove_from_jpeg 函數中
0
micheall at inthemdl dot net
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;
}
}
?>
0
jb at ibbeck dot de
17 年前
如果您的檔案沒有任何 IPTC 記錄,您可以使用空字串作為第一個引數呼叫 iptcembed 來新增一個新的空記錄,如下所示

$buffer = iptcembed("",$imagename,0);
$fp = fopen($imagename,"w");
fwrite($fp,$buffer);
fclose($fp);
0
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);
}
?>
0
muguran
19 年前
請注意不要新增值長度等於 0 的程式碼;行為可能很奇怪...
(例如,$iptc_old["2#015"][0] = "")
-2
dj dot cyberdance at gmx dot at
20 年前
是的,我又來了 :-) 在花了一些時間搜尋將 IPTC 欄位寫入 JPEG 檔案的其他可能性之後,我發現了這個

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

這個看起來運作得很好,請記住您必須安裝 PEAR 才能使其執行(Debian 套件 php4-pear)。
-2
pprem at pprem dot net
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 中的一部分。

願原力與我們同在 :-)
-2
knut dot satre dot NoSpam at No_Spam dot nord dot no
22 年前
以下範例示範如何從圖片讀取 IPTC 文字、變更文字並使用 iptcparse 和 iptcembed 函式寫入新檔案。

還有最常見的 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] = "運動";
// .. 並在原始標題中新增更多文字
$iptc_old["2#120"][0] .= " 更多標題文字";

// 製作新的 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)
return chr(0x1c).chr($rec).chr($dat).
chr($len >> 8).
chr($len & 0xff).
$val;
else
return 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 - 本地標題
-4
Jarek Milewski
13 年前
若要從 jpeg 檔案中移除所有 EXIF、XMP 等標籤,您不需要重新取樣 (順道一提,重新取樣可能會造成記憶體問題)。 重新建立影像就夠了,假設使用 100% 品質,才不會遺失任何東西。程式碼就這麼簡單

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