Skip to content

Commit 16f3c7e

Browse files
committed
draft support retrieve Exif from heif file
Signed-off-by: benstone <[email protected]>
1 parent 3baa606 commit 16f3c7e

File tree

2 files changed

+138
-1
lines changed

2 files changed

+138
-1
lines changed

ext/exif/exif.c

Lines changed: 137 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1284,6 +1284,18 @@ typedef struct {
12841284
mn_offset_mode_t offset_mode;
12851285
} maker_note_type;
12861286

1287+
#define FOURCC(id) (((uint32_t)(id[0])<<24) | (id[1]<<16) | (id[2]<<8) | (id[3]))
1288+
1289+
typedef struct {
1290+
uint64_t size;
1291+
uint32_t type;
1292+
} isobmff_box_type;
1293+
1294+
typedef struct {
1295+
uint32_t offset;
1296+
uint32_t size;
1297+
} isobmff_item_pos_type;
1298+
12871299
/* Some maker notes (e.g. DJI info tag) require custom parsing */
12881300
#define REQUIRES_CUSTOM_PARSING NULL
12891301

@@ -4285,11 +4297,125 @@ static bool exif_process_IFD_in_TIFF(image_info_type *ImageInfo, size_t dir_offs
42854297
return result;
42864298
}
42874299

