return ($io, @extras);
}
+sub _test_format {
+ my ($io) = @_;
+
+ return i_test_format_probe($io, -1);
+}
+
+sub add_file_magic {
+ my ($class, %opts) = @_;
+
+ my $name = delete $opts{name};
+ my $bits = delete $opts{bits};
+ my $mask = delete $opts{mask};
+
+ unless (i_add_file_magic($name, $bits, $mask)) {
+ Imager->_set_error(Imager->_error_as_msg);
+ return;
+ }
+
+ 1;
+}
+
# Read an image from file
sub read {
my $type = $input{'type'};
unless ($type) {
- $type = i_test_format_probe($IO, -1);
+ $type = _test_format($IO);
}
if ($input{file} && !$type) {
my $type = $opts{'type'};
unless ($type) {
- $type = i_test_format_probe($IO, -1);
+ $type = _test_format($IO);
}
if ($opts{file} && !$type) {
addcolors() - L<Imager::ImageTypes/addcolors()> - add colors to a
paletted image
+add_file_magic() -
+
addtag() - L<Imager::ImageTypes/addtag()> - add image tags
-add_type_extensions() -
+add_type_extensions() - L<Imager::Files/add_file_magic()> - add magic
+for new image file types.
+
L<Imager::Files/add_type_extensions($type, $ext, ...)> - add extensions for
new image file types.
Imager::IO ig
int length
+int
+i_add_file_magic(name, bits_sv, mask_sv)
+ const char *name
+ SV *bits_sv
+ SV *mask_sv
+ PREINIT:
+ const unsigned char *bits;
+ const unsigned char *mask;
+ size_t bits_size;
+ size_t mask_size;
+ CODE:
+ i_clear_error();
+ bits = SvPV(bits_sv, bits_size);
+ mask = SvPV(mask_sv, mask_size);
+ if (bits_size == 0) {
+ i_push_error(0, "bits must be non-empty");
+ XSRETURN_EMPTY;
+ }
+ if (mask_size == 0) {
+ i_push_error(0, "mask must be non-empty");
+ XSRETURN_EMPTY;
+ }
+ if (bits_size != mask_size) {
+ i_push_error(0, "bits and mask must be the same length");
+ XSRETURN_EMPTY;
+ }
+ if (!*name) {
+ i_push_error(0, "name must be non-empty");
+ XSRETURN_EMPTY;
+ }
+ RETVAL = i_add_file_magic(name, bits, mask, bits_size);
+ OUTPUT:
+ RETVAL
+
Imager::ImgRaw
i_readpnm_wiol(ig, allow_incomplete)
Imager::IO ig
return NULL;
}
+ ctx->file_magic = NULL;
+
ctx->refcount = 1;
#ifdef IMAGER_TRACE_CONTEXT
if (ctx->error_stack[i].msg)
myfree(ctx->error_stack[i].msg);
}
+
+ {
+ im_file_magic *p = ctx->file_magic;
+ while (p != NULL) {
+ im_file_magic *n = p->next;
+ free(p->m.name);
+ free(p->m.magic);
+ free(p->m.mask);
+ free(p);
+ p = n;
+ }
+ }
#ifdef IMAGER_LOG
if (ctx->lg_file && ctx->own_log)
fclose(ctx->lg_file);
nctx->refcount = 1;
+ {
+ im_file_magic *inp = ctx->file_magic;
+ im_file_magic **outpp = &nctx->file_magic;
+ *outpp = NULL;
+ while (inp) {
+ im_file_magic *m = malloc(sizeof(im_file_magic));
+ if (!m) {
+ /* should free any parts of the list already allocated */
+ im_context_refdec(nctx, "failed cloning");
+ return NULL;
+ }
+ m->next = NULL;
+ m->m.name = strdup(inp->m.name);
+ m->m.magic_size = inp->m.magic_size;
+ m->m.magic = malloc(inp->m.magic_size);
+ m->m.mask = malloc(inp->m.magic_size);
+ if (m->m.name == NULL || m->m.magic == NULL || m->m.mask == NULL) {
+ free(m->m.name);
+ free(m->m.magic);
+ free(m->m.mask);
+ free(m);
+ im_context_refdec(nctx, "failed cloning");
+ return NULL;
+ }
+ memcpy(m->m.magic, inp->m.magic, m->m.magic_size);
+ memcpy(m->m.mask, inp->m.mask, m->m.magic_size);
+ *outpp = m;
+ outpp = &m->next;
+ inp = inp->next;
+ }
+ }
+
#ifdef IMAGER_TRACE_CONTEXT
fprintf(stderr, "im_context:%s: cloned %p to %p\n", where, ctx, nctx);
#endif
return ctx->slots[slot];
}
+
+/*
+=item im_add_file_magic(ctx, name, bits, mask, length)
+
+Add file type magic to the given context.
+
+=cut
+*/
+
+int
+im_add_file_magic(im_context_t ctx, const char *name,
+ const unsigned char *bits, const unsigned char *mask,
+ size_t length) {
+ im_file_magic *m = malloc(sizeof(im_file_magic));
+
+ if (m == NULL)
+ return 0;
+
+ if (length > 512)
+ length = 512;
+
+ m->m.name = strdup(name);
+ m->m.magic = malloc(length);
+ m->m.mask = malloc(length);
+ m->m.magic_size = length;
+
+ if (name == NULL || bits == NULL || mask == NULL) {
+ free(m->m.name);
+ free(m->m.magic);
+ free(m->m.mask);
+ free(m);
+ return 0;
+ }
+ memcpy(m->m.magic, bits, length);
+ memcpy(m->m.mask, mask, length);
+ m->next = ctx->file_magic;
+ ctx->file_magic = m;
+
+ return 1;
+}
}
}
-struct magic_entry {
- unsigned char *magic;
- size_t magic_size;
- char *name;
- unsigned char *mask;
-};
-
static int
-test_magic(unsigned char *buffer, size_t length, struct magic_entry const *magic) {
+test_magic(unsigned char *buffer, size_t length, struct file_magic_entry const *magic) {
if (length < magic->magic_size)
return 0;
if (magic->mask) {
{ (unsigned char *)(magic ""), sizeof(magic)-1, type, (unsigned char *)(mask) }
const char *
-i_test_format_probe(io_glue *data, int length) {
- static const struct magic_entry formats[] = {
+im_test_format_probe(im_context_t ctx, io_glue *data, int length) {
+ static const struct file_magic_entry formats[] = {
FORMAT_ENTRY("\xFF\xD8", "jpeg"),
FORMAT_ENTRY("GIF87a", "gif"),
FORMAT_ENTRY("GIF89a", "gif"),
/* FLIF - Free Lossless Image Format - https://flif.info/spec.html */
FORMAT_ENTRY("FLIF", "flif")
};
- static const struct magic_entry more_formats[] = {
+ static const struct file_magic_entry more_formats[] = {
/* these were originally both listed as ico, but cur files can
include hotspot information */
FORMAT_ENTRY("\x00\x00\x01\x00", "ico"), /* Windows icon */
}
#endif
+ {
+ im_file_magic *p = ctx->file_magic;
+ while (p) {
+ if (test_magic(head, rc, &p->m)) {
+ return p->m.name;
+ }
+ p = p->next;
+ }
+ }
+
for(i=0; i<sizeof(formats)/sizeof(formats[0]); i++) {
- struct magic_entry const *entry = formats + i;
+ struct file_magic_entry const *entry = formats + i;
if (test_magic(head, rc, entry))
return entry->name;
return "tga";
for(i=0; i<sizeof(more_formats)/sizeof(more_formats[0]); i++) {
- struct magic_entry const *entry = more_formats + i;
+ struct file_magic_entry const *entry = more_formats + i;
if (test_magic(head, rc, entry))
return entry->name;
extern int i_get_file_background(i_img *im, i_color *bg);
extern int i_get_file_backgroundf(i_img *im, i_fcolor *bg);
-const char * i_test_format_probe(io_glue *data, int length);
-
+const char * im_test_format_probe(im_context_t ctx, io_glue *data, int length);
+#define i_test_format_probe(io, length) im_test_format_probe(aIMCTX, (io), (length))
+
+/* file type magic to extend file detection */
+extern int im_add_file_magic(im_context_t ctx, const char *name,
+ const unsigned char *bits, const unsigned char *mask,
+ size_t length);
+#define i_add_file_magic(name, bits, mask, length) \
+ im_add_file_magic(aIMCTX, (name), (bits), (mask), (length))
i_img * i_readraw_wiol(io_glue *ig, i_img_dim x, i_img_dim y, int datachannels, int storechannels, int intrl);
undef_int i_writeraw_wiol(i_img* im, io_glue *ig);
#define color_to_grey(col) ((col)->rgb.r * 0.222 + (col)->rgb.g * 0.707 + (col)->rgb.b * 0.071)
+struct file_magic_entry {
+ unsigned char *magic;
+ size_t magic_size;
+ char *name;
+ unsigned char *mask;
+};
+
+
+typedef struct im_file_magic im_file_magic;
+struct im_file_magic {
+ struct file_magic_entry m;
+
+ /* more magic to check */
+ im_file_magic *next;
+};
+
#define IM_ERROR_COUNT 20
typedef struct im_context_tag {
int error_sp;
size_t slot_alloc;
void **slots;
+ /* registered file type magic */
+ im_file_magic *file_magic;
+
ptrdiff_t refcount;
} im_context_struct;
my $img = Imager->new(file => $filename)
or die Imager->errstr;
+ Imager->add_file_magic(name => $name, bits => $bits, mask => $mask);
+
=head1 DESCRIPTION
You can read and write a variety of images formats, assuming you have
=back
+You can now add to the magic database Imager uses for detecting file
+types:
+
+=over
+
+=item add_file_magic()
+
+ Imager->add_file_magic(name => $name, bits => $bits, mask => $mask)
+
+Adds to list of magic, the parameters are all required. The
+parameters are:
+
+=over
+
+=item *
+
+C<name> - the file type name to return on match.
+
+=item *
+
+C<bits> - a binary string to match.
+
+=item *
+
+C<mask> - a mask controlling which parts of I<bits> are significant.
+
+=back
+
+While I<mask> is mostly a bit mask, some byte values are translated,
+the space character is treated as all zeros (C<"\x00">), and the C<x> character as
+all ones (C<"\xFF">).
+
+New magic entries take priority over old entries.
+
+You can add more than one magic entry for a given I<name>.
+
+ Imager->add_file_magic(name => "heif",
+ bits => " ftypheif"
+ mask => " xxxxxxxx");
+
+=back
+
=head2 Limiting the sizes of images you read
=over
CF 3A FF 56 5D FF 67 87 CE 9C 0E D6 69 CD 1F EF
FLIF
+ok(Imager->add_file_magic(name => 'testtype',
+ bits => " testtype",
+ mask => " xxxxxxxx"),
+ "add magic");
+
+probe_ok(<<TESTTYPE, "testtype", "Test adding a format with magic");
+46 4C 49 46 74 65 73 74 74 79 70 65 03 AF B0 B1
+E8 03 37 FF F7 D5 C2 D8 B7 D5 58 59 6E D9 71 8C
+0F A9 88 B4 1C B1 7F C0 2E FB 8C 7D 90 B6 04 DF
+CF 3A FF 56 5D FF 67 87 CE 9C 0E D6 69 CD 1F EF
+TESTTYPE
+
{ # RT 72475
# check error messages from read/read_multi
my $data = "nothing useful";
my $data = pack("H*", $packed);
my $io = Imager::io_new_buffer($data);
- my $result = Imager::i_test_format_probe($io, -1);
+ my $result = Imager::_test_format($io);
return $builder->is_eq($result, $exp_type, $name)
}