From 1fefb5f65d5055c1d3c1b3aeb9b5ec9a83fb41e5 Mon Sep 17 00:00:00 2001 From: TSenter Date: Sun, 10 May 2020 00:06:11 -0400 Subject: [PATCH 01/23] Adding all files in move to Ubuntu --- CHANGELOG.md | 2 + formats/png.c | 38 ++++++++++- main.c | 44 +++++++++--- meta.h | 9 +++ utils/iv_opts.h | 4 ++ utils/logging.c | 0 utils/logging.h | 0 utils/types.c | 175 ++++++++++++++++++++++++++++++++++++++++++++++++ utils/types.h | 90 +++++++++++++++++++++++++ 9 files changed, 350 insertions(+), 12 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 meta.h create mode 100644 utils/iv_opts.h create mode 100644 utils/logging.c create mode 100644 utils/logging.h create mode 100644 utils/types.c create mode 100644 utils/types.h diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..0988204 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,2 @@ +## v0.0.0 +* No features have been implemented yet diff --git a/formats/png.c b/formats/png.c index dd96fb9..7beeda8 100644 --- a/formats/png.c +++ b/formats/png.c @@ -4,6 +4,7 @@ #include "png.h" #include "../utils/version.h" +#include "../utils/iv_opts.h" Format_PNG png_new() { Format_PNG png = (Format_PNG) malloc(sizeof(struct format_png_t)); @@ -21,7 +22,7 @@ Format_PNG png_new() { * > 0 - file is not of type PNG */ int png_open(Format_PNG png, char *filename) { - char buf[512]; + char buf[PNG_SIG_SZ]; int rv; if (filename == NULL) { png->name = NULL; @@ -39,11 +40,19 @@ int png_open(Format_PNG png, char *filename) { /* Attempt to read the signature */ rv = fread(buf, sizeof(char), PNG_SIG_SZ, png->fin); + if (iv_opts.verbose) { + printf("Reading signature... "); + } else if (iv_opts.scan) { + printf("[PNG] Read signature\n"); + } if (rv != PNG_SIG_SZ) return 1; /* Validate the signature */ if ((*(long long*) (&buf)) - SIG_PNG != 0) return 1; + if (iv_opts.verbose) { + printf("matched.\n"); + } png->IHDR = png_chunk_next(png); @@ -71,17 +80,32 @@ png_chunk_t *png_chunk_next(Format_PNG png) { /* Read chunk length */ rv = fread(&chunk->length, PNG_CHNK_LEN, 1, png->fin); + if (feof(png->fin)) { + free(chunk); + return NULL; + } if (rv != 1) { + if (iv_opts.verbose) { + printf("Chunk: Error (chunk length)\n"); + } free(chunk); return NULL; } + if (iv_opts.verbose) { + printf("Chunk: "); + fflush(stdout); + } + /* Normalize length */ chunk->length = ntohl(chunk->length); /* Read chunk type */ rv = fread(chunk->type, 1, PNG_CHNK_TYPE_SZ, png->fin); if (rv != PNG_CHNK_TYPE_SZ) { + if (iv_opts.verbose) { + printf("Error (chunk type)\n"); + } fprintf(stderr, "[%s/png]: Unexpected EOF in reading chunk type, read %d/%d chunk type bytes\n", IV_PROGRAM_NAME, rv, PNG_CHNK_TYPE_SZ); free(chunk); return NULL; @@ -94,6 +118,9 @@ png_chunk_t *png_chunk_next(Format_PNG png) { if (chunk->length != 0) { rv = fread(chunk->data, 1, chunk->length, png->fin); if (rv != chunk->length) { + if (iv_opts.verbose) { + printf("Error (chunk data)\n"); + } fprintf(stderr, "[%s/png]: Unexpected EOF in reading chunk '%.4s', read %d/%d chunk data bytes\n", IV_PROGRAM_NAME, chunk->type, rv, chunk->length); free(chunk->data); free(chunk); @@ -104,12 +131,19 @@ png_chunk_t *png_chunk_next(Format_PNG png) { /* Read CRC */ rv = fread(&chunk->CRC, sizeof(int), 1, png->fin); if (rv != 1) { - fprintf(stderr, "[img"); + if (iv_opts.verbose) { + printf("Error (chunk CRC)\n"); + } + fprintf(stderr, "[%s/png]: Unexpected EOF in reading CRC for chunk '%.4s', read %d/%d bytes\n", IV_PROGRAM_NAME, chunk->type, rv, 4); free(chunk->data); free(chunk); return NULL; } + if (iv_opts.verbose) { + printf("%.4s (%d byte%s)\n", chunk->type, chunk->length, chunk->length != 1 ? "s" : ""); + } + return chunk; } diff --git a/main.c b/main.c index e15c053..fb300c5 100644 --- a/main.c +++ b/main.c @@ -3,7 +3,9 @@ #include #include #include +#include +#include "utils/iv_opts.h" #include "utils/types.h" #include "utils/version.h" #include "formats/png.h" @@ -47,13 +49,17 @@ int main(int argc, char **argv) { { "version", no_argument, NULL, 'v' }, { "help", optional_argument, NULL, 'h' }, { "verbose", optional_argument, NULL, 'V' }, + { "scan", no_argument, NULL, 's' }, {NULL, 0, NULL, 0}, /* Last element must be all 0s */ }; + /* Default options */ + iv_opts.verbose = 0; + int option_index; int c; while (1) { - c = getopt_long(argc, argv, "vh::", options, &option_index); + c = getopt_long(argc, argv, "vh::V::s", options, &option_index); if (c == -1) break; @@ -67,7 +73,12 @@ int main(int argc, char **argv) { break; case 'V': - + iv_opts.verbose = 1; + printf(" ===== %s [%s] =====\n", IV_PROGRAM_NAME, IV_VERSION); + break; + + case 's': + iv_opts.scan = 1; break; case '?': @@ -80,12 +91,24 @@ int main(int argc, char **argv) { } if (optind == argc) { + if (iv_opts.verbose) { + printf("No input files detected. Checking stdin..."); + } + // TODO Read from stdin - int c = fgetc(stdin); - if (feof(stdin)) { - usage(EXIT_FAILURE); + struct pollfd ps = { 0, POLLIN, 0 }; + + int rv = poll(&ps, 1, 0); + if (rv == 0) { + if (iv_opts.verbose) { + printf(" nothing in stdin.\n"); + printf("Exiting...\n"); + exit(1); + } else { + usage(EXIT_FAILURE); + } } else { - fputc(c, stdin); + printf(" input located!"); Format_PNG png = png_new(); png->fin = stdin; handle_png(png); @@ -100,17 +123,18 @@ int main(int argc, char **argv) { printf("File does not exist: %s\n", argv[i]); continue; } - printf("Opening %s... ", argv[i]); rv = png_open(png, argv[i]); if (rv != 0) { - printf("error (%d)\n", rv); png_close(png); continue; } - printf("success!\n"); - handle_png(png); + while (png_chunk_next(png) != NULL); + + // handle_png(png); png_close(png); } + + exit(EXIT_SUCCESS); } \ No newline at end of file diff --git a/meta.h b/meta.h new file mode 100644 index 0000000..e053c69 --- /dev/null +++ b/meta.h @@ -0,0 +1,9 @@ +#ifndef __IV_META_H +#define __IV_META_H +#include "utils/types.h" + +typedef struct meta { + char key[80]; + IVValue val; +} meta_t; +#endif \ No newline at end of file diff --git a/utils/iv_opts.h b/utils/iv_opts.h new file mode 100644 index 0000000..12cdbc1 --- /dev/null +++ b/utils/iv_opts.h @@ -0,0 +1,4 @@ +struct global_opts { + int verbose; + int scan; +} iv_opts; \ No newline at end of file diff --git a/utils/logging.c b/utils/logging.c new file mode 100644 index 0000000..e69de29 diff --git a/utils/logging.h b/utils/logging.h new file mode 100644 index 0000000..e69de29 diff --git a/utils/types.c b/utils/types.c new file mode 100644 index 0000000..bf49720 --- /dev/null +++ b/utils/types.c @@ -0,0 +1,175 @@ +#include +#include "types.h" + +IVValue new_iv() { + return (IVValue) malloc(sizeof(union ivvalue_t)); +} + +IVValue new_iv_str(char *str) { + IVValue iv = new_iv(); + iv->str = str; + return iv; +} + +IVValue new_iv_v(void *v) { + IVValue iv = new_iv(); + iv->v = v; + return iv; +} + +IVValue new_iv_c(char c) { + IVValue iv = new_iv(); + iv->c = c; + return iv; +} + +IVValue new_iv_ca(char ca[8]) { + IVValue iv = new_iv(); + int i; + for (i = 0; i < 8; i++) + iv->ca[i] = ca[i]; + return iv; +} + +IVValue new_iv_uc(unsigned char uc) { + IVValue iv = new_iv(); + iv->uc = uc; + return iv; +} + +IVValue new_iv_uca(unsigned char uca[8]) { + IVValue iv = new_iv(); + int i; + for (i = 0; i < 8; i++) + iv->uca[i] = uca[i]; + return iv; +} + +IVValue new_iv_s(short s) { + IVValue iv = new_iv(); + iv->s = s; + return iv; +} + +IVValue new_iv_sa(short sa[4]) { + IVValue iv = new_iv(); + int i; + for (i = 0; i < 4; i++) + iv->sa[i] = sa[i]; + return iv; +} + +IVValue new_iv_us(unsigned short us) { + IVValue iv = new_iv(); + iv->us = us; + return iv; +} + +IVValue new_iv_usa(unsigned short usa[4]) { + IVValue iv = new_iv(); + int i; + for (i = 0; i < 4; i++) + iv->usa[i] = usa[i]; + return iv; +} + +IVValue new_iv_i(int i) { + IVValue iv = new_iv(); + iv->i = i; + return iv; +} + +IVValue new_iv_ia(int ia[2]) { + IVValue iv = new_iv(); + iv->ia[0] = ia[0]; + iv->ia[1] = ia[1]; + return iv; +} + +IVValue new_iv_ui(unsigned int ui) { + IVValue iv = new_iv(); + iv->ui = ui; + return iv; +} + +IVValue new_iv_uia(unsigned int uia[2]) { + IVValue iv = new_iv(); + iv->uia[0] = uia[0]; + iv->uia[1] = uia[1]; + return iv; +} + +IVValue new_iv_l(long long l) { + IVValue iv = new_iv(); + iv->l = l; + return iv; +} + +IVValue new_iv_la(long long *la) { + IVValue iv = new_iv(); + iv->la = la; + return iv; +} + +IVValue new_iv_ul(unsigned long long ul) { + IVValue iv = new_iv(); + iv->ul = ul; + return iv; +} + +IVValue new_iv_ula(unsigned long long *ula) { + IVValue iv = new_iv(); + iv->ula = ula; + return iv; +} + +IVValue new_iv_f(float f) { + IVValue iv = new_iv(); + iv->f = f; + return iv; +} + +IVValue new_iv_fa(float fa[2]) { + IVValue iv = new_iv(); + iv->fa[0] = fa[0]; + iv->fa[1] = fa[1]; + return iv; +} + +IVValue new_iv_d(double d) { + IVValue iv = new_iv(); + iv->d = d; + return iv; +} + +IVValue new_iv_da(double *da) { + IVValue iv = new_iv(); + iv->da = da; + return iv; +} + +char *iv_str(IVValue iv) { + return iv->str; +} + +void *iv_v(IVValue iv) { return iv->v; } +char iv_c(IVValue iv) { return iv->c; } +char *iv_ca(IVValue iv) { return iv->ca; } +unsigned char iv_uc(IVValue iv) { return iv->uc; } +unsigned char *iv_uca(IVValue iv) { return iv->uca; } +short iv_s(IVValue iv) { return iv->s; } +short *iv_sa(IVValue iv) { return iv->sa; } +unsigned short iv_us(IVValue iv) { return iv->us; } +unsigned short *iv_usa(IVValue iv) { return iv->usa; } +int iv_i(IVValue iv) { return iv->i; } +int *iv_ia(IVValue iv) { return iv->ia; } +unsigned int iv_ui(IVValue iv) { return iv->ui; } +unsigned int *iv_uia(IVValue iv) { return iv->uia; } +long long iv_l(IVValue iv) { return iv->l; } +long long *iv_la(IVValue iv) { return iv->la; } +unsigned long long iv_ul(IVValue iv) { return iv->ul; } +unsigned long long *iv_ula(IVValue iv) { return iv->ula; } +float iv_f(IVValue iv) { return iv->f; } +float *iv_fa(IVValue iv) { return iv->fa; } +double iv_d(IVValue iv) { return iv->d; } +double *iv_da(IVValue iv) { return iv->da; } \ No newline at end of file diff --git a/utils/types.h b/utils/types.h new file mode 100644 index 0000000..f256808 --- /dev/null +++ b/utils/types.h @@ -0,0 +1,90 @@ +#ifndef __TYPES_IV +#define __TYPES_IV + +#define VT_STRING 1 +#define VT_CHAR 2 +#define VT_SHORT 3 +#define VT_INT 4 +#define VT_FLOAT 5 +#define VT_LONG 6 +#define VT_DOUBLE 7 +#define VT_VOID 8 + +typedef union ivvalue_t { + char *str; + void *v; + + char c; + char ca[8]; + unsigned char uc; + unsigned char uca[8]; + + short s; + short sa[4]; + unsigned short us; + unsigned short usa[4]; + + int i; + int ia[2]; + unsigned int ui; + unsigned int uia[2]; + + long long l; + long long *la; + unsigned long long ul; + unsigned long long *ula; + + float f; + float fa[2]; + + double d; + double *da; +} *IVValue; + +IVValue new_iv_str(char *); +IVValue new_iv_v(void *); +IVValue new_iv_c(char); +IVValue new_iv_ca(char [8]); +IVValue new_iv_uc(unsigned char); +IVValue new_iv_uca(unsigned char [8]); +IVValue new_iv_s(short); +IVValue new_iv_sa(short [4]); +IVValue new_iv_us(unsigned short); +IVValue new_iv_usa(unsigned short [4]); +IVValue new_iv_i(int); +IVValue new_iv_ia(int [2]); +IVValue new_iv_ui(unsigned int); +IVValue new_iv_uia(unsigned int [2]); +IVValue new_iv_l(long long); +IVValue new_iv_la(long long *); +IVValue new_iv_ul(unsigned long long); +IVValue new_iv_ula(unsigned long long *); +IVValue new_iv_f(float); +IVValue new_iv_fa(float [2]); +IVValue new_iv_d(double); +IVValue new_iv_da(double *); + +char *iv_str(IVValue); +void *iv_v(IVValue); +char iv_c(IVValue); +char *iv_ca(IVValue); +unsigned char iv_uc(IVValue); +unsigned char *iv_uca(IVValue); +short iv_s(IVValue); +short *iv_sa(IVValue); +unsigned short iv_us(IVValue); +unsigned short *iv_usa(IVValue); +int iv_i(IVValue); +int *iv_ia(IVValue); +unsigned int iv_ui(IVValue); +unsigned int *iv_uia(IVValue); +long long iv_l(IVValue); +long long *iv_la(IVValue); +unsigned long long iv_ul(IVValue); +unsigned long long *iv_ula(IVValue); +float iv_f(IVValue); +float *iv_fa(IVValue); +double iv_d(IVValue); +double *iv_da(IVValue); + +#endif \ No newline at end of file From f9c7b628058309103c0c0aba784d2a97c5bf09af Mon Sep 17 00:00:00 2001 From: Tyler Senter Date: Sun, 10 May 2020 04:38:26 -0400 Subject: [PATCH 02/23] WIP feat(PNG): Begin extracting iTXt meta --- formats/png.c | 46 +++++++++++++++++++++++++++++++++++++++++++++- formats/png.h | 4 ++++ main.c | 34 +++++++++++++++++++++------------- 3 files changed, 70 insertions(+), 14 deletions(-) diff --git a/formats/png.c b/formats/png.c index 7beeda8..3145fd5 100644 --- a/formats/png.c +++ b/formats/png.c @@ -1,6 +1,7 @@ #include #include #include +#include "arpa/inet.h" #include "png.h" #include "../utils/version.h" @@ -69,6 +70,7 @@ int png_open(Format_PNG png, char *filename) { void png_close(Format_PNG png) { if (png->name) free(png->name); if (png->fin) fclose(png->fin); + if (png->IHDR) png_chunk_free(png->IHDR); free(png); } @@ -126,7 +128,10 @@ png_chunk_t *png_chunk_next(Format_PNG png) { free(chunk); return NULL; } - } else chunk->data = NULL; + } else { + free(chunk->data); + chunk->data = NULL; + } /* Read CRC */ rv = fread(&chunk->CRC, sizeof(int), 1, png->fin); @@ -147,6 +152,45 @@ png_chunk_t *png_chunk_next(Format_PNG png) { return chunk; } +void png_chunk_free(png_chunk_t *chunk) { + if (chunk->data) free(chunk->data); + + free(chunk); +} + +/* + * Extract the keyword from an iTXt chunk + */ +char *png_iTXt_keyword(png_chunk_t *chunk) { + if (strncmp(chunk->type, "iTXt", 4) != 0) return NULL; + + return (char *) chunk->data; +} + +/* + * Extract the text value from an iTXt chunk + */ +char *png_iTXt_text(png_chunk_t *chunk) { + int c = strlen(chunk->data) + 3; /* Keyword + comp. flag + comp. method */ + char *p = chunk->data + c; + char *buf; + + c += strlen(p) + 1; /* Language tag */ + p = chunk->data + c; + + c += strlen(p) + 1; /* Translated keyword */ + p = chunk->data + c; + + c = chunk->length - c; + + buf = (char *) malloc(sizeof(char) * (c + 1)); + + strncpy(buf, p, c); + buf[c] = 0; + + return buf; +} + /* * Fetch the width of the given image */ diff --git a/formats/png.h b/formats/png.h index 90b076d..5650a46 100644 --- a/formats/png.h +++ b/formats/png.h @@ -38,6 +38,10 @@ void png_close(Format_PNG); /* PNG chunks */ png_chunk_t *png_chunk_next(Format_PNG); +void png_chunk_free(png_chunk_t *); + +char *png_iTXt_keyword(png_chunk_t *); +char *png_iTXt_text(png_chunk_t *); /* Image attributes */ int png_attr_w(Format_PNG); diff --git a/main.c b/main.c index fb300c5..e7a41fb 100644 --- a/main.c +++ b/main.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "utils/iv_opts.h" #include "utils/types.h" @@ -38,9 +39,19 @@ void help() { } void handle_png(Format_PNG png) { - int rv; + png_chunk_t *chunk; + + while (chunk = png_chunk_next(png)) { + if (strncmp(chunk->type, "iTXt", 4) == 0) { + char *text = png_iTXt_text(chunk); + printf(" %s: %s\n", png_iTXt_keyword(chunk), text); + free(text); + } - png_debug(png); + png_chunk_free(chunk); + } + + // png_debug(png); } int main(int argc, char **argv) { @@ -91,33 +102,32 @@ int main(int argc, char **argv) { } if (optind == argc) { - if (iv_opts.verbose) { - printf("No input files detected. Checking stdin..."); - } - // TODO Read from stdin struct pollfd ps = { 0, POLLIN, 0 }; int rv = poll(&ps, 1, 0); if (rv == 0) { if (iv_opts.verbose) { - printf(" nothing in stdin.\n"); - printf("Exiting...\n"); exit(1); } else { usage(EXIT_FAILURE); } } else { - printf(" input located!"); Format_PNG png = png_new(); - png->fin = stdin; + rv = png_open(png, NULL); + if (rv != 0) { + png_close(png); + exit(EXIT_FAILURE); + } handle_png(png); + png_close(png); } } // TODO Read files for (i = optind; i < argc; i++) { Format_PNG png = png_new(); + png_chunk_t *chunk; if (access(argv[i], R_OK) == -1) { // TODO Error message for bad file printf("File does not exist: %s\n", argv[i]); @@ -130,9 +140,7 @@ int main(int argc, char **argv) { continue; } - while (png_chunk_next(png) != NULL); - - // handle_png(png); + handle_png(png); png_close(png); } From 686bae14e2f2cd792209ba410074f04a728cef31 Mon Sep 17 00:00:00 2001 From: Tyler Senter Date: Sun, 10 May 2020 22:19:10 -0400 Subject: [PATCH 03/23] WIP feat(PNG): Add support for tIME and tEXt chunks (#4) --- formats/png.c | 125 ++++++++++++++++++++++++++++++++++++++++++++++++-- formats/png.h | 20 ++++++++ 2 files changed, 142 insertions(+), 3 deletions(-) diff --git a/formats/png.c b/formats/png.c index 3145fd5..e5eaa75 100644 --- a/formats/png.c +++ b/formats/png.c @@ -1,7 +1,7 @@ #include #include #include -#include "arpa/inet.h" +#include #include "png.h" #include "../utils/version.h" @@ -162,15 +162,62 @@ void png_chunk_free(png_chunk_t *chunk) { * Extract the keyword from an iTXt chunk */ char *png_iTXt_keyword(png_chunk_t *chunk) { - if (strncmp(chunk->type, "iTXt", 4) != 0) return NULL; + if (strncmp(chunk->type, PNG_CHUNK_TEXT_INT, PNG_CHNK_LEN) != 0) return NULL; - return (char *) chunk->data; + return strdup((char *) chunk->data); +} + +/* + * Extract the keyword from a tEXt chunk + * + * Returns: NULL - on error + * keyword on success; must be free'd + */ +char *png_tEXt_keyword(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_TEXT, PNG_CHNK_LEN) != 0) return NULL; + + return strdup(chunk->data); +} + +/* + * Extract the text from a tEXt chunk + * + * Returns: NULL - on error + * keyword on success; must be free'd + */ +char *png_tEXt_text(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_TEXT, PNG_CHNK_LEN) != 0) return NULL; + + int c = strlen(chunk->data) + 1; + char *text = (char *) malloc(chunk->length - c + 1); + + strncpy(text, chunk->data + c, chunk->length - c); + text[chunk->length - c] = 0; + + return text; +} + +/* + * Extract the language tag from an iTXt chunk + * + * Returns: NULL - on error + * "" - no language tag available; must be free'd + * string containing ISO 646 hyphen-separated words; must be free'd + */ +char *png_iTXt_lang(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_TEXT_INT, PNG_CHNK_LEN) != 0) return NULL; + + if (chunk->length <= strlen(chunk->data) + 3) return NULL; + + return strdup(chunk->data + strlen(chunk->data) + 3); } /* * Extract the text value from an iTXt chunk */ char *png_iTXt_text(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_TEXT_INT, PNG_CHNK_LEN) != 0) return NULL; + int c = strlen(chunk->data) + 3; /* Keyword + comp. flag + comp. method */ char *p = chunk->data + c; char *buf; @@ -191,6 +238,78 @@ char *png_iTXt_text(png_chunk_t *chunk) { return buf; } +/* + * Extract the year from the last modified date + * + * Returns: -1 on error + * Complete year (eg. 1995, not 95) on success + */ +short png_tIME_year(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_TIME, PNG_CHNK_LEN) != 0) return -1; + + return ntohs(*((short *) (chunk->data))); +} + +/* + * Extract the month from the last modified date + * + * Returns: -1 on error + * Month (1-12) on success + */ +char png_tIME_month(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_TIME, PNG_CHNK_LEN) != 0) return -1; + + return *((char *) (chunk->data + 2)); +} + +/* + * Extract the day from the last modified date + * + * Returns: -1 on error + * Day (1-31) on success + */ +char png_tIME_day(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_TIME, PNG_CHNK_LEN) != 0) return -1; + + return *((char *) (chunk->data + 3)); +} + +/* + * Extract the hour from the last modified date + * + * Returns: -1 on error + * Hour (0-23) on success + */ +char png_tIME_hour(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_TIME, PNG_CHNK_LEN) != 0) return -1; + + return *((char *) (chunk->data + 4)); +} + +/* + * Extract the minute from the last modified date + * + * Returns: -1 on error + * Minute (0-59) on success + */ +char png_tIME_minute(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_TIME, PNG_CHNK_LEN) != 0) return -1; + + return *((char *) (chunk->data + 5)); +} + +/* + * Extract the second from the last modified date + * + * Returns: -1 on error + * Minute (0-60 to allow for leap seconds) on success + */ +char png_tIME_second(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_TIME, PNG_CHNK_LEN) != 0) return -1; + + return *((char *) (chunk->data + 6)); +} + /* * Fetch the width of the given image */ diff --git a/formats/png.h b/formats/png.h index 5650a46..2663ad0 100644 --- a/formats/png.h +++ b/formats/png.h @@ -9,6 +9,15 @@ #define PNG_CHNK_TYPE_SZ 4 #define PNG_CHNK_IHDR 0x52444849 +#define PNG_CHUNK_HEADER "IHDR" +#define PNG_CHUNK_PALETTE "PLTE" +#define PNG_CHUNK_DATA "IDAT" +#define PNG_CHUNK_END "IEND" +#define PNG_CHUNK_TIME "tIME" +#define PNG_CHUNK_TEXT_INT "iTXt" +#define PNG_CHUNK_TEXT "tEXt" +#define PNG_CHUNK_TEXT_COMPRESSED "zTXt" + #define PNG_ISCRITICAL(chunk) (chunk->type[0] & 0x20 != 0) #define PNG_ISANCILLARY(chunk) (chunk->type[0] & 0x20 == 0) @@ -40,9 +49,20 @@ void png_close(Format_PNG); png_chunk_t *png_chunk_next(Format_PNG); void png_chunk_free(png_chunk_t *); +char *png_tEXt_keyword(png_chunk_t *); +char *png_tEXt_text(png_chunk_t *); + char *png_iTXt_keyword(png_chunk_t *); +char *png_iTXt_lang(png_chunk_t *); char *png_iTXt_text(png_chunk_t *); +short png_tIME_year(png_chunk_t *); +char png_tIME_month(png_chunk_t *); +char png_tIME_day(png_chunk_t *); +char png_tIME_hour(png_chunk_t *); +char png_tIME_minute(png_chunk_t *); +char png_tIME_second(png_chunk_t *); + /* Image attributes */ int png_attr_w(Format_PNG); int png_attr_h(Format_PNG); From a479737ed553c0531f3421152c0add1bb5e11f33 Mon Sep 17 00:00:00 2001 From: Tyler Senter Date: Sun, 10 May 2020 22:19:49 -0400 Subject: [PATCH 04/23] fix(makefile): Remove '-g' flag --- makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefile b/makefile index e8b63f8..75a8758 100644 --- a/makefile +++ b/makefile @@ -1,5 +1,5 @@ CC=gcc -CFLAGS=-g +CFLAGS= DEPS = formats/png.h OBJ = main.o formats/png.o From 5e744e00df57aefe804a655be81403465673cb0e Mon Sep 17 00:00:00 2001 From: Tyler Senter Date: Sun, 10 May 2020 22:20:25 -0400 Subject: [PATCH 05/23] feat: Dump header info with '--verbose' option --- main.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/main.c b/main.c index e7a41fb..4d3c1da 100644 --- a/main.c +++ b/main.c @@ -43,9 +43,30 @@ void handle_png(Format_PNG png) { while (chunk = png_chunk_next(png)) { if (strncmp(chunk->type, "iTXt", 4) == 0) { - char *text = png_iTXt_text(chunk); - printf(" %s: %s\n", png_iTXt_keyword(chunk), text); + char *text = png_iTXt_keyword(chunk); + printf(" %s", text); free(text); + text = png_iTXt_text(chunk); + printf(": %s", text); free(text); + + text = png_iTXt_lang(chunk); + if (text && *text) { + printf(" (lang: %s)", text); + } + printf("\n"); free(text); + } else if (strncmp(chunk->type, PNG_CHUNK_TEXT, PNG_CHNK_LEN) == 0) { + char *text = png_tEXt_keyword(chunk); + printf(" %s", text); free(text); + text = png_tEXt_text(chunk); + printf(": %s\n", text); free(text); + } else if (strncmp(chunk->type, PNG_CHUNK_TIME, PNG_CHNK_LEN) == 0) { + printf(" Timestamp: %d-%02d-%02dT%02d:%02d:%02d\n", + png_tIME_year(chunk), + png_tIME_month(chunk), + png_tIME_day(chunk), + png_tIME_hour(chunk), + png_tIME_minute(chunk), + png_tIME_second(chunk)); } png_chunk_free(chunk); From d32e03aaad00374b69f5d2adcd73cf69f02872de Mon Sep 17 00:00:00 2001 From: Tyler Senter Date: Sun, 10 May 2020 22:42:51 -0400 Subject: [PATCH 06/23] docs: Update to v0.1.0 --- CHANGELOG.md | 12 +++++++++++- utils/version.h | 4 ++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0988204..dc5be49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,2 +1,12 @@ +## v0.1.0 +**The PNG Update** +Incremental support for PNG image types is being added +* Scan through PNG chunks with `png_open()`, `png_chunk_next()`; online documentation coming soon +* Some textual metadata supported: + 1. `tEXt` chunks are supported through `png_tEXt_*()` functions + 2. `iTXt` chunks are partially supported for keyword, language and text fields through `png_iTXt_*()` functions + 3. `tIME` chunks are supported for extracting each timestamp attribute through `png_tIME_*()` functions +* Easy attribute extraction for image width and height: `png_attr_w()` and `png_attr_h()`, respectively + ## v0.0.0 -* No features have been implemented yet +* No features have been implemented yet \ No newline at end of file diff --git a/utils/version.h b/utils/version.h index caa700e..31bef0f 100644 --- a/utils/version.h +++ b/utils/version.h @@ -10,7 +10,7 @@ #endif /* Version */ -#define IV_VERSION "v0.0.0" -#define IV_UPDATED "2020-05-05" +#define IV_VERSION "v0.0.1" +#define IV_UPDATED "2020-05-10" #endif \ No newline at end of file From 0e51dfc56b04606a7a9cafc4c66fb27ae39fecf0 Mon Sep 17 00:00:00 2001 From: Tyler Senter Date: Sun, 10 May 2020 23:06:43 -0400 Subject: [PATCH 07/23] WIP feat(PNG): Extract tIME chunk as ISO 8601 timestamp --- formats/png.c | 20 ++++++++++++++++++++ formats/png.h | 1 + 2 files changed, 21 insertions(+) diff --git a/formats/png.c b/formats/png.c index e5eaa75..7385bd6 100644 --- a/formats/png.c +++ b/formats/png.c @@ -310,6 +310,26 @@ char png_tIME_second(png_chunk_t *chunk) { return *((char *) (chunk->data + 6)); } +/* + * Extract the last modified date in ISO 8601 format + * + * Returns: NULL on error + * Last modified date in ISO 8601 format: YYYY-MM-DDTHH:MM:SS+00:00 + */ +char *png_tIME_iso8601(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_TIME, PNG_CHNK_LEN) != 0) return NULL; + + char *time = (char *) malloc(sizeof(char) * 26); // Prefer not to hardcode the length in + + sprintf(time, "%04hd-%02hhd-%02hhdT%02hhd:%02hhd:%02hhd+00:00", + ntohs(*((short *) (chunk->data))), + *((char *) (chunk->data + 2)), + *((char *) (chunk->data + 3)), + *((char *) (chunk->data + 4)), + *((char *) (chunk->data + 5)), + *((char *) (chunk->data + 6))); +} + /* * Fetch the width of the given image */ diff --git a/formats/png.h b/formats/png.h index 2663ad0..b32957a 100644 --- a/formats/png.h +++ b/formats/png.h @@ -62,6 +62,7 @@ char png_tIME_day(png_chunk_t *); char png_tIME_hour(png_chunk_t *); char png_tIME_minute(png_chunk_t *); char png_tIME_second(png_chunk_t *); +char *png_tIME_iso8601(png_chunk_t *); /* Image attributes */ int png_attr_w(Format_PNG); From 3b903a74dfeca21024da6bdb2f80e61a9858197c Mon Sep 17 00:00:00 2001 From: Tyler Senter Date: Sun, 17 May 2020 00:58:33 -0400 Subject: [PATCH 08/23] feat(PNG): Allow image attribute extraction --- formats/png.c | 85 +++++++++++++++++++++++++++++++++++++++++++-------- formats/png.h | 13 ++++---- 2 files changed, 80 insertions(+), 18 deletions(-) diff --git a/formats/png.c b/formats/png.c index 7385bd6..6898856 100644 --- a/formats/png.c +++ b/formats/png.c @@ -333,33 +333,94 @@ char *png_tIME_iso8601(png_chunk_t *chunk) { /* * Fetch the width of the given image */ -int png_attr_w(Format_PNG png) { +int png_attr_width(Format_PNG png) { return ntohl(*((int *) png->IHDR->data)); } /* * Fetch the height of the given image */ -int png_attr_h(Format_PNG png) { +int png_attr_height(Format_PNG png) { return ntohl(*((int *) (png->IHDR->data + 4))); } -// TODO Remove when no longer needed -void png_debug(Format_PNG png) { - printf("%-25s | %u x %u pixels\n", png->name, png_attr_w(png), png_attr_h(png)); +/* + * Fetch the bit depth of the given image + * + * Returns: -1 on error + * bit depth on success + */ +char png_attr_bit_depth(Format_PNG png) { + if (png == NULL) return -1; + + if (png->IHDR == NULL || png->IHDR->data == NULL) return -1; + + if (png->IHDR->length < 9) return -1; + + return *((char *) png->IHDR->data + 8); } -/* - * Extract all metadata from a PNG image +/* + * Fetch the color type of the given image + * + * Returns: -1 on error + * color type on success + */ +char png_attr_col_type(Format_PNG png) { + if (png == NULL) return -1; + + if (png->IHDR == NULL || png->IHDR->data == NULL) return -1; + + if (png->IHDR->length < 10) return -1; + + return *((char *) png->IHDR->data + 9); +} + +/* + * Fetch the compression method of the given image * - * Returns: > 0 - total number of metadata items extracted - * 0 - metadata already extracted - * < 0 - an error occurred + * Returns: -1 on error + * compression method on success + */ +char png_attr_comp_method(Format_PNG png) { + if (png == NULL) return -1; + + if (png->IHDR == NULL || png->IHDR->data == NULL) return -1; + + if (png->IHDR->length < 11) return -1; + + return *((char *) png->IHDR->data + 10); +} + +/* + * Fetch the filter method of the given image + * + * Returns: -1 on error + * filter method on success */ -int png_extract_meta_all(Format_PNG png) { +char png_attr_filter_method(Format_PNG png) { + if (png == NULL) return -1; + + if (png->IHDR == NULL || png->IHDR->data == NULL) return -1; + if (png->IHDR->length < 12) return -1; + + return *((char *) png->IHDR->data + 11); } -int png_extract_meta(Format_PNG png, char *key, IVValue val) { +/* + * Fetch the interlace method of the given image + * + * Returns: -1 on error + * 0 on success (no interlace) + * 1 on success (Adam7 interlace) + */ +char png_attr_il_method(Format_PNG png) { + if (png == NULL) return -1; + if (png->IHDR == NULL || png->IHDR->data == NULL) return -1; + + if (png->IHDR->length < 13) return -1; + + return *((char *) png->IHDR->data + 12); } \ No newline at end of file diff --git a/formats/png.h b/formats/png.h index b32957a..76e35ea 100644 --- a/formats/png.h +++ b/formats/png.h @@ -65,11 +65,12 @@ char png_tIME_second(png_chunk_t *); char *png_tIME_iso8601(png_chunk_t *); /* Image attributes */ -int png_attr_w(Format_PNG); -int png_attr_h(Format_PNG); - -void png_debug(Format_PNG); - -int png_extract_meta(Format_PNG, char *, IVValue); +int png_attr_width(Format_PNG); +int png_attr_height(Format_PNG); +char png_attr_bit_depth(Format_PNG); +char png_attr_col_type(Format_PNG); +char png_attr_comp_method(Format_PNG); +char png_attr_filter_method(Format_PNG); +char png_attr_il_method(Format_PNG); #endif \ No newline at end of file From 496ea8728ceb2fa81c5ed4402083d85b8792aa79 Mon Sep 17 00:00:00 2001 From: Tyler Senter Date: Sun, 17 May 2020 01:37:30 -0400 Subject: [PATCH 09/23] feat(PNG): Add support for pHYs and zTXt chunks --- formats/png.c | 107 +++++++++++++++++++++++++++++++++++++++++++++++--- formats/png.h | 12 +++++- 2 files changed, 113 insertions(+), 6 deletions(-) diff --git a/formats/png.c b/formats/png.c index 6898856..539b493 100644 --- a/formats/png.c +++ b/formats/png.c @@ -50,7 +50,7 @@ int png_open(Format_PNG png, char *filename) { if (rv != PNG_SIG_SZ) return 1; /* Validate the signature */ - if ((*(long long*) (&buf)) - SIG_PNG != 0) return 1; + if ((*(long long*) (&buf)) - PNG_SIG != 0) return 1; if (iv_opts.verbose) { printf("matched.\n"); } @@ -159,12 +159,46 @@ void png_chunk_free(png_chunk_t *chunk) { } /* - * Extract the keyword from an iTXt chunk + * Extract the pixels per unit along the X axis + * + * Returns: -1 on error + * pixels per unit on success */ -char *png_iTXt_keyword(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_TEXT_INT, PNG_CHNK_LEN) != 0) return NULL; +int png_pHYs_ppuX(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_PHYS, PNG_CHNK_LEN) != 0) return -1; - return strdup((char *) chunk->data); + if (chunk->length < 4) return -1; + + return ntohl(*((int *)chunk->data)); +} + +/* + * Extract the pixels per unit along the Y axis + * + * Returns: -1 on error + * pixels per unit on success + */ +int png_pHYs_ppuY(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_PHYS, PNG_CHNK_LEN) != 0) return -1; + + if (chunk->length < 8) return -1; + + return ntohl(*((int *)(chunk->data + 4))); +} + +/* + * Extract the unit for pixels per unit + * + * Returns: -1 on error + * 0 for unknown unit + * 1 for meter + */ +char png_pHYs_unit(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_PHYS, PNG_CHNK_LEN) != 0) return -1; + + if (chunk->length < 9) return -1; + + return *((char *) chunk->data + 9); } /* @@ -197,6 +231,69 @@ char *png_tEXt_text(png_chunk_t *chunk) { return text; } +/* + * Extract the keyword from a zTXt chunk + * + * Returns: NULL on error + * keyword on success; must be free'd + */ +char *png_zTXt_keyword(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_TEXT, PNG_CHNK_LEN) != 0) return NULL; + + if (chunk->data == NULL || chunk->length == 0) return NULL; + + return strdup(chunk->data); +} + +/* + * Extract the compression method from a zTXt chunk + * + * Returns: -1 on error + * compression method (>= 0) + */ +char png_zTXt_method(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_TEXT_COMPRESSED, PNG_CHNK_LEN) != 0) return NULL; + + if (chunk->data == NULL || chunk->length == 0) return NULL; + + return *((char *) chunk->data + strlen(chunk->data) + 1); +} + +/* + * Extract the compressed datastream + */ +void *png_zTXt_data(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_TEXT_COMPRESSED, PNG_CHNK_LEN) != 0) return NULL; + + if (chunk->data == NULL || chunk->length == 0) return NULL; + + int len = strlen(chunk->data) + 2; + int n = chunk->length - len; + void *data = malloc(n); + + return memcpy(data, chunk->data + len, n); +} + +int png_zTXt_length(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_TEXT_COMPRESSED, PNG_CHNK_LEN) != 0) return NULL; + + if (chunk->data == NULL || chunk->length == 0) return NULL; + + return chunk->length - (strlen(chunk->data) + 2); +} + +/* + * Extract the keyword from an iTXt chunk + * + * Returns: NULL on error + * keyword on success + */ +char *png_iTXt_keyword(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_TEXT_INT, PNG_CHNK_LEN) != 0) return NULL; + + return strdup((char *) chunk->data); +} + /* * Extract the language tag from an iTXt chunk * diff --git a/formats/png.h b/formats/png.h index 76e35ea..fa16e9a 100644 --- a/formats/png.h +++ b/formats/png.h @@ -3,7 +3,7 @@ #include #include "../utils/types.h" -#define SIG_PNG 0x0A1A0A0D474E5089 +#define PNG_SIG 0x0A1A0A0D474E5089 #define PNG_SIG_SZ 8 #define PNG_CHNK_LEN 4 #define PNG_CHNK_TYPE_SZ 4 @@ -13,6 +13,7 @@ #define PNG_CHUNK_PALETTE "PLTE" #define PNG_CHUNK_DATA "IDAT" #define PNG_CHUNK_END "IEND" +#define PNG_CHUNK_PHYS "pHYs" #define PNG_CHUNK_TIME "tIME" #define PNG_CHUNK_TEXT_INT "iTXt" #define PNG_CHUNK_TEXT "tEXt" @@ -49,9 +50,18 @@ void png_close(Format_PNG); png_chunk_t *png_chunk_next(Format_PNG); void png_chunk_free(png_chunk_t *); +int png_pHYs_ppuX(png_chunk_t *); +int png_pHYs_ppuY(png_chunk_t *); +char png_pHYs_unit(png_chunk_t *); + char *png_tEXt_keyword(png_chunk_t *); char *png_tEXt_text(png_chunk_t *); +char *png_zTXt_keyword(png_chunk_t *); +char png_zTXt_method(png_chunk_t *); +void *png_zTXt_data(png_chunk_t *); +int png_zTXt_length(png_chunk_t *); + char *png_iTXt_keyword(png_chunk_t *); char *png_iTXt_lang(png_chunk_t *); char *png_iTXt_text(png_chunk_t *); From 63fe0038b5ed1860e824ed2bbb22f73aaad45eca Mon Sep 17 00:00:00 2001 From: Tyler Senter Date: Mon, 18 May 2020 21:14:43 -0400 Subject: [PATCH 10/23] docs(PNG): Document all functions to-date --- formats/png.c | 50 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/formats/png.c b/formats/png.c index 539b493..4229849 100644 --- a/formats/png.c +++ b/formats/png.c @@ -7,6 +7,12 @@ #include "../utils/version.h" #include "../utils/iv_opts.h" +/* + * Initialize an empty PNG image + * + * Returns: NULL on error + * Initialized Formant_PNG type + */ Format_PNG png_new() { Format_PNG png = (Format_PNG) malloc(sizeof(struct format_png_t)); @@ -66,6 +72,8 @@ int png_open(Format_PNG png, char *filename) { /* * Close the png file and free the resources associated with it + * + * Returns: void */ void png_close(Format_PNG png) { if (png->name) free(png->name); @@ -74,6 +82,13 @@ void png_close(Format_PNG png) { free(png); } +/* + * Extract the next chunk from the PNG image + * + * Returns: -1 on error + * NULL when a valid chunk cannot be read (EOF) + * Valid chunk from PNG image + */ png_chunk_t *png_chunk_next(Format_PNG png) { if (feof(png->fin) || ferror(png->fin)) return NULL; int rv; @@ -152,6 +167,11 @@ png_chunk_t *png_chunk_next(Format_PNG png) { return chunk; } +/* + * Free the memory allocated to a png chunk + * + * Returns: void + */ void png_chunk_free(png_chunk_t *chunk) { if (chunk->data) free(chunk->data); @@ -204,7 +224,7 @@ char png_pHYs_unit(png_chunk_t *chunk) { /* * Extract the keyword from a tEXt chunk * - * Returns: NULL - on error + * Returns: NULL on error * keyword on success; must be free'd */ char *png_tEXt_keyword(png_chunk_t *chunk) { @@ -216,7 +236,7 @@ char *png_tEXt_keyword(png_chunk_t *chunk) { /* * Extract the text from a tEXt chunk * - * Returns: NULL - on error + * Returns: NULL on error * keyword on success; must be free'd */ char *png_tEXt_text(png_chunk_t *chunk) { @@ -260,7 +280,10 @@ char png_zTXt_method(png_chunk_t *chunk) { } /* - * Extract the compressed datastream + * Extract the compressed text datastream + * + * Returns: NULL on error + * Pointer to block containing datastream; must be free'd */ void *png_zTXt_data(png_chunk_t *chunk) { if (strncmp(chunk->type, PNG_CHUNK_TEXT_COMPRESSED, PNG_CHNK_LEN) != 0) return NULL; @@ -274,6 +297,12 @@ void *png_zTXt_data(png_chunk_t *chunk) { return memcpy(data, chunk->data + len, n); } +/* + * Extract the length of the compressed text datastream + * + * Returns: -1 on error + * Length of datastream + */ int png_zTXt_length(png_chunk_t *chunk) { if (strncmp(chunk->type, PNG_CHUNK_TEXT_COMPRESSED, PNG_CHNK_LEN) != 0) return NULL; @@ -297,8 +326,8 @@ char *png_iTXt_keyword(png_chunk_t *chunk) { /* * Extract the language tag from an iTXt chunk * - * Returns: NULL - on error - * "" - no language tag available; must be free'd + * Returns: NULL on error + * Empty string if no language tag available; must be free'd * string containing ISO 646 hyphen-separated words; must be free'd */ char *png_iTXt_lang(png_chunk_t *chunk) { @@ -311,6 +340,9 @@ char *png_iTXt_lang(png_chunk_t *chunk) { /* * Extract the text value from an iTXt chunk + * + * Returns: NULL on error + * string containing text from an iTXt chunk on success; must be free'd */ char *png_iTXt_text(png_chunk_t *chunk) { if (strncmp(chunk->type, PNG_CHUNK_TEXT_INT, PNG_CHNK_LEN) != 0) return NULL; @@ -399,7 +431,7 @@ char png_tIME_minute(png_chunk_t *chunk) { * Extract the second from the last modified date * * Returns: -1 on error - * Minute (0-60 to allow for leap seconds) on success + * Second (0-60 to allow for leap seconds) on success */ char png_tIME_second(png_chunk_t *chunk) { if (strncmp(chunk->type, PNG_CHUNK_TIME, PNG_CHNK_LEN) != 0) return -1; @@ -429,6 +461,9 @@ char *png_tIME_iso8601(png_chunk_t *chunk) { /* * Fetch the width of the given image + * + * Returns: -1 on error + * width of image (in pixels) on success */ int png_attr_width(Format_PNG png) { return ntohl(*((int *) png->IHDR->data)); @@ -436,6 +471,9 @@ int png_attr_width(Format_PNG png) { /* * Fetch the height of the given image + * + * Returns: -1 on error + * height of image (in pixels) on success */ int png_attr_height(Format_PNG png) { return ntohl(*((int *) (png->IHDR->data + 4))); From 63df7f76db2be09d059d2d3fe0cfb6b6a441219f Mon Sep 17 00:00:00 2001 From: Tyler Senter Date: Mon, 18 May 2020 21:30:57 -0400 Subject: [PATCH 11/23] fix(PNG): Add checks for width, height --- formats/png.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/formats/png.c b/formats/png.c index 4229849..056f1b2 100644 --- a/formats/png.c +++ b/formats/png.c @@ -304,9 +304,9 @@ void *png_zTXt_data(png_chunk_t *chunk) { * Length of datastream */ int png_zTXt_length(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_TEXT_COMPRESSED, PNG_CHNK_LEN) != 0) return NULL; + if (strncmp(chunk->type, PNG_CHUNK_TEXT_COMPRESSED, PNG_CHNK_LEN) != 0) return -1; - if (chunk->data == NULL || chunk->length == 0) return NULL; + if (chunk->data == NULL || chunk->length == 0) return -1; return chunk->length - (strlen(chunk->data) + 2); } @@ -466,6 +466,12 @@ char *png_tIME_iso8601(png_chunk_t *chunk) { * width of image (in pixels) on success */ int png_attr_width(Format_PNG png) { + if (png == NULL) return -1; + + if (png->IHDR == NULL || png->IHDR->data == NULL) return -1; + + if (png->IHDR->length < 4) return -1; + return ntohl(*((int *) png->IHDR->data)); } @@ -476,6 +482,12 @@ int png_attr_width(Format_PNG png) { * height of image (in pixels) on success */ int png_attr_height(Format_PNG png) { + if (png == NULL) return -1; + + if (png->IHDR == NULL || png->IHDR->data == NULL) return -1; + + if (png->IHDR->length < 8) return -1; + return ntohl(*((int *) (png->IHDR->data + 4))); } From ebb2e8f6b428c7217fd7ef551916e92d157b0d4d Mon Sep 17 00:00:00 2001 From: Tyler Senter Date: Mon, 18 May 2020 21:41:58 -0400 Subject: [PATCH 12/23] misc(PNG): Clean up memory and messaging --- formats/png.c | 58 ++++++++++++++++++++------------------------------- formats/png.h | 1 + 2 files changed, 24 insertions(+), 35 deletions(-) diff --git a/formats/png.c b/formats/png.c index 056f1b2..2542051 100644 --- a/formats/png.c +++ b/formats/png.c @@ -5,7 +5,6 @@ #include "png.h" #include "../utils/version.h" -#include "../utils/iv_opts.h" /* * Initialize an empty PNG image @@ -16,7 +15,13 @@ Format_PNG png_new() { Format_PNG png = (Format_PNG) malloc(sizeof(struct format_png_t)); + if (png == NULL) return NULL; + // TODO Initialize PNG image + png->IHDR = NULL; + png->fin = NULL; + png->name = NULL; + png->bytes = 0; return png; } @@ -47,23 +52,18 @@ int png_open(Format_PNG png, char *filename) { /* Attempt to read the signature */ rv = fread(buf, sizeof(char), PNG_SIG_SZ, png->fin); - if (iv_opts.verbose) { - printf("Reading signature... "); - } else if (iv_opts.scan) { - printf("[PNG] Read signature\n"); - } if (rv != PNG_SIG_SZ) return 1; + png->bytes += sizeof(char) * rv; /* Validate the signature */ if ((*(long long*) (&buf)) - PNG_SIG != 0) return 1; - if (iv_opts.verbose) { - printf("matched.\n"); - } png->IHDR = png_chunk_next(png); - if (png->IHDR == NULL) { + if (png->IHDR == (png_chunk_t *) -1) { + free(png->name); + fclose(png->fin); return -1; } @@ -90,8 +90,9 @@ void png_close(Format_PNG png) { * Valid chunk from PNG image */ png_chunk_t *png_chunk_next(Format_PNG png) { - if (feof(png->fin) || ferror(png->fin)) return NULL; + int read = 0; int rv; + if (feof(png->fin) || ferror(png->fin)) return (png_chunk_t *) -1; png_chunk_t *chunk = (png_chunk_t *) malloc(sizeof(png_chunk_t)); @@ -102,31 +103,24 @@ png_chunk_t *png_chunk_next(Format_PNG png) { return NULL; } if (rv != 1) { - if (iv_opts.verbose) { - printf("Chunk: Error (chunk length)\n"); - } free(chunk); - return NULL; - } - if (iv_opts.verbose) { - printf("Chunk: "); - fflush(stdout); + return (png_chunk_t *) -1; } + read += sizeof(char) * rv; /* Normalize length */ chunk->length = ntohl(chunk->length); /* Read chunk type */ - rv = fread(chunk->type, 1, PNG_CHNK_TYPE_SZ, png->fin); + rv = fread(chunk->type, sizeof(char), PNG_CHNK_TYPE_SZ, png->fin); if (rv != PNG_CHNK_TYPE_SZ) { - if (iv_opts.verbose) { - printf("Error (chunk type)\n"); - } + fseek(png->fin, -1 * read, SEEK_CUR); fprintf(stderr, "[%s/png]: Unexpected EOF in reading chunk type, read %d/%d chunk type bytes\n", IV_PROGRAM_NAME, rv, PNG_CHNK_TYPE_SZ); free(chunk); return NULL; } + read += sizeof(char) * rv; /* Allocate memory for chunk data */ chunk->data = malloc(chunk->length); @@ -135,14 +129,13 @@ png_chunk_t *png_chunk_next(Format_PNG png) { if (chunk->length != 0) { rv = fread(chunk->data, 1, chunk->length, png->fin); if (rv != chunk->length) { - if (iv_opts.verbose) { - printf("Error (chunk data)\n"); - } + fseek(png->fin, -1 * read, SEEK_CUR); fprintf(stderr, "[%s/png]: Unexpected EOF in reading chunk '%.4s', read %d/%d chunk data bytes\n", IV_PROGRAM_NAME, chunk->type, rv, chunk->length); free(chunk->data); free(chunk); return NULL; } + read += rv; } else { free(chunk->data); chunk->data = NULL; @@ -151,18 +144,13 @@ png_chunk_t *png_chunk_next(Format_PNG png) { /* Read CRC */ rv = fread(&chunk->CRC, sizeof(int), 1, png->fin); if (rv != 1) { - if (iv_opts.verbose) { - printf("Error (chunk CRC)\n"); - } + fseek(png->fin, -1 * read, SEEK_CUR); fprintf(stderr, "[%s/png]: Unexpected EOF in reading CRC for chunk '%.4s', read %d/%d bytes\n", IV_PROGRAM_NAME, chunk->type, rv, 4); free(chunk->data); free(chunk); return NULL; } - - if (iv_opts.verbose) { - printf("%.4s (%d byte%s)\n", chunk->type, chunk->length, chunk->length != 1 ? "s" : ""); - } + read += sizeof(int) * rv; return chunk; } @@ -272,9 +260,9 @@ char *png_zTXt_keyword(png_chunk_t *chunk) { * compression method (>= 0) */ char png_zTXt_method(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_TEXT_COMPRESSED, PNG_CHNK_LEN) != 0) return NULL; + if (strncmp(chunk->type, PNG_CHUNK_TEXT_COMPRESSED, PNG_CHNK_LEN) != 0) return -1; - if (chunk->data == NULL || chunk->length == 0) return NULL; + if (chunk->data == NULL || chunk->length == 0) return -1; return *((char *) chunk->data + strlen(chunk->data) + 1); } diff --git a/formats/png.h b/formats/png.h index fa16e9a..060c134 100644 --- a/formats/png.h +++ b/formats/png.h @@ -37,6 +37,7 @@ typedef struct format_png_t { png_chunk_t *IHDR; FILE *fin; + unsigned long long bytes; } *Format_PNG; /* Function signatures */ From a0f462f0d59872a0d39b3286b5dbef4992edd611 Mon Sep 17 00:00:00 2001 From: Tyler Senter Date: Mon, 18 May 2020 23:01:12 -0400 Subject: [PATCH 13/23] docs: Update to v0.1.1 --- CHANGELOG.md | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc5be49..f67b5c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,27 @@ -## v0.1.0 -**The PNG Update** +# Image-View Changelog + +## v0.1.x - **The PNG Update** + Incremental support for PNG image types is being added + +### v0.1.1 + +* Added extraction for other image attributes: + * Bit depth with `png_attr_bit_depth()` + * Color type with `png_attr_col_type()` + * Compression method with `png_attr_comp_method()` + * Filter method with `png_attr_filter_method()` + * Interlace method with `png_attr_il_method()` +* Added ability to quickly extract timestamp from `tIME` chunks in ISO 8601 format with `png_tIME_iso8601()` +* Added support for two new chunks: + 1. `pHYs` chunks are supported through `png_pHYs_*()` functions + 2. `zTXt` chunks are supported through `png_zTXt_*()` functions +* Other various improvements were made: + * Memory management was cleaned up + * Logging was enhanced and messaging was removed, to be implemented elsewhere + +### v0.1.0 + * Scan through PNG chunks with `png_open()`, `png_chunk_next()`; online documentation coming soon * Some textual metadata supported: 1. `tEXt` chunks are supported through `png_tEXt_*()` functions @@ -9,4 +30,5 @@ Incremental support for PNG image types is being added * Easy attribute extraction for image width and height: `png_attr_w()` and `png_attr_h()`, respectively ## v0.0.0 -* No features have been implemented yet \ No newline at end of file + +* No features have been implemented yet From 10276e08a4aa9c561c9c81db7a8a16786dabd25e Mon Sep 17 00:00:00 2001 From: Tyler Senter Date: Mon, 18 May 2020 23:39:05 -0400 Subject: [PATCH 14/23] WIP feat: Rework driver --- main.c | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/main.c b/main.c index 4d3c1da..12c8a7c 100644 --- a/main.c +++ b/main.c @@ -16,7 +16,8 @@ void usage(int exit_code) { "Usage: %s [options...] [command] [file2...]\n\n" "Options:\n" "-h, --help [command] Get help on %s (or [command])\n" - "-v, --version Print version information\n", + "-v, --version Print version information\n" + "-s, --scan Scan the chunks of each image\n", USAGE_PROGRAM_NAME, USAGE_PROGRAM_NAME); exit(exit_code); } @@ -42,7 +43,13 @@ void handle_png(Format_PNG png) { png_chunk_t *chunk; while (chunk = png_chunk_next(png)) { - if (strncmp(chunk->type, "iTXt", 4) == 0) { + printf(" Chunk: "); + if (chunk == ((png_chunk_t *) -1)) { + printf("error\n"); + break; + } + printf("%.4s (%d)\n", chunk->type, chunk->length); + if (strncmp(chunk->type, PNG_CHUNK_TEXT_INT, 4) == 0) { char *text = png_iTXt_keyword(chunk); printf(" %s", text); free(text); text = png_iTXt_text(chunk); @@ -71,12 +78,10 @@ void handle_png(Format_PNG png) { png_chunk_free(chunk); } - - // png_debug(png); } int main(int argc, char **argv) { - int i, rv; + int i, rv, n, len = 0; const struct option options[] = { { "version", no_argument, NULL, 'v' }, { "help", optional_argument, NULL, 'h' }, @@ -140,20 +145,32 @@ int main(int argc, char **argv) { png_close(png); exit(EXIT_FAILURE); } - handle_png(png); + if (iv_opts.scan) { + handle_png(png); + } png_close(png); } } // TODO Read files + for (i = optind; i < argc; i++) { + n = strlen(argv[i]); + if (n > len) len = n; + } + + n = argc - optind; for (i = optind; i < argc; i++) { Format_PNG png = png_new(); png_chunk_t *chunk; if (access(argv[i], R_OK) == -1) { // TODO Error message for bad file - printf("File does not exist: %s\n", argv[i]); + printf("Could not open file %s\n", argv[i]); continue; } + if (n != 1) { + printf("%s %.*s\n", argv[i], (int) (len - strlen(argv[i]) + 7), "--------------------------------------"); + } + rv = png_open(png, argv[i]); if (rv != 0) { @@ -161,7 +178,9 @@ int main(int argc, char **argv) { continue; } - handle_png(png); + if (iv_opts.scan) { + handle_png(png); + } png_close(png); } From bf458c8b7055e61906538fe6a4604d6192174451 Mon Sep 17 00:00:00 2001 From: Tyler Senter Date: Mon, 25 May 2020 14:40:55 -0400 Subject: [PATCH 15/23] feat(PNG): Add support for remaining critical chunks (#6) --- CHANGELOG.md | 7 ++++ formats/png.c | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++ formats/png.h | 9 +++++ main.c | 9 +++++ 4 files changed, 133 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f67b5c1..2f0e839 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,13 @@ Incremental support for PNG image types is being added +### v0.1.2 + +* Added support for remaining critical chunks: + * `PLTE` chunks can be processed through the `png_PLTE_*()` functions + * `IDAT` chunks can be processed through the `png_IDAT_*()` functions + * `IEND` chunks can be validated with the `png_IEND_valid()` function + ### v0.1.1 * Added extraction for other image attributes: diff --git a/formats/png.c b/formats/png.c index 2542051..8a219ce 100644 --- a/formats/png.c +++ b/formats/png.c @@ -166,6 +166,114 @@ void png_chunk_free(png_chunk_t *chunk) { free(chunk); } +/* + * Fetch the palette as a two-dimensional array: + * [n][0] - Red channel + * [n][1] - Green channel + * [n][2] - Blue channel + * + * Returns: NULL on error + * (char **) -1 if chunk length is not divisible by 3 (required by Internation Standard section 11.2.3) + */ +char **png_PLTE_get(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_PALETTE, PNG_CHNK_LEN) != 0) return NULL; + + if (chunk->length == 0 || chunk->data == NULL) return NULL; + + int n = chunk->length % 3, i; + + if (n != 0) return (char **) -1; + + n = chunk->length / 3; + char **palette = (char **) malloc(sizeof(char *) * n); + + for (i = 0; i < n; i++) { + palette[i] = malloc(sizeof(char) * 3); + palette[i][0] = ((char *) chunk->data)[3*i]; + palette[i][1] = ((char *) chunk->data)[3*i+1]; + palette[i][2] = ((char *) chunk->data)[3*i+2]; + } + + return palette; +} + +/* + * Fetch the number of entries in a palette chunk + * + * Returns: -1 on error + * 0 if chunk does not conform to International Standard (section 11.2.3) + * + */ +int png_PLTE_length(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_PALETTE, PNG_CHNK_LEN) != 0) return -1; + + if (chunk->length == 0 || chunk->data == NULL) return -1; + + if (chunk->length % 3 != 0) return 0; + + return chunk->length / 3; +} + +/* + * Free the memory allocated for a palette + * + * Returns: void + */ +void png_PLTE_free(char **palette, int len) { + if (palette == NULL || palette[0] == NULL) return; + + if (len <= 0) return; + + while (len--) { + free(palette[len]); + } + free(palette); +} + +/* + * Get the included datastream for the IDAT chunk + * + * Returns: NULL on error + */ +void *png_IDAT_get(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_DATA, PNG_CHNK_LEN) != 0) return NULL; + + if (chunk->length == 0 || chunk->data == NULL) return NULL; + + void *data = malloc(chunk->length); + + memcpy(data, chunk->data, chunk->length); + + return data; +} + +/* + * Get the length of the datastream for this IDAT chunk + * + * Returns: -1 on error + */ +int png_IDAT_length(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_DATA, PNG_CHNK_LEN) != 0) return -1; + + if (chunk->length == 0 || chunk->data == NULL) return -1; + + return chunk->length; +} + +/* + * Validate an IEND chunk. An IEND chunk is considered valid if it has no length and no data + * + * Returns: 1 if chunk is invalid + * 0 if chunk is valid + */ +int png_IEND_valid(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_END, PNG_CHNK_LEN) != 0) return 1; + + if (chunk->length != 0 || chunk->data != NULL) return 1; + + return 0; +} + /* * Extract the pixels per unit along the X axis * diff --git a/formats/png.h b/formats/png.h index 060c134..997f67f 100644 --- a/formats/png.h +++ b/formats/png.h @@ -51,6 +51,15 @@ void png_close(Format_PNG); png_chunk_t *png_chunk_next(Format_PNG); void png_chunk_free(png_chunk_t *); +char **png_PLTE_get(png_chunk_t *); +int png_PLTE_length(png_chunk_t *); +void png_PLTE_free(char **, int); + +void *png_IDAT_get(png_chunk_t *); +int png_IDAT_length(png_chunk_t *); + +int png_IEND_valid(png_chunk_t *); + int png_pHYs_ppuX(png_chunk_t *); int png_pHYs_ppuY(png_chunk_t *); char png_pHYs_unit(png_chunk_t *); diff --git a/main.c b/main.c index 12c8a7c..2459afd 100644 --- a/main.c +++ b/main.c @@ -74,6 +74,15 @@ void handle_png(Format_PNG png) { png_tIME_hour(chunk), png_tIME_minute(chunk), png_tIME_second(chunk)); + } else if (strncmp(chunk->type, PNG_CHUNK_PALETTE, PNG_CHNK_LEN) == 0) { + int len = png_PLTE_length(chunk), i; + char **palette = png_PLTE_get(chunk); + printf(" Entries: %d\n", len); + for (i = 0; i < 10 && i < len; i++) { + printf(" #%d: 0x%02hhx%02hhx%02hhx\n", i, palette[i][0], palette[i][1], palette[i][2]); + } + if (i != len) printf(" ...\n"); + png_PLTE_free(palette, len); } png_chunk_free(chunk); From ad4439c88d17a52f994c061bd60b016a3e2215bb Mon Sep 17 00:00:00 2001 From: Tyler Senter Date: Mon, 25 May 2020 23:16:50 -0400 Subject: [PATCH 16/23] feat: Add command-line option for 'deep scan' --- CHANGELOG.md | 6 ++++ formats/png.c | 2 ++ formats/png.h | 2 +- main.c | 80 +++++++++++++++++++++++++++---------------------- utils/iv_opts.h | 1 + 5 files changed, 54 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f0e839..a2b3774 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ Incremental support for PNG image types is being added +### v0.1.3 + +* Added command-line option for a deep scan: + * A normal scan (options `-s` or `--scan`) only reveal the chunks and their length + * A deep scan reveals information pertinent to each chunk (first 10 palette entries, textual/date information, etc.) + ### v0.1.2 * Added support for remaining critical chunks: diff --git a/formats/png.c b/formats/png.c index 8a219ce..3eec0c5 100644 --- a/formats/png.c +++ b/formats/png.c @@ -152,6 +152,8 @@ png_chunk_t *png_chunk_next(Format_PNG png) { } read += sizeof(int) * rv; + chunk->png = png; + return chunk; } diff --git a/formats/png.h b/formats/png.h index 997f67f..70eabbe 100644 --- a/formats/png.h +++ b/formats/png.h @@ -28,7 +28,7 @@ typedef struct png_chunk { void *data; unsigned int CRC; - struct png_chunk *next; + struct format_png_t *png; } png_chunk_t; typedef struct format_png_t { diff --git a/main.c b/main.c index 2459afd..e0dfa2e 100644 --- a/main.c +++ b/main.c @@ -17,7 +17,8 @@ void usage(int exit_code) { "Options:\n" "-h, --help [command] Get help on %s (or [command])\n" "-v, --version Print version information\n" - "-s, --scan Scan the chunks of each image\n", + "-s, --scan View the chunks of each image\n" + "-d, --scan-deep View details of the chunks of each image\n", USAGE_PROGRAM_NAME, USAGE_PROGRAM_NAME); exit(exit_code); } @@ -49,40 +50,41 @@ void handle_png(Format_PNG png) { break; } printf("%.4s (%d)\n", chunk->type, chunk->length); - if (strncmp(chunk->type, PNG_CHUNK_TEXT_INT, 4) == 0) { - char *text = png_iTXt_keyword(chunk); - printf(" %s", text); free(text); - text = png_iTXt_text(chunk); - printf(": %s", text); free(text); - - text = png_iTXt_lang(chunk); - if (text && *text) { - printf(" (lang: %s)", text); - } - printf("\n"); - free(text); - } else if (strncmp(chunk->type, PNG_CHUNK_TEXT, PNG_CHNK_LEN) == 0) { - char *text = png_tEXt_keyword(chunk); - printf(" %s", text); free(text); - text = png_tEXt_text(chunk); - printf(": %s\n", text); free(text); - } else if (strncmp(chunk->type, PNG_CHUNK_TIME, PNG_CHNK_LEN) == 0) { - printf(" Timestamp: %d-%02d-%02dT%02d:%02d:%02d\n", - png_tIME_year(chunk), - png_tIME_month(chunk), - png_tIME_day(chunk), - png_tIME_hour(chunk), - png_tIME_minute(chunk), - png_tIME_second(chunk)); - } else if (strncmp(chunk->type, PNG_CHUNK_PALETTE, PNG_CHNK_LEN) == 0) { - int len = png_PLTE_length(chunk), i; - char **palette = png_PLTE_get(chunk); - printf(" Entries: %d\n", len); - for (i = 0; i < 10 && i < len; i++) { - printf(" #%d: 0x%02hhx%02hhx%02hhx\n", i, palette[i][0], palette[i][1], palette[i][2]); - } - if (i != len) printf(" ...\n"); - png_PLTE_free(palette, len); + if (iv_opts.deep) { + if (strncmp(chunk->type, PNG_CHUNK_TEXT_INT, 4) == 0) { + char *text = png_iTXt_keyword(chunk); + printf(" %s", text); free(text); + text = png_iTXt_text(chunk); + printf(": %s", text); free(text); + + text = png_iTXt_lang(chunk); + if (text && *text) { + printf(" (lang: %s)", text); + } + printf("\n"); + free(text); + } else if (strncmp(chunk->type, PNG_CHUNK_TEXT, PNG_CHNK_LEN) == 0) { + char *text = png_tEXt_keyword(chunk); + printf(" %s", text); free(text); + text = png_tEXt_text(chunk); + printf(": %s\n", text); free(text); + } else if (strncmp(chunk->type, PNG_CHUNK_TIME, PNG_CHNK_LEN) == 0) { + printf(" Timestamp: %d-%02d-%02dT%02d:%02d:%02d\n", + png_tIME_year(chunk), + png_tIME_month(chunk), + png_tIME_day(chunk), + png_tIME_hour(chunk), + png_tIME_minute(chunk), + png_tIME_second(chunk)); + } else if (strncmp(chunk->type, PNG_CHUNK_PALETTE, PNG_CHNK_LEN) == 0) { + int len = png_PLTE_length(chunk), i; + char **palette = png_PLTE_get(chunk); + printf(" Entries: %d\n", len); + for (i = 0; i < 10 && i < len; i++) { + printf(" #%d: 0x%02hhx%02hhx%02hhx\n", i, palette[i][0], palette[i][1], palette[i][2]); + } + if (i != len) printf(" ...\n"); + png_PLTE_free(palette, len); } png_chunk_free(chunk); @@ -96,6 +98,7 @@ int main(int argc, char **argv) { { "help", optional_argument, NULL, 'h' }, { "verbose", optional_argument, NULL, 'V' }, { "scan", no_argument, NULL, 's' }, + { "scan-deep", no_argument, NULL, 'd' }, {NULL, 0, NULL, 0}, /* Last element must be all 0s */ }; @@ -105,7 +108,7 @@ int main(int argc, char **argv) { int option_index; int c; while (1) { - c = getopt_long(argc, argv, "vh::V::s", options, &option_index); + c = getopt_long(argc, argv, "vh::V::sd", options, &option_index); if (c == -1) break; @@ -127,6 +130,11 @@ int main(int argc, char **argv) { iv_opts.scan = 1; break; + case 'd': + iv_opts.scan = 1; + iv_opts.deep = 1; + break; + case '?': exit(EXIT_FAILURE); diff --git a/utils/iv_opts.h b/utils/iv_opts.h index 12cdbc1..ebe0d45 100644 --- a/utils/iv_opts.h +++ b/utils/iv_opts.h @@ -1,4 +1,5 @@ struct global_opts { int verbose; int scan; + char deep; } iv_opts; \ No newline at end of file From 400d63cef7f1f11b6190f667220d43bebc797ad4 Mon Sep 17 00:00:00 2001 From: Tyler Senter Date: Mon, 25 May 2020 23:20:09 -0400 Subject: [PATCH 17/23] feat(PNG): Add support for 'tRNS' chunk --- CHANGELOG.md | 5 ++++ formats/png.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++ formats/png.h | 6 ++++ main.c | 22 +++++++++++++++ 4 files changed, 110 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a2b3774..96250d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ Incremental support for PNG image types is being added +### v0.1.4 + +* Added support for transparency ancillary chunks: + * `tRNS` chunks can be processed through the `png_tRNS_*()` functions + ### v0.1.3 * Added command-line option for a deep scan: diff --git a/formats/png.c b/formats/png.c index 3eec0c5..d6bbfcb 100644 --- a/formats/png.c +++ b/formats/png.c @@ -276,6 +276,83 @@ int png_IEND_valid(png_chunk_t *chunk) { return 0; } +/* + * Fetch transparency from tRNS chunk (color type 0 - grayscale) + * + * Returns: -1 on error (image could be of the wrong color type) + */ +short png_tRNS_gray(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_TRANSPARENCY, PNG_CHNK_LEN) != 0) return -1; + + if (chunk->length == 0 || chunk->data == NULL) return -1; + + if (png_attr_col_type(chunk->png) != 0) return -1; + + return ntohs(*((short *)chunk->data)); +} + +/* + * Fetch transparency samples from tRNS chunk (color type 2 - truecolor) + * + * Returns: NULL on error + * Array of 3 sample values (R, G, B); must be free'd + */ +short *png_tRNS_true(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_TRANSPARENCY, PNG_CHNK_LEN) != 0) return NULL; + + if (chunk->length == 0 || chunk->data == NULL) return NULL; + + if (png_attr_col_type(chunk->png) != 2) return NULL; + + short *array = (short *) malloc(sizeof(short) * 3); + int i; + + for (i = 0; i < 3; i++) { + array[i] = ntohs(*(((short *) chunk->data) + i)); + } + + return array; +} + +/* + * Fetch transparency palette from tRNS chunk (color type 3 - indexed-color) + * + * Returns: NULL on error + * Array of alpha channel for palette indexes; must be free'd + */ +char *png_tRNS_index(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_TRANSPARENCY, PNG_CHNK_LEN) != 0) return NULL; + + if (chunk->length == 0 || chunk->data == NULL) return NULL; + + if (png_attr_col_type(chunk->png) != 3) return NULL; + + char *palette = (char *) malloc(sizeof(char) * chunk->length); + int i; + + for (i = 0; i < chunk->length; i++) { + palette[i] = ((char *)(chunk->data))[i]; + } + + return palette; +} + +/* + * Fetch the length of the transparency palette from tRNS chunk (color type 3 - indexed-color) + * + * Returns: -1 on error + * Length of array returned by `png_tRNS_index()` + */ +int png_tRNS_index_length(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_TRANSPARENCY, PNG_CHNK_LEN) != 0) return -1; + + if (chunk->length == 0 || chunk->data == NULL) return -1; + + if (png_attr_col_type(chunk->png) != 3) return -1; + + return chunk->length; +} + /* * Extract the pixels per unit along the X axis * diff --git a/formats/png.h b/formats/png.h index 70eabbe..8aecb42 100644 --- a/formats/png.h +++ b/formats/png.h @@ -13,6 +13,7 @@ #define PNG_CHUNK_PALETTE "PLTE" #define PNG_CHUNK_DATA "IDAT" #define PNG_CHUNK_END "IEND" +#define PNG_CHUNK_TRANSPARENCY "tRNS" #define PNG_CHUNK_PHYS "pHYs" #define PNG_CHUNK_TIME "tIME" #define PNG_CHUNK_TEXT_INT "iTXt" @@ -60,6 +61,11 @@ int png_IDAT_length(png_chunk_t *); int png_IEND_valid(png_chunk_t *); +short png_tRNS_gray(png_chunk_t *); +short *png_tRNS_true(png_chunk_t *); +char *png_tRNS_index(png_chunk_t *); +int png_tRNS_index_length(png_chunk_t *); + int png_pHYs_ppuX(png_chunk_t *); int png_pHYs_ppuY(png_chunk_t *); char png_pHYs_unit(png_chunk_t *); diff --git a/main.c b/main.c index e0dfa2e..b365e69 100644 --- a/main.c +++ b/main.c @@ -85,6 +85,28 @@ void handle_png(Format_PNG png) { } if (i != len) printf(" ...\n"); png_PLTE_free(palette, len); + } else if (strncmp(chunk->type, PNG_CHUNK_TRANSPARENCY, PNG_CHNK_LEN) == 0) { + int type = png_attr_col_type(png); + + if (type == 0) { + printf(" Grayscale sample: %hx\n", png_tRNS_gray(chunk)); + } else if (type == 2) { + short *rgb = png_tRNS_true(chunk); + printf(" Truecolor sample:\n R = %hx\n G = %hx\n B = %hx\n", rgb[0], rgb[1], rgb[2]); + free(rgb); + } else if (type == 3) { + int len = png_tRNS_index_length(chunk), i; + char *palette = png_tRNS_index(chunk); + printf(" Entries: %d\n", len); + for (i = 0; i < 10 && i < len; i++) { + printf(" #%d: %hhu\n", i, palette[i]); + } + if (i != len) printf(" ...\n"); + free(palette); + } else { + printf(" [Error]: Invalid color type %d\n", type); + } + } } png_chunk_free(chunk); From c041b3d1c0fed586f54e152481b8b719502a5cb7 Mon Sep 17 00:00:00 2001 From: Tyler Senter Date: Tue, 26 May 2020 22:21:33 -0400 Subject: [PATCH 18/23] feat(PNG): Add CSI chunk support (#11) --- CHANGELOG.md | 9 ++ formats/png.c | 300 ++++++++++++++++++++++++++++++++++++++++++++++++++ formats/png.h | 28 +++++ main.c | 64 +++++++++++ 4 files changed, 401 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 96250d3..144f980 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,15 @@ Incremental support for PNG image types is being added +### v0.1.5 + +* Added support for color space information chunks: + * `cHRM` chunks can be processed through the `png_cHRM_*()` functions + * `gAMA` chunks can be processed through the `png_gAMA*()` functions + * `iCCP` chunks can be processed through the `png_iCCP_*()` functions + * `sBIT` chunks can be processed through the `png_sBIT_*()` functions + * `sRGB` chunks can be processed through the `png_sRGB_get()` function + ### v0.1.4 * Added support for transparency ancillary chunks: diff --git a/formats/png.c b/formats/png.c index d6bbfcb..ffea70f 100644 --- a/formats/png.c +++ b/formats/png.c @@ -353,6 +353,306 @@ int png_tRNS_index_length(png_chunk_t *chunk) { return chunk->length; } +/* + * Fetch the white point X from the cHRM chunk + * + * Returns: -1 on error + */ +float png_cHRM_whiteX(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_CHROMACITY, PNG_CHNK_LEN) != 0) return -1; + + if (chunk->length == 0 || chunk->data == NULL) return -1; + + int whiteX = *((int *) chunk->data); + whiteX = ntohl(whiteX); + + return whiteX / PNG_RATIO; +} + +/* + * Fetch the white point Y from the cHRM chunk + * + * Returns: -1 on error + */ +float png_cHRM_whiteY(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_CHROMACITY, PNG_CHNK_LEN) != 0) return -1; + + if (chunk->length == 0 || chunk->data == NULL) return -1; + + int whiteY = *(((int *)chunk->data)+1); + whiteY = ntohl(whiteY); + + return whiteY / PNG_RATIO; +} + +/* + * Fetch the red point X from the cHRM chunk + * + * Returns: -1 on error + */ +float png_cHRM_redX(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_CHROMACITY, PNG_CHNK_LEN) != 0) return -1; + + if (chunk->length == 0 || chunk->data == NULL) return -1; + + int redX = *(((int *)chunk->data)+2); + redX = ntohl(redX); + + return redX / PNG_RATIO; +} + +/* + * Fetch the red point Y from the cHRM chunk + * + * Returns: -1 on error + */ +float png_cHRM_redY(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_CHROMACITY, PNG_CHNK_LEN) != 0) return -1; + + if (chunk->length == 0 || chunk->data == NULL) return -1; + + int redY = *(((int *)chunk->data)+3); + redY = ntohl(redY); + + return redY / PNG_RATIO; +} + +/* + * Fetch the green point X from the cHRM chunk + * + * Returns: -1 on error + */ +float png_cHRM_greenX(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_CHROMACITY, PNG_CHNK_LEN) != 0) return -1; + + if (chunk->length == 0 || chunk->data == NULL) return -1; + + int greenX = *(((int *)chunk->data)+4); + greenX = ntohl(greenX); + + return greenX / PNG_RATIO; +} + +/* + * Fetch the green point Y from the cHRM chunk + * + * Returns: -1 on error + */ +float png_cHRM_greenY(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_CHROMACITY, PNG_CHNK_LEN) != 0) return -1; + + if (chunk->length == 0 || chunk->data == NULL) return -1; + + int greenY = *(((int *)chunk->data)+5); + greenY = ntohl(greenY); + + return greenY / PNG_RATIO; +} + +/* + * Fetch the blue point X from the cHRM chunk + * + * Returns: -1 on error + */ +float png_cHRM_blueX(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_CHROMACITY, PNG_CHNK_LEN) != 0) return -1; + + if (chunk->length == 0 || chunk->data == NULL) return -1; + + int blueX = *(((int *)chunk->data)+6); + blueX = ntohl(blueX); + + return blueX / PNG_RATIO; +} + +/* + * Fetch the blue point Y from the cHRM chunk + * + * Returns: -1 on error + */ +float png_cHRM_blueY(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_CHROMACITY, PNG_CHNK_LEN) != 0) return -1; + + if (chunk->length == 0 || chunk->data == NULL) return -1; + + int blueY = *(((int *)chunk->data)+7); + blueY = ntohl(blueY); + + return blueY / PNG_RATIO; +} + +/* + * Calculate the image gamma from the gAMA chunk + * + * Returns: -1 on error + */ +float png_gAMA(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_GAMMA, PNG_CHNK_LEN) != 0) return -1; + + if (chunk->length == 0 || chunk->data == NULL) return -1; + + int gamma = *((int *)chunk->data); + gamma = ntohl(gamma); + + return gamma / PNG_RATIO; +} + +/* + * Fetch the raw image gamma from the gAMA chunk (gamma times 100000) + * + * Returns: -1 on error + */ +int png_gAMA_raw(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_GAMMA, PNG_CHNK_LEN) != 0) return -1; + + if (chunk->length == 0 || chunk->data == NULL) return -1; + + int gamma = *((int *)chunk->data); + gamma = ntohl(gamma); + + return gamma; +} + +/* + * Fetch the name of the embedded ICC (International Color Consortium) profile + * + * Returns: NULL on error + * Profile name on success; must be free'd + */ +char *png_iCCP_name(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_ICCP, PNG_CHNK_LEN) != 0) return NULL; + + if (chunk->length == 0 || chunk->data == NULL) return NULL; + + return strdup((char *)chunk->data); +} + +/* + * Fetch the compression method of the embedded ICC (International Color Consortium) profile + * + * Returns: -1 on error + */ +char png_iCCP_method(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_ICCP, PNG_CHNK_LEN) != 0) return -1; + + if (chunk->length == 0 || chunk->data == NULL) return -1; + + int offset = strlen(chunk->data) + 1; + + return *((char *)chunk->data+offset); +} + +/* + * Fetch the compressed embedded ICC (International Color Consortium) profile + * + * Returns: NULL on error + * Datastream containing profile; must be free'd + */ +void *png_iCCP_profile(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_ICCP, PNG_CHNK_LEN) != 0) return NULL; + + if (chunk->length == 0 || chunk->data == NULL) return NULL; + + int offset = strlen(chunk->data) + 2; + int size = chunk->length - offset; + void *profile = malloc(size); + + memcpy(profile, chunk->data + offset, size); + + return profile; +} + +/* + * Fetch the length of the compressed embedded ICC (International Color Consortium) profile + * + * Returns: -1 on error + */ +int png_iCCP_profile_len(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_ICCP, PNG_CHNK_LEN) != 0) return -1; + + if (chunk->length == 0 || chunk->data == NULL) return -1; + + return chunk->length - (strlen(chunk->data) + 2); +} + +/* + * Fetch the significant bits from the sBIT chunk + * + * Returns: NULL on error + */ +char *png_sBIT_get(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_SIGBITS, PNG_CHNK_LEN) != 0) return NULL; + + if (chunk->length == 0 || chunk->data == NULL) return NULL; + + char *bits; + int len; + + switch (png_attr_col_type(chunk->png)) { + case 0: + len = 1; + break; + case 2: + case 3: + len = 3; + break; + case 4: + len = 2; + break; + case 6: + len = 4; + break; + default: + len = -1; + } + + if (len == -1) return NULL; + + bits = (char *) malloc(len); + + memcpy(bits, chunk->data, len); + + return bits; +} + +/* + * Fetch the length of the significant bits array from the sBIT chunk + * + * Returns: -1 on error + */ +int png_sBIT_len(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_SIGBITS, PNG_CHNK_LEN) != 0) return -1; + + if (chunk->length == 0 || chunk->data == NULL) return -1; + + switch (png_attr_col_type(chunk->png)) { + case 0: + return 1; + case 2: + case 3: + return 3; + case 4: + return 2; + case 6: + return 4; + default: + return -1; + } +} + +/* + * Fetch the rendering intent from the sRGB chunk + * + * Returns: -1 on error + */ +char png_sRGB_get(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_SRGB, PNG_CHNK_LEN) != 0) return -1; + + if (chunk->length == 0 || chunk->data == NULL) return -1; + + return *((char *)chunk->data); +} + /* * Extract the pixels per unit along the X axis * diff --git a/formats/png.h b/formats/png.h index 8aecb42..338fd4c 100644 --- a/formats/png.h +++ b/formats/png.h @@ -8,12 +8,18 @@ #define PNG_CHNK_LEN 4 #define PNG_CHNK_TYPE_SZ 4 #define PNG_CHNK_IHDR 0x52444849 +#define PNG_RATIO 100000.0F #define PNG_CHUNK_HEADER "IHDR" #define PNG_CHUNK_PALETTE "PLTE" #define PNG_CHUNK_DATA "IDAT" #define PNG_CHUNK_END "IEND" #define PNG_CHUNK_TRANSPARENCY "tRNS" +#define PNG_CHUNK_CHROMACITY "cHRM" +#define PNG_CHUNK_GAMMA "gAMA" +#define PNG_CHUNK_ICCP "iCCP" +#define PNG_CHUNK_SIGBITS "sBIT" +#define PNG_CHUNK_SRGB "sRGB" #define PNG_CHUNK_PHYS "pHYs" #define PNG_CHUNK_TIME "tIME" #define PNG_CHUNK_TEXT_INT "iTXt" @@ -66,6 +72,28 @@ short *png_tRNS_true(png_chunk_t *); char *png_tRNS_index(png_chunk_t *); int png_tRNS_index_length(png_chunk_t *); +float png_cHRM_whiteX(png_chunk_t *); +float png_cHRM_whiteY(png_chunk_t *); +float png_cHRM_redX(png_chunk_t *); +float png_cHRM_redY(png_chunk_t *); +float png_cHRM_greenX(png_chunk_t *); +float png_cHRM_greenY(png_chunk_t *); +float png_cHRM_blueX(png_chunk_t *); +float png_cHRM_blueY(png_chunk_t *); + +float png_gAMA(png_chunk_t *); +int png_gAMA_raw(png_chunk_t *); + +char *png_iCCP_name(png_chunk_t *); +char png_iCCP_method(png_chunk_t *); +void *png_iCCP_profile(png_chunk_t *); +int png_iCCP_profile_len(png_chunk_t *); + +char *png_sBIT_get(png_chunk_t *); +int png_sBIT_len(png_chunk_t *); + +char png_sRGB_get(png_chunk_t *); + int png_pHYs_ppuX(png_chunk_t *); int png_pHYs_ppuY(png_chunk_t *); char png_pHYs_unit(png_chunk_t *); diff --git a/main.c b/main.c index b365e69..064dd43 100644 --- a/main.c +++ b/main.c @@ -106,6 +106,70 @@ void handle_png(Format_PNG png) { } else { printf(" [Error]: Invalid color type %d\n", type); } + } else if (strncmp(chunk->type, PNG_CHUNK_CHROMACITY, PNG_CHNK_LEN) == 0) { + printf(" Chromacities:\n"); + printf(" White point (x): %.5f\n" + " White point (y): %.5f\n" + " Red (x): %.5f\n" + " Red (y): %.5f\n" + " Green (x): %.5f\n" + " Green (y): %.5f\n" + " Blue (x): %.5f\n" + " Blue (y): %.5f\n", + png_cHRM_whiteX(chunk), + png_cHRM_whiteX(chunk), + png_cHRM_redX(chunk), + png_cHRM_redY(chunk), + png_cHRM_greenX(chunk), + png_cHRM_greenY(chunk), + png_cHRM_blueX(chunk), + png_cHRM_blueY(chunk)); + } else if (strncmp(chunk->type, PNG_CHUNK_GAMMA, PNG_CHNK_LEN) == 0) { + printf(" Gamma: %.5f (0x%08x)\n", png_gAMA(chunk), png_gAMA_raw(chunk)); + } else if (strncmp(chunk->type, PNG_CHUNK_ICCP, PNG_CHNK_LEN) == 0) { + char *profile = png_iCCP_name(chunk); + + printf(" Profile: '%s' (%d bytes, %s compression)\n", profile, png_iCCP_profile_len(chunk), png_iCCP_method(chunk) ? "unknown" : "zlib"); + + free(profile); + } else if (strncmp(chunk->type, PNG_CHUNK_SIGBITS, PNG_CHNK_LEN) == 0) { + int type = png_attr_col_type(png); + char *bits = png_sBIT_get(chunk); + + if (type == 0 || type == 4) { + printf(" Grayscale: 0x%hhx\n", bits[0]); + } else if (type == 2 || type == 3 || type == 6) { + printf(" Red: 0x%hhx\n" + " Green: 0x%hhx\n" + " Blue: 0x%hhx\n", + bits[0], bits[1], bits[2]); + } + if (type == 4 || type == 6) { + printf(" Alpha: 0x%hhx\n", bits[type == 4 ? 2 : 3]); + } + + free(bits); + } else if (strncmp(chunk->type, PNG_CHUNK_SRGB, PNG_CHNK_LEN) == 0) { + char intent = png_sRGB_get(chunk); + char *s = "error"; + + switch (intent) { + case 0: + s = "perceptual"; + break; + case 1: + s = "relative colorimetric"; + break; + case 2: + s = "saturation"; + break; + case 3: + s = "absolute colorimetric"; + break; + } + + printf(" Rendering intent: %s\n", s); + } } From 4766057052f3ac14746793ca75455f75897abc82 Mon Sep 17 00:00:00 2001 From: Tyler Senter Date: Wed, 27 May 2020 22:12:10 -0400 Subject: [PATCH 19/23] refactor(PNG): Change function order to match IS --- CHANGELOG.md | 4 ++ formats/png.c | 118 +++++++++++++++++++++++++------------------------- formats/png.h | 16 +++---- 3 files changed, 71 insertions(+), 67 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 144f980..2242cb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ Incremental support for PNG image types is being added +### v0.1.5a + +* Refactor functions to more accurately represent chunk order, as defined in the International Standard + ### v0.1.5 * Added support for color space information chunks: diff --git a/formats/png.c b/formats/png.c index ffea70f..f32766c 100644 --- a/formats/png.c +++ b/formats/png.c @@ -654,46 +654,59 @@ char png_sRGB_get(png_chunk_t *chunk) { } /* - * Extract the pixels per unit along the X axis + * Extract the keyword from an iTXt chunk * - * Returns: -1 on error - * pixels per unit on success + * Returns: NULL on error + * keyword on success */ -int png_pHYs_ppuX(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_PHYS, PNG_CHNK_LEN) != 0) return -1; - - if (chunk->length < 4) return -1; +char *png_iTXt_keyword(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_TEXT_INT, PNG_CHNK_LEN) != 0) return NULL; - return ntohl(*((int *)chunk->data)); + return strdup((char *) chunk->data); } /* - * Extract the pixels per unit along the Y axis + * Extract the language tag from an iTXt chunk * - * Returns: -1 on error - * pixels per unit on success + * Returns: NULL on error + * Empty string if no language tag available; must be free'd + * string containing ISO 646 hyphen-separated words; must be free'd */ -int png_pHYs_ppuY(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_PHYS, PNG_CHNK_LEN) != 0) return -1; +char *png_iTXt_lang(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_TEXT_INT, PNG_CHNK_LEN) != 0) return NULL; - if (chunk->length < 8) return -1; + if (chunk->length <= strlen(chunk->data) + 3) return NULL; - return ntohl(*((int *)(chunk->data + 4))); + return strdup(chunk->data + strlen(chunk->data) + 3); } /* - * Extract the unit for pixels per unit + * Extract the text value from an iTXt chunk * - * Returns: -1 on error - * 0 for unknown unit - * 1 for meter + * Returns: NULL on error + * string containing text from an iTXt chunk on success; must be free'd */ -char png_pHYs_unit(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_PHYS, PNG_CHNK_LEN) != 0) return -1; +char *png_iTXt_text(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_TEXT_INT, PNG_CHNK_LEN) != 0) return NULL; - if (chunk->length < 9) return -1; + int c = strlen(chunk->data) + 3; /* Keyword + comp. flag + comp. method */ + char *p = chunk->data + c; + char *buf; - return *((char *) chunk->data + 9); + c += strlen(p) + 1; /* Language tag */ + p = chunk->data + c; + + c += strlen(p) + 1; /* Translated keyword */ + p = chunk->data + c; + + c = chunk->length - c; + + buf = (char *) malloc(sizeof(char) * (c + 1)); + + strncpy(buf, p, c); + buf[c] = 0; + + return buf; } /* @@ -787,59 +800,46 @@ int png_zTXt_length(png_chunk_t *chunk) { } /* - * Extract the keyword from an iTXt chunk + * Extract the pixels per unit along the X axis * - * Returns: NULL on error - * keyword on success + * Returns: -1 on error + * pixels per unit on success */ -char *png_iTXt_keyword(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_TEXT_INT, PNG_CHNK_LEN) != 0) return NULL; +int png_pHYs_ppuX(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_PHYS, PNG_CHNK_LEN) != 0) return -1; - return strdup((char *) chunk->data); + if (chunk->length < 4) return -1; + + return ntohl(*((int *)chunk->data)); } /* - * Extract the language tag from an iTXt chunk + * Extract the pixels per unit along the Y axis * - * Returns: NULL on error - * Empty string if no language tag available; must be free'd - * string containing ISO 646 hyphen-separated words; must be free'd + * Returns: -1 on error + * pixels per unit on success */ -char *png_iTXt_lang(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_TEXT_INT, PNG_CHNK_LEN) != 0) return NULL; +int png_pHYs_ppuY(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_PHYS, PNG_CHNK_LEN) != 0) return -1; - if (chunk->length <= strlen(chunk->data) + 3) return NULL; + if (chunk->length < 8) return -1; - return strdup(chunk->data + strlen(chunk->data) + 3); + return ntohl(*((int *)(chunk->data + 4))); } /* - * Extract the text value from an iTXt chunk + * Extract the unit for pixels per unit * - * Returns: NULL on error - * string containing text from an iTXt chunk on success; must be free'd + * Returns: -1 on error + * 0 for unknown unit + * 1 for meter */ -char *png_iTXt_text(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_TEXT_INT, PNG_CHNK_LEN) != 0) return NULL; - - int c = strlen(chunk->data) + 3; /* Keyword + comp. flag + comp. method */ - char *p = chunk->data + c; - char *buf; - - c += strlen(p) + 1; /* Language tag */ - p = chunk->data + c; - - c += strlen(p) + 1; /* Translated keyword */ - p = chunk->data + c; - - c = chunk->length - c; - - buf = (char *) malloc(sizeof(char) * (c + 1)); +char png_pHYs_unit(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_PHYS, PNG_CHNK_LEN) != 0) return -1; - strncpy(buf, p, c); - buf[c] = 0; + if (chunk->length < 9) return -1; - return buf; + return *((char *) chunk->data + 9); } /* diff --git a/formats/png.h b/formats/png.h index 338fd4c..597aa96 100644 --- a/formats/png.h +++ b/formats/png.h @@ -20,11 +20,11 @@ #define PNG_CHUNK_ICCP "iCCP" #define PNG_CHUNK_SIGBITS "sBIT" #define PNG_CHUNK_SRGB "sRGB" -#define PNG_CHUNK_PHYS "pHYs" -#define PNG_CHUNK_TIME "tIME" #define PNG_CHUNK_TEXT_INT "iTXt" #define PNG_CHUNK_TEXT "tEXt" #define PNG_CHUNK_TEXT_COMPRESSED "zTXt" +#define PNG_CHUNK_PHYS "pHYs" +#define PNG_CHUNK_TIME "tIME" #define PNG_ISCRITICAL(chunk) (chunk->type[0] & 0x20 != 0) #define PNG_ISANCILLARY(chunk) (chunk->type[0] & 0x20 == 0) @@ -94,9 +94,9 @@ int png_sBIT_len(png_chunk_t *); char png_sRGB_get(png_chunk_t *); -int png_pHYs_ppuX(png_chunk_t *); -int png_pHYs_ppuY(png_chunk_t *); -char png_pHYs_unit(png_chunk_t *); +char *png_iTXt_keyword(png_chunk_t *); +char *png_iTXt_lang(png_chunk_t *); +char *png_iTXt_text(png_chunk_t *); char *png_tEXt_keyword(png_chunk_t *); char *png_tEXt_text(png_chunk_t *); @@ -106,9 +106,9 @@ char png_zTXt_method(png_chunk_t *); void *png_zTXt_data(png_chunk_t *); int png_zTXt_length(png_chunk_t *); -char *png_iTXt_keyword(png_chunk_t *); -char *png_iTXt_lang(png_chunk_t *); -char *png_iTXt_text(png_chunk_t *); +int png_pHYs_ppuX(png_chunk_t *); +int png_pHYs_ppuY(png_chunk_t *); +char png_pHYs_unit(png_chunk_t *); short png_tIME_year(png_chunk_t *); char png_tIME_month(png_chunk_t *); From e8844ba37907b150029196a45e2a1b279c3bd920 Mon Sep 17 00:00:00 2001 From: Tyler Senter Date: Wed, 27 May 2020 22:13:49 -0400 Subject: [PATCH 20/23] refactor: Update output spacing to be uniform across chunk types --- main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main.c b/main.c index 064dd43..7bf05c4 100644 --- a/main.c +++ b/main.c @@ -53,7 +53,7 @@ void handle_png(Format_PNG png) { if (iv_opts.deep) { if (strncmp(chunk->type, PNG_CHUNK_TEXT_INT, 4) == 0) { char *text = png_iTXt_keyword(chunk); - printf(" %s", text); free(text); + printf(" %s", text); free(text); text = png_iTXt_text(chunk); printf(": %s", text); free(text); @@ -65,7 +65,7 @@ void handle_png(Format_PNG png) { free(text); } else if (strncmp(chunk->type, PNG_CHUNK_TEXT, PNG_CHNK_LEN) == 0) { char *text = png_tEXt_keyword(chunk); - printf(" %s", text); free(text); + printf(" %s", text); free(text); text = png_tEXt_text(chunk); printf(": %s\n", text); free(text); } else if (strncmp(chunk->type, PNG_CHUNK_TIME, PNG_CHNK_LEN) == 0) { From 2b998dd0c9dad416ed316706729d3ebbc0ebe0fa Mon Sep 17 00:00:00 2001 From: Tyler Senter Date: Wed, 3 Jun 2020 20:16:29 -0400 Subject: [PATCH 21/23] feat(PNG): Add support for sPLT chunks --- CHANGELOG.md | 7 ++ formats/png.c | 230 ++++++++++++++++++++++++++++++++++++++++++++++++ formats/png.h | 27 ++++++ main.c | 14 +++ utils/version.h | 4 +- 5 files changed, 280 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2242cb6..d487298 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,13 @@ Incremental support for PNG image types is being added +### v0.1.6 + +* Added support for remaining miscellaneous chunk types + * `bKGD` chunks can be processed through the `png_bKGD_*()` functions + * `hIST` chunks can be processed through the `png_hIST_*()` functions + * `sPLT` chunks can be processed through the `png_sPLT_*()` functions + ### v0.1.5a * Refactor functions to more accurately represent chunk order, as defined in the International Standard diff --git a/formats/png.c b/formats/png.c index f32766c..a854628 100644 --- a/formats/png.c +++ b/formats/png.c @@ -799,6 +799,114 @@ int png_zTXt_length(png_chunk_t *chunk) { return chunk->length - (strlen(chunk->data) + 2); } +/* + * Fetch the background color information of the image + * + * Returns: NULL on error + * Array of values corresponding to appropriate background color channels; must be free'd + */ +void *png_bKGD_get(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_BACKGROUND, PNG_CHNK_LEN) != 0) return NULL; + + if (chunk->length == 0 || chunk->data == NULL) return NULL; + + int type = png_attr_col_type(chunk->png); + void *array = NULL; + + if (type == 0 || type == 4) { + array = malloc(sizeof(short)); + + short s = *((short *)chunk->data); + ((short *)array)[0] = ntohs(s); + } else if (type == 2 || type == 6) { + array = malloc(sizeof(short) * 3); + + short *s = (short *)chunk->data; + ((short *)array)[0] = ntohs(s[0]); + ((short *)array)[1] = ntohs(s[1]); + ((short *)array)[2] = ntohs(s[2]); + } else if (type == 3) { + array == malloc(sizeof(char)); + + ((char *)array)[0] = *((char *)chunk->data); + } + + return array; +} + +/* + * Fetch the length of the array returned by `png_bKGD_get()`; this is the length of the array in bytes, not elements + * + * Returns: -1 on error + */ +int png_bKGD_len(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_BACKGROUND, PNG_CHNK_LEN) != 0) return -1; + + if (chunk->length == 0 || chunk->data == NULL) return -1; + + switch (png_attr_col_type(chunk->png)) { + case 0: + case 4: + return 2; + case 2: + case 6: + return 6; + case 3: + return 1; + default: + return -1; + } +} + +/* + * Fetch the histogram of the image + * + * Returns: NULL on error + * Array of values representing the frequencies of the colors contained in the PLTE chunk; must be free'd + */ +unsigned short *png_hIST_get(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_HISTOGRAM, PNG_CHNK_LEN) != 0) return NULL; + + if (chunk->length == 0 || chunk->data == NULL) return NULL; + + switch (png_attr_col_type(chunk->png)) { + case 2: + case 3: + case 6: + break; + default: + return NULL; + } + + unsigned short *array = (unsigned short *) malloc(chunk->length); + + memcpy(array, chunk->data, chunk->length); + + return array; +} + +/* + * Fetch the length of the histogram of the image + * + * Returns: -1 on error + */ +int png_hIST_len(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_HISTOGRAM, PNG_CHNK_LEN) != 0) return -1; + + if (chunk->length == 0 || chunk->data == NULL) return -1; + + switch (png_attr_col_type(chunk->png)) { + case 2: + case 3: + case 6: + break; + default: + return -1; + } + + return chunk->length / sizeof(unsigned short); +} + /* * Extract the pixels per unit along the X axis * @@ -842,6 +950,128 @@ char png_pHYs_unit(png_chunk_t *chunk) { return *((char *) chunk->data + 9); } +/* + * Fetch the name of the suggested palette + * + * Returns: NULL on error + * Name of the palette; must be free'd + */ +char *png_sPLT_name(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_SPALETTE, PNG_CHNK_LEN) != 0) return NULL; + + if (chunk->length == 0 || chunk->data == NULL) return NULL; + + return strdup(chunk->data); +} + +/* + * Fetch the sample depth of the suggested palette + * + * Returns: -1 on error + */ +char png_sPLT_sample_depth(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_SPALETTE, PNG_CHNK_LEN) != 0) return -1; + + if (chunk->length == 0 || chunk->data == NULL) return -1; + + int offset = strlen(chunk->data) + 1; + + return *((char *)chunk->data + offset); +} + +/* + * Fetch an array of entries in the suggested palette + * + * Returns: NULL on error + * Array of entries on success; must be free'd + */ +palette_entry_t *png_sPLT_entries(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_SPALETTE, PNG_CHNK_LEN) != 0) return NULL; + + if (chunk->length == 0 || chunk->data == NULL) return NULL; + + int offset = strlen(chunk->data) + 2, i; + int size = chunk->length - offset, entry_size = 10; + int num_entries; + void *data = chunk->data + offset; + + if (size % 6 == 0) { + num_entries = size / 6; + size = 6; + } else if (size % 10 == 0) { + num_entries = size / 10; + size = 10; + } else { + return NULL; + } + + palette_entry_t *entries = (palette_entry_t *) malloc(sizeof(palette_entry_t) * num_entries); + + if (size == 6) { + for (i = 0; i < num_entries; i++) { + entries[i].red = *((char *)(data + (i * size + 0))); + entries[i].green = *((char *)(data + (i * size + 1))); + entries[i].blue = *((char *)(data + (i * size + 2))); + entries[i].alpha = *((char *)(data + (i * size + 3))); + entries[i].frequency = ntohs(*((short *)(data + (i * size + 4)))); + } + } else { + for (i = 0; i < num_entries; i++) { + entries[i].red = ntohs(*((short *)(data + (i * size + 0)))); + entries[i].green = ntohs(*((short *)(data + (i * size + 2)))); + entries[i].blue = ntohs(*((short *)(data + (i * size + 4)))); + entries[i].alpha = ntohs(*((short *)(data + (i * size + 6)))); + entries[i].frequency = ntohs(*((short *)(data + (i * size + 8)))); + } + } + + return entries; +} + +/* + * Fetch the number of entries in the suggested palette + * + * Returns: -1 on error + */ +int png_sPLT_entries_len(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_SPALETTE, PNG_CHNK_LEN) != 0) return -1; + + if (chunk->length == 0 || chunk->data == NULL) return -1; + + int size = chunk->length - (strlen(chunk->data) + 2); + + if (size % 6 == 0) { + return size / 6; + } else if (size % 10 == 0) { + return size / 10; + } else { + return -1; + } +} + +/* + * Fetch the minimum amount of space (in bytes) required to store one entry in the suggested palette + * + * Returns: -1 on error + * 6 if the rgba channels can be stored in one byte + * 10 if the rgba channels can be stored in two bytes + */ +int png_sPLT_entry_size(png_chunk_t *chunk) { + if (strncmp(chunk->type, PNG_CHUNK_SPALETTE, PNG_CHNK_LEN) != 0) return -1; + + if (chunk->length == 0 || chunk->data == NULL) return -1; + + int sample_depth = *((int *) (chunk->data + strlen(chunk->data) + 1)); + + if (sample_depth == 8) { + return 6; + } else if (sample_depth == 16) { + return 10; + } else { + return -1; + } +} + /* * Extract the year from the last modified date * diff --git a/formats/png.h b/formats/png.h index 597aa96..3890fcb 100644 --- a/formats/png.h +++ b/formats/png.h @@ -23,12 +23,27 @@ #define PNG_CHUNK_TEXT_INT "iTXt" #define PNG_CHUNK_TEXT "tEXt" #define PNG_CHUNK_TEXT_COMPRESSED "zTXt" +#define PNG_CHUNK_BACKGROUND "bKGD" +#define PNG_CHUNK_HISTOGRAM "hIST" #define PNG_CHUNK_PHYS "pHYs" +#define PNG_CHUNK_SPALETTE "sPLT" #define PNG_CHUNK_TIME "tIME" #define PNG_ISCRITICAL(chunk) (chunk->type[0] & 0x20 != 0) #define PNG_ISANCILLARY(chunk) (chunk->type[0] & 0x20 == 0) +typedef unsigned char byte; +typedef unsigned short ushort; +typedef unsigned int uint; + +typedef struct palette_entry { + ushort red; + ushort green; + ushort blue; + ushort alpha; + ushort frequency; +} palette_entry_t; + typedef struct png_chunk { unsigned int length; char type[4]; @@ -106,10 +121,22 @@ char png_zTXt_method(png_chunk_t *); void *png_zTXt_data(png_chunk_t *); int png_zTXt_length(png_chunk_t *); +void *png_bKGD_get(png_chunk_t *); +int png_bKGD_len(png_chunk_t *); + +unsigned short *png_hIST_get(png_chunk_t *); +int png_hIST_len(png_chunk_t *); + int png_pHYs_ppuX(png_chunk_t *); int png_pHYs_ppuY(png_chunk_t *); char png_pHYs_unit(png_chunk_t *); +char *png_sPLT_name(png_chunk_t *); +char png_sPLT_sample_depth(png_chunk_t *); +palette_entry_t *png_sPLT_entries(png_chunk_t *); +int png_sPLT_entries_len(png_chunk_t *); +int png_sPLT_entry_size(png_chunk_t *); + short png_tIME_year(png_chunk_t *); char png_tIME_month(png_chunk_t *); char png_tIME_day(png_chunk_t *); diff --git a/main.c b/main.c index 7bf05c4..ba5c0f7 100644 --- a/main.c +++ b/main.c @@ -170,6 +170,20 @@ void handle_png(Format_PNG png) { printf(" Rendering intent: %s\n", s); + } else if (strncmp(chunk->type, PNG_CHUNK_BACKGROUND, PNG_CHNK_LEN) == 0) { + int size = png_bKGD_len(chunk); + void *array = png_bKGD_get(chunk); + short *s = (short *) array; + + if (size == 2) { + printf(" Grayscale: 0x%hx\n", s[0]); + } else if (size == 6) { + printf(" HTML Code: 0x%hx%hx%hx\n", s[0], s[1], s[2]); + } else if (size == 1) { + printf(" Palette index: %hhd\n", *((char *)array)); + } + + free(array); } } diff --git a/utils/version.h b/utils/version.h index 31bef0f..1dbf527 100644 --- a/utils/version.h +++ b/utils/version.h @@ -10,7 +10,7 @@ #endif /* Version */ -#define IV_VERSION "v0.0.1" -#define IV_UPDATED "2020-05-10" +#define IV_VERSION "v0.1.6" +#define IV_UPDATED "2020-06-03" #endif \ No newline at end of file From 40431e0a9047715d313664a78b415cd89d0cd595 Mon Sep 17 00:00:00 2001 From: Tyler Senter Date: Wed, 3 Jun 2020 20:17:03 -0400 Subject: [PATCH 22/23] chore(PNG): Remove unused include --- formats/png.h | 1 - 1 file changed, 1 deletion(-) diff --git a/formats/png.h b/formats/png.h index 3890fcb..025f0a8 100644 --- a/formats/png.h +++ b/formats/png.h @@ -1,7 +1,6 @@ #ifndef __IV_FORMAT_PNG #define __IV_FORMAT_PNG #include -#include "../utils/types.h" #define PNG_SIG 0x0A1A0A0D474E5089 #define PNG_SIG_SZ 8 From 18dee5057583f24136956fa63ddbc1293da6f661 Mon Sep 17 00:00:00 2001 From: Tyler Senter Date: Wed, 3 Jun 2020 20:36:34 -0400 Subject: [PATCH 23/23] refactor(PNG): Extract chunk type validation to macro --- CHANGELOG.md | 5 ++ formats/png.c | 190 ++++++++++++++---------------------------------- formats/png.h | 2 + utils/version.h | 2 +- 4 files changed, 61 insertions(+), 138 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d487298..c797814 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ Incremental support for PNG image types is being added +### v0.1.7 + +* Extract chunk validation to macro + * Instead of validating the chunk type (and data) manually, the `PNG_VALIDATE(chunkType, defaultReturn)` macro should be used + ### v0.1.6 * Added support for remaining miscellaneous chunk types diff --git a/formats/png.c b/formats/png.c index a854628..ae689d5 100644 --- a/formats/png.c +++ b/formats/png.c @@ -178,9 +178,7 @@ void png_chunk_free(png_chunk_t *chunk) { * (char **) -1 if chunk length is not divisible by 3 (required by Internation Standard section 11.2.3) */ char **png_PLTE_get(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_PALETTE, PNG_CHNK_LEN) != 0) return NULL; - - if (chunk->length == 0 || chunk->data == NULL) return NULL; + PNG_VALIDATE(PNG_CHUNK_PALETTE, NULL); int n = chunk->length % 3, i; @@ -207,9 +205,7 @@ char **png_PLTE_get(png_chunk_t *chunk) { * */ int png_PLTE_length(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_PALETTE, PNG_CHNK_LEN) != 0) return -1; - - if (chunk->length == 0 || chunk->data == NULL) return -1; + PNG_VALIDATE(PNG_CHUNK_PALETTE, -1); if (chunk->length % 3 != 0) return 0; @@ -238,9 +234,7 @@ void png_PLTE_free(char **palette, int len) { * Returns: NULL on error */ void *png_IDAT_get(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_DATA, PNG_CHNK_LEN) != 0) return NULL; - - if (chunk->length == 0 || chunk->data == NULL) return NULL; + PNG_VALIDATE(PNG_CHUNK_DATA, NULL); void *data = malloc(chunk->length); @@ -255,9 +249,7 @@ void *png_IDAT_get(png_chunk_t *chunk) { * Returns: -1 on error */ int png_IDAT_length(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_DATA, PNG_CHNK_LEN) != 0) return -1; - - if (chunk->length == 0 || chunk->data == NULL) return -1; + PNG_VALIDATE(PNG_CHUNK_DATA, -1); return chunk->length; } @@ -282,9 +274,7 @@ int png_IEND_valid(png_chunk_t *chunk) { * Returns: -1 on error (image could be of the wrong color type) */ short png_tRNS_gray(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_TRANSPARENCY, PNG_CHNK_LEN) != 0) return -1; - - if (chunk->length == 0 || chunk->data == NULL) return -1; + PNG_VALIDATE(PNG_CHUNK_TRANSPARENCY, -1); if (png_attr_col_type(chunk->png) != 0) return -1; @@ -298,9 +288,7 @@ short png_tRNS_gray(png_chunk_t *chunk) { * Array of 3 sample values (R, G, B); must be free'd */ short *png_tRNS_true(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_TRANSPARENCY, PNG_CHNK_LEN) != 0) return NULL; - - if (chunk->length == 0 || chunk->data == NULL) return NULL; + PNG_VALIDATE(PNG_CHUNK_TRANSPARENCY, NULL); if (png_attr_col_type(chunk->png) != 2) return NULL; @@ -321,9 +309,7 @@ short *png_tRNS_true(png_chunk_t *chunk) { * Array of alpha channel for palette indexes; must be free'd */ char *png_tRNS_index(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_TRANSPARENCY, PNG_CHNK_LEN) != 0) return NULL; - - if (chunk->length == 0 || chunk->data == NULL) return NULL; + PNG_VALIDATE(PNG_CHUNK_TRANSPARENCY, NULL); if (png_attr_col_type(chunk->png) != 3) return NULL; @@ -344,9 +330,7 @@ char *png_tRNS_index(png_chunk_t *chunk) { * Length of array returned by `png_tRNS_index()` */ int png_tRNS_index_length(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_TRANSPARENCY, PNG_CHNK_LEN) != 0) return -1; - - if (chunk->length == 0 || chunk->data == NULL) return -1; + PNG_VALIDATE(PNG_CHUNK_TRANSPARENCY, -1); if (png_attr_col_type(chunk->png) != 3) return -1; @@ -359,9 +343,7 @@ int png_tRNS_index_length(png_chunk_t *chunk) { * Returns: -1 on error */ float png_cHRM_whiteX(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_CHROMACITY, PNG_CHNK_LEN) != 0) return -1; - - if (chunk->length == 0 || chunk->data == NULL) return -1; + PNG_VALIDATE(PNG_CHUNK_CHROMACITY, -1); int whiteX = *((int *) chunk->data); whiteX = ntohl(whiteX); @@ -375,9 +357,7 @@ float png_cHRM_whiteX(png_chunk_t *chunk) { * Returns: -1 on error */ float png_cHRM_whiteY(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_CHROMACITY, PNG_CHNK_LEN) != 0) return -1; - - if (chunk->length == 0 || chunk->data == NULL) return -1; + PNG_VALIDATE(PNG_CHUNK_CHROMACITY, -1); int whiteY = *(((int *)chunk->data)+1); whiteY = ntohl(whiteY); @@ -391,9 +371,7 @@ float png_cHRM_whiteY(png_chunk_t *chunk) { * Returns: -1 on error */ float png_cHRM_redX(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_CHROMACITY, PNG_CHNK_LEN) != 0) return -1; - - if (chunk->length == 0 || chunk->data == NULL) return -1; + PNG_VALIDATE(PNG_CHUNK_CHROMACITY, -1); int redX = *(((int *)chunk->data)+2); redX = ntohl(redX); @@ -407,9 +385,7 @@ float png_cHRM_redX(png_chunk_t *chunk) { * Returns: -1 on error */ float png_cHRM_redY(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_CHROMACITY, PNG_CHNK_LEN) != 0) return -1; - - if (chunk->length == 0 || chunk->data == NULL) return -1; + PNG_VALIDATE(PNG_CHUNK_CHROMACITY, -1); int redY = *(((int *)chunk->data)+3); redY = ntohl(redY); @@ -423,9 +399,7 @@ float png_cHRM_redY(png_chunk_t *chunk) { * Returns: -1 on error */ float png_cHRM_greenX(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_CHROMACITY, PNG_CHNK_LEN) != 0) return -1; - - if (chunk->length == 0 || chunk->data == NULL) return -1; + PNG_VALIDATE(PNG_CHUNK_CHROMACITY, -1); int greenX = *(((int *)chunk->data)+4); greenX = ntohl(greenX); @@ -439,9 +413,7 @@ float png_cHRM_greenX(png_chunk_t *chunk) { * Returns: -1 on error */ float png_cHRM_greenY(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_CHROMACITY, PNG_CHNK_LEN) != 0) return -1; - - if (chunk->length == 0 || chunk->data == NULL) return -1; + PNG_VALIDATE(PNG_CHUNK_CHROMACITY, -1); int greenY = *(((int *)chunk->data)+5); greenY = ntohl(greenY); @@ -455,9 +427,7 @@ float png_cHRM_greenY(png_chunk_t *chunk) { * Returns: -1 on error */ float png_cHRM_blueX(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_CHROMACITY, PNG_CHNK_LEN) != 0) return -1; - - if (chunk->length == 0 || chunk->data == NULL) return -1; + PNG_VALIDATE(PNG_CHUNK_CHROMACITY, -1); int blueX = *(((int *)chunk->data)+6); blueX = ntohl(blueX); @@ -471,9 +441,7 @@ float png_cHRM_blueX(png_chunk_t *chunk) { * Returns: -1 on error */ float png_cHRM_blueY(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_CHROMACITY, PNG_CHNK_LEN) != 0) return -1; - - if (chunk->length == 0 || chunk->data == NULL) return -1; + PNG_VALIDATE(PNG_CHUNK_CHROMACITY, -1); int blueY = *(((int *)chunk->data)+7); blueY = ntohl(blueY); @@ -487,9 +455,7 @@ float png_cHRM_blueY(png_chunk_t *chunk) { * Returns: -1 on error */ float png_gAMA(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_GAMMA, PNG_CHNK_LEN) != 0) return -1; - - if (chunk->length == 0 || chunk->data == NULL) return -1; + PNG_VALIDATE(PNG_CHUNK_GAMMA, -1); int gamma = *((int *)chunk->data); gamma = ntohl(gamma); @@ -503,9 +469,7 @@ float png_gAMA(png_chunk_t *chunk) { * Returns: -1 on error */ int png_gAMA_raw(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_GAMMA, PNG_CHNK_LEN) != 0) return -1; - - if (chunk->length == 0 || chunk->data == NULL) return -1; + PNG_VALIDATE(PNG_CHUNK_GAMMA, -1); int gamma = *((int *)chunk->data); gamma = ntohl(gamma); @@ -520,9 +484,7 @@ int png_gAMA_raw(png_chunk_t *chunk) { * Profile name on success; must be free'd */ char *png_iCCP_name(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_ICCP, PNG_CHNK_LEN) != 0) return NULL; - - if (chunk->length == 0 || chunk->data == NULL) return NULL; + PNG_VALIDATE(PNG_CHUNK_ICCP, NULL); return strdup((char *)chunk->data); } @@ -533,9 +495,7 @@ char *png_iCCP_name(png_chunk_t *chunk) { * Returns: -1 on error */ char png_iCCP_method(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_ICCP, PNG_CHNK_LEN) != 0) return -1; - - if (chunk->length == 0 || chunk->data == NULL) return -1; + PNG_VALIDATE(PNG_CHUNK_ICCP, -1); int offset = strlen(chunk->data) + 1; @@ -549,9 +509,7 @@ char png_iCCP_method(png_chunk_t *chunk) { * Datastream containing profile; must be free'd */ void *png_iCCP_profile(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_ICCP, PNG_CHNK_LEN) != 0) return NULL; - - if (chunk->length == 0 || chunk->data == NULL) return NULL; + PNG_VALIDATE(PNG_CHUNK_ICCP, NULL); int offset = strlen(chunk->data) + 2; int size = chunk->length - offset; @@ -568,9 +526,7 @@ void *png_iCCP_profile(png_chunk_t *chunk) { * Returns: -1 on error */ int png_iCCP_profile_len(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_ICCP, PNG_CHNK_LEN) != 0) return -1; - - if (chunk->length == 0 || chunk->data == NULL) return -1; + PNG_VALIDATE(PNG_CHUNK_ICCP, -1); return chunk->length - (strlen(chunk->data) + 2); } @@ -581,9 +537,7 @@ int png_iCCP_profile_len(png_chunk_t *chunk) { * Returns: NULL on error */ char *png_sBIT_get(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_SIGBITS, PNG_CHNK_LEN) != 0) return NULL; - - if (chunk->length == 0 || chunk->data == NULL) return NULL; + PNG_VALIDATE(PNG_CHUNK_SIGBITS, NULL); char *bits; int len; @@ -621,9 +575,7 @@ char *png_sBIT_get(png_chunk_t *chunk) { * Returns: -1 on error */ int png_sBIT_len(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_SIGBITS, PNG_CHNK_LEN) != 0) return -1; - - if (chunk->length == 0 || chunk->data == NULL) return -1; + PNG_VALIDATE(PNG_CHUNK_SIGBITS, -1); switch (png_attr_col_type(chunk->png)) { case 0: @@ -646,9 +598,7 @@ int png_sBIT_len(png_chunk_t *chunk) { * Returns: -1 on error */ char png_sRGB_get(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_SRGB, PNG_CHNK_LEN) != 0) return -1; - - if (chunk->length == 0 || chunk->data == NULL) return -1; + PNG_VALIDATE(PNG_CHUNK_SRGB, -1); return *((char *)chunk->data); } @@ -660,7 +610,7 @@ char png_sRGB_get(png_chunk_t *chunk) { * keyword on success */ char *png_iTXt_keyword(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_TEXT_INT, PNG_CHNK_LEN) != 0) return NULL; + PNG_VALIDATE(PNG_CHUNK_TEXT_INT, NULL); return strdup((char *) chunk->data); } @@ -673,9 +623,7 @@ char *png_iTXt_keyword(png_chunk_t *chunk) { * string containing ISO 646 hyphen-separated words; must be free'd */ char *png_iTXt_lang(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_TEXT_INT, PNG_CHNK_LEN) != 0) return NULL; - - if (chunk->length <= strlen(chunk->data) + 3) return NULL; + PNG_VALIDATE(PNG_CHUNK_TEXT_INT, NULL); return strdup(chunk->data + strlen(chunk->data) + 3); } @@ -687,7 +635,7 @@ char *png_iTXt_lang(png_chunk_t *chunk) { * string containing text from an iTXt chunk on success; must be free'd */ char *png_iTXt_text(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_TEXT_INT, PNG_CHNK_LEN) != 0) return NULL; + PNG_VALIDATE(PNG_CHUNK_TEXT_INT, NULL); int c = strlen(chunk->data) + 3; /* Keyword + comp. flag + comp. method */ char *p = chunk->data + c; @@ -716,7 +664,7 @@ char *png_iTXt_text(png_chunk_t *chunk) { * keyword on success; must be free'd */ char *png_tEXt_keyword(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_TEXT, PNG_CHNK_LEN) != 0) return NULL; + PNG_VALIDATE(PNG_CHUNK_TEXT, NULL); return strdup(chunk->data); } @@ -728,7 +676,7 @@ char *png_tEXt_keyword(png_chunk_t *chunk) { * keyword on success; must be free'd */ char *png_tEXt_text(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_TEXT, PNG_CHNK_LEN) != 0) return NULL; + PNG_VALIDATE(PNG_CHUNK_TEXT, NULL); int c = strlen(chunk->data) + 1; char *text = (char *) malloc(chunk->length - c + 1); @@ -746,9 +694,7 @@ char *png_tEXt_text(png_chunk_t *chunk) { * keyword on success; must be free'd */ char *png_zTXt_keyword(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_TEXT, PNG_CHNK_LEN) != 0) return NULL; - - if (chunk->data == NULL || chunk->length == 0) return NULL; + PNG_VALIDATE(PNG_CHUNK_TEXT, NULL); return strdup(chunk->data); } @@ -760,9 +706,7 @@ char *png_zTXt_keyword(png_chunk_t *chunk) { * compression method (>= 0) */ char png_zTXt_method(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_TEXT_COMPRESSED, PNG_CHNK_LEN) != 0) return -1; - - if (chunk->data == NULL || chunk->length == 0) return -1; + PNG_VALIDATE(PNG_CHUNK_TEXT_COMPRESSED, -1); return *((char *) chunk->data + strlen(chunk->data) + 1); } @@ -774,9 +718,7 @@ char png_zTXt_method(png_chunk_t *chunk) { * Pointer to block containing datastream; must be free'd */ void *png_zTXt_data(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_TEXT_COMPRESSED, PNG_CHNK_LEN) != 0) return NULL; - - if (chunk->data == NULL || chunk->length == 0) return NULL; + PNG_VALIDATE(PNG_CHUNK_TEXT_COMPRESSED, NULL); int len = strlen(chunk->data) + 2; int n = chunk->length - len; @@ -792,9 +734,7 @@ void *png_zTXt_data(png_chunk_t *chunk) { * Length of datastream */ int png_zTXt_length(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_TEXT_COMPRESSED, PNG_CHNK_LEN) != 0) return -1; - - if (chunk->data == NULL || chunk->length == 0) return -1; + PNG_VALIDATE(PNG_CHUNK_TEXT_COMPRESSED, -1); return chunk->length - (strlen(chunk->data) + 2); } @@ -806,9 +746,7 @@ int png_zTXt_length(png_chunk_t *chunk) { * Array of values corresponding to appropriate background color channels; must be free'd */ void *png_bKGD_get(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_BACKGROUND, PNG_CHNK_LEN) != 0) return NULL; - - if (chunk->length == 0 || chunk->data == NULL) return NULL; + PNG_VALIDATE(PNG_CHUNK_BACKGROUND, NULL); int type = png_attr_col_type(chunk->png); void *array = NULL; @@ -840,9 +778,7 @@ void *png_bKGD_get(png_chunk_t *chunk) { * Returns: -1 on error */ int png_bKGD_len(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_BACKGROUND, PNG_CHNK_LEN) != 0) return -1; - - if (chunk->length == 0 || chunk->data == NULL) return -1; + PNG_VALIDATE(PNG_CHUNK_BACKGROUND, -1); switch (png_attr_col_type(chunk->png)) { case 0: @@ -865,9 +801,7 @@ int png_bKGD_len(png_chunk_t *chunk) { * Array of values representing the frequencies of the colors contained in the PLTE chunk; must be free'd */ unsigned short *png_hIST_get(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_HISTOGRAM, PNG_CHNK_LEN) != 0) return NULL; - - if (chunk->length == 0 || chunk->data == NULL) return NULL; + PNG_VALIDATE(PNG_CHUNK_HISTOGRAM, NULL); switch (png_attr_col_type(chunk->png)) { case 2: @@ -891,9 +825,7 @@ unsigned short *png_hIST_get(png_chunk_t *chunk) { * Returns: -1 on error */ int png_hIST_len(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_HISTOGRAM, PNG_CHNK_LEN) != 0) return -1; - - if (chunk->length == 0 || chunk->data == NULL) return -1; + PNG_VALIDATE(PNG_CHUNK_HISTOGRAM, -1); switch (png_attr_col_type(chunk->png)) { case 2: @@ -914,9 +846,7 @@ int png_hIST_len(png_chunk_t *chunk) { * pixels per unit on success */ int png_pHYs_ppuX(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_PHYS, PNG_CHNK_LEN) != 0) return -1; - - if (chunk->length < 4) return -1; + PNG_VALIDATE(PNG_CHUNK_PHYS, -1); return ntohl(*((int *)chunk->data)); } @@ -928,9 +858,7 @@ int png_pHYs_ppuX(png_chunk_t *chunk) { * pixels per unit on success */ int png_pHYs_ppuY(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_PHYS, PNG_CHNK_LEN) != 0) return -1; - - if (chunk->length < 8) return -1; + PNG_VALIDATE(PNG_CHUNK_PHYS, -1); return ntohl(*((int *)(chunk->data + 4))); } @@ -943,9 +871,7 @@ int png_pHYs_ppuY(png_chunk_t *chunk) { * 1 for meter */ char png_pHYs_unit(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_PHYS, PNG_CHNK_LEN) != 0) return -1; - - if (chunk->length < 9) return -1; + PNG_VALIDATE(PNG_CHUNK_PHYS, -1); return *((char *) chunk->data + 9); } @@ -957,9 +883,7 @@ char png_pHYs_unit(png_chunk_t *chunk) { * Name of the palette; must be free'd */ char *png_sPLT_name(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_SPALETTE, PNG_CHNK_LEN) != 0) return NULL; - - if (chunk->length == 0 || chunk->data == NULL) return NULL; + PNG_VALIDATE(PNG_CHUNK_SPALETTE, NULL); return strdup(chunk->data); } @@ -970,9 +894,7 @@ char *png_sPLT_name(png_chunk_t *chunk) { * Returns: -1 on error */ char png_sPLT_sample_depth(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_SPALETTE, PNG_CHNK_LEN) != 0) return -1; - - if (chunk->length == 0 || chunk->data == NULL) return -1; + PNG_VALIDATE(PNG_CHUNK_SPALETTE, -1); int offset = strlen(chunk->data) + 1; @@ -986,9 +908,7 @@ char png_sPLT_sample_depth(png_chunk_t *chunk) { * Array of entries on success; must be free'd */ palette_entry_t *png_sPLT_entries(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_SPALETTE, PNG_CHNK_LEN) != 0) return NULL; - - if (chunk->length == 0 || chunk->data == NULL) return NULL; + PNG_VALIDATE(PNG_CHUNK_SPALETTE, NULL); int offset = strlen(chunk->data) + 2, i; int size = chunk->length - offset, entry_size = 10; @@ -1034,9 +954,7 @@ palette_entry_t *png_sPLT_entries(png_chunk_t *chunk) { * Returns: -1 on error */ int png_sPLT_entries_len(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_SPALETTE, PNG_CHNK_LEN) != 0) return -1; - - if (chunk->length == 0 || chunk->data == NULL) return -1; + PNG_VALIDATE(PNG_CHUNK_SPALETTE, -1); int size = chunk->length - (strlen(chunk->data) + 2); @@ -1057,9 +975,7 @@ int png_sPLT_entries_len(png_chunk_t *chunk) { * 10 if the rgba channels can be stored in two bytes */ int png_sPLT_entry_size(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_SPALETTE, PNG_CHNK_LEN) != 0) return -1; - - if (chunk->length == 0 || chunk->data == NULL) return -1; + PNG_VALIDATE(PNG_CHUNK_SPALETTE, -1); int sample_depth = *((int *) (chunk->data + strlen(chunk->data) + 1)); @@ -1079,7 +995,7 @@ int png_sPLT_entry_size(png_chunk_t *chunk) { * Complete year (eg. 1995, not 95) on success */ short png_tIME_year(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_TIME, PNG_CHNK_LEN) != 0) return -1; + PNG_VALIDATE(PNG_CHUNK_TIME, -1); return ntohs(*((short *) (chunk->data))); } @@ -1091,7 +1007,7 @@ short png_tIME_year(png_chunk_t *chunk) { * Month (1-12) on success */ char png_tIME_month(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_TIME, PNG_CHNK_LEN) != 0) return -1; + PNG_VALIDATE(PNG_CHUNK_TIME, -1); return *((char *) (chunk->data + 2)); } @@ -1103,7 +1019,7 @@ char png_tIME_month(png_chunk_t *chunk) { * Day (1-31) on success */ char png_tIME_day(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_TIME, PNG_CHNK_LEN) != 0) return -1; + PNG_VALIDATE(PNG_CHUNK_TIME, -1); return *((char *) (chunk->data + 3)); } @@ -1115,7 +1031,7 @@ char png_tIME_day(png_chunk_t *chunk) { * Hour (0-23) on success */ char png_tIME_hour(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_TIME, PNG_CHNK_LEN) != 0) return -1; + PNG_VALIDATE(PNG_CHUNK_TIME, -1); return *((char *) (chunk->data + 4)); } @@ -1127,7 +1043,7 @@ char png_tIME_hour(png_chunk_t *chunk) { * Minute (0-59) on success */ char png_tIME_minute(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_TIME, PNG_CHNK_LEN) != 0) return -1; + PNG_VALIDATE(PNG_CHUNK_TIME, -1); return *((char *) (chunk->data + 5)); } @@ -1139,7 +1055,7 @@ char png_tIME_minute(png_chunk_t *chunk) { * Second (0-60 to allow for leap seconds) on success */ char png_tIME_second(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_TIME, PNG_CHNK_LEN) != 0) return -1; + PNG_VALIDATE(PNG_CHUNK_TIME, -1); return *((char *) (chunk->data + 6)); } @@ -1151,7 +1067,7 @@ char png_tIME_second(png_chunk_t *chunk) { * Last modified date in ISO 8601 format: YYYY-MM-DDTHH:MM:SS+00:00 */ char *png_tIME_iso8601(png_chunk_t *chunk) { - if (strncmp(chunk->type, PNG_CHUNK_TIME, PNG_CHNK_LEN) != 0) return NULL; + PNG_VALIDATE(PNG_CHUNK_TIME, NULL); char *time = (char *) malloc(sizeof(char) * 26); // Prefer not to hardcode the length in diff --git a/formats/png.h b/formats/png.h index 025f0a8..7c8b614 100644 --- a/formats/png.h +++ b/formats/png.h @@ -30,6 +30,8 @@ #define PNG_ISCRITICAL(chunk) (chunk->type[0] & 0x20 != 0) #define PNG_ISANCILLARY(chunk) (chunk->type[0] & 0x20 == 0) +#define PNG_VALIDATE(chunkType, defaultReturn) if (strncmp(chunk->type, chunkType, PNG_CHNK_LEN) != 0) return defaultReturn;\ + if (chunk->length == 0 || chunk->data == NULL) return defaultReturn; typedef unsigned char byte; typedef unsigned short ushort; diff --git a/utils/version.h b/utils/version.h index 1dbf527..345c5d2 100644 --- a/utils/version.h +++ b/utils/version.h @@ -10,7 +10,7 @@ #endif /* Version */ -#define IV_VERSION "v0.1.6" +#define IV_VERSION "v0.1.7" #define IV_UPDATED "2020-06-03" #endif \ No newline at end of file