4300+
static int exif_isobmff_parse_box(unsigned char *buf, isobmff_box_type *box)
4301+
{
4302+
box->size = php_ifd_get32u(buf, 1);
4303+
buf += 4;
4304+
box->type = php_ifd_get32u(buf, 1);
4305+
if (box->size != 1) {
4306+
return 8;
4307+
}
4308+
buf += 4;
4309+
box->size = php_ifd_get64u(buf, 1);
4310+
return 16;
4311+
}
4312+
4313+
static void exif_isobmff_parse_meta(unsigned char *data, unsigned char *end, isobmff_item_pos_type *pos)
4314+
{
4315+
isobmff_box_type box, item;
4316+
unsigned char *box_offset, *p, *p2;
4317+
int header_size, exif_id = -1, version, item_count, i;
4318+
4319+
for (box_offset = data + 4; box_offset < end; box_offset += box.size) {
4320+
header_size = exif_isobmff_parse_box(box_offset, &box);
4321+
if (box.type == FOURCC("iinf")) {
4322+
p = box_offset + header_size;
4323+
version = p[0];
4324+
p += 4;
4325+
if (version < 2) {
4326+
item_count = php_ifd_get16u(p, 1);
4327+
p += 2;
4328+
} else {
4329+
item_count = php_ifd_get32u(p, 1);
4330+
p += 4;
4331+
}
4332+
for (i=0; i<item_count; i++) {
4333+
header_size = exif_isobmff_parse_box(p, &item);
4334+
if (!memcmp(p + header_size + 8, "Exif", 4)) {
4335+
exif_id = php_ifd_get16u(p + header_size + 4, 1);
4336+
break;
4337+
}
4338+
p += item.size;
4339+
}
4340+
if (exif_id < 0) {
4341+
break;
4342+
}
4343+
}
4344+
else if (box.type == FOURCC("iloc")) {
4345+
p = box_offset + header_size;
4346+
version = p[0];
4347+
p += 6;
4348+
if (version < 2) {
4349+
item_count = php_ifd_get16u(p, 1);
4350+
p += 2;
4351+
} else {
4352+
item_count = php_ifd_get32u(p, 1);
4353+
p += 4;
4354+
}
4355+
for (i=0, p2=p; i<item_count; i++, p2 += 16) {
4356+
fflush(stdout);
4357+
if (php_ifd_get16u(p2, 1) == exif_id) {
4358+
pos->offset = php_ifd_get32u(p2 + 8, 1);
4359+
pos->size = php_ifd_get32u(p2 + 12, 1);
4360+
break;
4361+
}
4362+
}
4363+
break;
4364+
}
4365+
}
4366+
}
4367+
4368+
static bool exif_scan_HEIF_header(image_info_type *ImageInfo, unsigned char *buf)
4369+
{
4370+
isobmff_box_type box;
4371+
isobmff_item_pos_type pos;
4372+
unsigned char *data;
4373+
off_t offset;
4374+
uint64_t limit;
4375+
int box_header_size, remain;
4376+
bool ret = false;
4377+
4378+
pos.size = 0;
4379+
for (offset = php_ifd_get32u(buf, 1); ImageInfo->FileSize > offset + 16; offset += box.size) {
4380+
if ((php_stream_seek(ImageInfo->infile, offset, SEEK_SET) < 0) ||
4381+
(exif_read_from_stream_file_looped(ImageInfo->infile, (char*)buf, 16) != 16)) {
4382+
break;
4383+
}
4384+
box_header_size = exif_isobmff_parse_box(buf, &box);
4385+
if (box.type == FOURCC("meta")) {
4386+
limit = box.size - box_header_size;
4387+
data = (unsigned char *)safe_emalloc(1, limit, 0);
4388+
remain = 16 - box_header_size;
4389+
if (remain) {
4390+
memcpy(data, buf + box_header_size, remain);
4391+
}
4392+
if (exif_read_from_stream_file_looped(ImageInfo->infile, data + remain, limit - remain) == limit - remain) {
4393+
exif_isobmff_parse_meta(data, data + limit, &pos);
4394+
}
4395+
efree(data);
4396+
if ((pos.size) &&
4397+
(ImageInfo->FileSize >= pos.offset + pos.size) &&
4398+
(php_stream_seek(ImageInfo->infile, pos.offset + 2, SEEK_SET) >= 0)) {
4399+
limit = pos.size - 2;
4400+
data = (unsigned char *)safe_emalloc(1, limit, 0);
4401+
if (exif_read_from_stream_file_looped(ImageInfo->infile, data, limit) == limit) {
4402+
exif_process_APP1(ImageInfo, data, limit, pos.offset + 2);
4403+
ret = true;
4404+
}
4405+
efree(data);
4406+
}
4407+
break;
4408+
}
4409+
}
4410+
4411+
return ret;
4412+
}
4413+
42884414
/* {{{ exif_scan_FILE_header
42894415
* Parse the marker stream until SOS or EOI is seen; */
42904416
static bool exif_scan_FILE_header(image_info_type *ImageInfo)
42914417
{
4292-
unsigned char file_header[8];
4418+
unsigned char file_header[16];
42934419
bool ret = false;
42944420

42954421
ImageInfo->FileType = IMAGE_FILETYPE_UNKNOWN;
@@ -4338,6 +4464,16 @@ static bool exif_scan_FILE_header(image_info_type *ImageInfo)
43384464
} else {
43394465
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Invalid TIFF file");
43404466
}
4467+
} else if ((ImageInfo->FileSize > 12) &&
4468+
(!memcmp(file_header + 4, "ftyp", 4)) &&
4469+
(exif_read_from_stream_file_looped(ImageInfo->infile, (char*)(file_header + 8), 4) == 4) &&
4470+
((!memcmp(file_header + 8, "heic", 4)) || (!memcmp(file_header + 8, "heix", 4)) || (!memcmp(file_header + 8, "mif1", 4)))) {
4471+
if (exif_scan_HEIF_header(ImageInfo, file_header)) {
4472+
ImageInfo->FileType = IMAGE_FILETYPE_HEIF;
4473+
ret = true;
4474+
} else {
4475+
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "Invalid HEIF file");
4476+
}
43414477
} else {
43424478
exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_WARNING, "File not supported");
43434479
return false;

ext/standard/php_image.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ typedef enum
4444
IMAGE_FILETYPE_ICO,
4545
IMAGE_FILETYPE_WEBP,
4646
IMAGE_FILETYPE_AVIF,
47+
IMAGE_FILETYPE_HEIF,
4748
/* WHEN EXTENDING: PLEASE ALSO REGISTER IN basic_function.stub.php */
4849
IMAGE_FILETYPE_COUNT
4950
} image_filetype;

0 commit comments

Comments
 (0)