diff --git a/.gitignore b/.gitignore index 344c97ca..221a9ba6 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ node_modules npm-debug.log tests/results .idea +tests/.DS_Store diff --git a/README.md b/README.md index d51504ac..890a9429 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ 0. [Opacify](#opacify) 0. [Paste](#paste) 0. [Set pixel](#set-pixel) + 0. [Set metadata](#set-metadata) 0. [Getters](#getters) 0. [Width](#width) 0. [Height](#height) @@ -47,6 +48,7 @@ 0. [PNG](#png) 0. [GIF](#gif) 0. [Write to file](#write-to-file) + 0. [Get metadata](#get-metadata) 0. [Batch operations](#batch-operations) 0. [Copyrights](#copyrights) @@ -554,6 +556,17 @@ Set the color of a pixel. 0. Extra caution is required when using this method in batch mode, as the dimensions of the image may change by the time this operation is called. +#### Set metadata + +Set the metadata in an image. This is currently only supported for PNG files. +Sets a tEXt chunk with the key `lwip_data` and comment as the given string. If +called with a `null` parameter, removes existing metadata from the image, +if present. + +`image.setMetadata(metadata)` + +0. `metadata {String}`: a string of arbitrary length, or null. + ### Getters #### Width @@ -685,6 +698,14 @@ Write encoded binary image data directly to a file. 0. `params {Object}`: **Optional** Format-specific parameters. 0. `callback {Function(err)}` +#### Get Metadata + +Get the textual metadata from an image. This is currently only supported for +tEXt chunks in PNG images, and will get the first tEXt chunk found with the key +`lwip_data`. If none is found, returns null. + +`image.getMetadata()` + ### Batch operations Each of the [image operations](#image-operations) above can be done as part of diff --git a/lib/Image.js b/lib/Image.js index 74083061..947bf42d 100644 --- a/lib/Image.js +++ b/lib/Image.js @@ -2,10 +2,11 @@ var lwip_image = require('../build/Release/lwip_image'); - function Image(pixelsBuf, width, height, trans) { + function Image(pixelsBuf, width, height, trans, metadata) { this.__lwip = new lwip_image.LwipImage(pixelsBuf, width, height); this.__locked = false; this.__trans = trans; + this.__metadata = metadata; } // EXPORTS diff --git a/lib/ImagePrototypeInit.js b/lib/ImagePrototypeInit.js index 73649249..d5c96d4c 100644 --- a/lib/ImagePrototypeInit.js +++ b/lib/ImagePrototypeInit.js @@ -47,6 +47,19 @@ this.__locked = false; }; + Image.prototype.getMetadata = function() { + return this.__metadata; + }; + + Image.prototype.setMetadata = function(data) { + if (typeof data != "string" && data != null) { + throw Error("Metadata must be a string or null"); + } + + this.__metadata = data; + return data; + }; + Image.prototype.width = function() { return this.__lwip.width(); }; @@ -558,6 +571,7 @@ params.compression, params.interlaced, params.transparency === 'auto' ? that.__trans : params.transparency, + that.__metadata, encoderCb ); } else if (type === 'gif') { diff --git a/lib/obtain.js b/lib/obtain.js index 23999c9c..0ade5895 100644 --- a/lib/obtain.js +++ b/lib/obtain.js @@ -28,8 +28,8 @@ opener = getOpener(type); fs.readFile(source, function(err, imbuff) { if (err) return callback(err); - opener(imbuff, function(err, pixelsBuf, width, height, channels, trans) { - callback(err, err ? null : new Image(pixelsBuf, width, height, trans)); + opener(imbuff, function(err, pixelsBuf, width, height, channels, trans, metadata) { + callback(err, err ? null : new Image(pixelsBuf, width, height, trans, metadata)); }); }); } else if (source instanceof Buffer) { @@ -44,8 +44,8 @@ } else if (typeof type === 'string') { // it's an encoded image opener = getOpener(type); - opener(source, function(err, pixelsBuf, width, height, channels, trans) { - callback(err, err ? null : new Image(pixelsBuf, width, height, trans)); + opener(source, function(err, pixelsBuf, width, height, channels, trans, metadata) { + callback(err, err ? null : new Image(pixelsBuf, width, height, trans, metadata)); }); } else throw Error('Invalid type'); } else throw Error("Invalid source"); diff --git a/src/decoder/buffer_worker.cpp b/src/decoder/buffer_worker.cpp index 534862e9..f2256f36 100644 --- a/src/decoder/buffer_worker.cpp +++ b/src/decoder/buffer_worker.cpp @@ -5,7 +5,7 @@ DecodeBufferWorker::DecodeBufferWorker( Local & buff, buf_dec_f_t decoder ): NanAsyncWorker(callback), _decoder(decoder), _pixbuf(NULL), _width(0), - _height(0), _channels(0), _trans(false) { + _height(0), _channels(0), _trans(false), _metadata("") { SaveToPersistent("buff", buff); // make sure buff isn't GC'ed _buffer = Buffer::Data(buff); _buffsize = Buffer::Length(buff); @@ -16,7 +16,10 @@ DecodeBufferWorker::~DecodeBufferWorker() {} void DecodeBufferWorker::Execute () { CImg * img = NULL; string err; - err = _decoder(_buffer, _buffsize, &img); + char * metadata = NULL; + + err = _decoder(_buffer, _buffsize, &img, &metadata); + if (img == NULL) { SetErrorMessage(err.c_str()); return; @@ -33,12 +36,22 @@ void DecodeBufferWorker::Execute () { _width = img->width(); _height = img->height(); _channels = 4; + _metadata = metadata; + delete img; return; } void DecodeBufferWorker::HandleOKCallback () { NanScope(); + + Local metadata; + if (_metadata == NULL) { + metadata = NanNull(); + } else { + metadata = NanNew(_metadata); + } + Local argv[] = { NanNull(), NanBufferUse( @@ -48,7 +61,9 @@ void DecodeBufferWorker::HandleOKCallback () { NanNew(_width), NanNew(_height), NanNew(_channels), - NanNew(_trans) + NanNew(_trans), + metadata }; - callback->Call(6, argv); + + callback->Call(7, argv); } diff --git a/src/decoder/decoder.h b/src/decoder/decoder.h index 3467765b..aa7e4f74 100644 --- a/src/decoder/decoder.h +++ b/src/decoder/decoder.h @@ -25,7 +25,7 @@ using namespace v8; using namespace node; using namespace std; -typedef string (* buf_dec_f_t)(char *, size_t, CImg **); +typedef string (* buf_dec_f_t)(char *, size_t, CImg **, char **); class DecodeBufferWorker : public NanAsyncWorker { public: @@ -46,6 +46,7 @@ class DecodeBufferWorker : public NanAsyncWorker { size_t _height; int _channels; bool _trans; // transparency + char * _metadata; }; typedef struct { @@ -77,9 +78,9 @@ inline void lwip_jpeg_error_exit (j_common_ptr cinfo) { */ string toRGBA(CImg ** img); -string decode_jpeg_buffer(char * buffer, size_t size, CImg ** img); -string decode_png_buffer(char * buffer, size_t size, CImg ** img); -string decode_gif_buffer(char * buffer, size_t size, CImg ** img); +string decode_jpeg_buffer(char * buffer, size_t size, CImg ** img, char ** metadata); +string decode_png_buffer(char * buffer, size_t size, CImg ** img, char ** metadata); +string decode_gif_buffer(char * buffer, size_t size, CImg ** img, char ** metadata); void pngReadCB(png_structp png_ptr, png_bytep data, png_size_t length); int gifReadCB(GifFileType * gif, GifByteType * buf, int length); diff --git a/src/decoder/gif_decoder.cpp b/src/decoder/gif_decoder.cpp index e6a14ff8..cbe0618f 100644 --- a/src/decoder/gif_decoder.cpp +++ b/src/decoder/gif_decoder.cpp @@ -4,7 +4,7 @@ #define ALPHA_OPAQUE 255 #define C_TRANS 0 -string decode_gif_buffer(char * buffer, size_t size, CImg ** cimg) { +string decode_gif_buffer(char * buffer, size_t size, CImg ** cimg, char ** metadata) { gifReadCbData buffinf = {(unsigned char *) buffer, size, 0}; GifFileType * gif = NULL; @@ -70,6 +70,10 @@ string decode_gif_buffer(char * buffer, size_t size, CImg ** cimg return GifErrorString(errcode); } + // TODO: implement getting metadata from GIFs; this is a placeholder + *metadata = (char *)malloc(sizeof(char)); + *metadata[0] = '\0'; + return ""; } diff --git a/src/decoder/jpeg_decoder.cpp b/src/decoder/jpeg_decoder.cpp index df96bd16..bc9b7001 100644 --- a/src/decoder/jpeg_decoder.cpp +++ b/src/decoder/jpeg_decoder.cpp @@ -1,6 +1,6 @@ #include "decoder.h" -string decode_jpeg_buffer(char * buffer, size_t size, CImg ** cimg) { +string decode_jpeg_buffer(char * buffer, size_t size, CImg ** cimg, char ** metadata) { struct jpeg_decompress_struct cinfo; struct lwip_jpeg_error_mgr jerr; @@ -53,5 +53,9 @@ string decode_jpeg_buffer(char * buffer, size_t size, CImg ** cim jpeg_finish_decompress(&cinfo); jpeg_destroy_decompress(&cinfo); + // TODO: implement getting metadata from GIFs; this is a placeholder + *metadata = (char *)malloc(sizeof(char)); + *metadata[0] = '\0'; + return ""; } diff --git a/src/decoder/png_decoder.cpp b/src/decoder/png_decoder.cpp index b68873f7..9e97ced3 100644 --- a/src/decoder/png_decoder.cpp +++ b/src/decoder/png_decoder.cpp @@ -1,6 +1,7 @@ #include "decoder.h" -string decode_png_buffer(char * buffer, size_t size, CImg ** cimg) { +string decode_png_buffer(char * buffer, size_t size, CImg ** cimg, char ** metadata) { + // check it's a valid png buffer if (size < 8 || png_sig_cmp((png_const_bytep) buffer, 0, 8)) { return "Invalid PNG buffer"; @@ -43,6 +44,20 @@ string decode_png_buffer(char * buffer, size_t size, CImg ** cimg png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL); + + // get metadata in first text chunk found with keyboard 'lwip_data' + png_textp text_ptr; + int num_comments = png_get_text(png_ptr, info_ptr, &text_ptr, NULL); + + for (int i = 0; i < num_comments; i++) { + if (strcmp(text_ptr[i].key, "lwip_data") == 0) { + int metadata_len = (strlen(text_ptr[i].text) + 1) * sizeof(char); + *metadata = (char *)malloc(metadata_len); + memcpy(*metadata, text_ptr[i].text, metadata_len); + break; //TODO: handle multiple lwip_data text chunks? + } + } + if (color_type == PNG_COLOR_TYPE_PALETTE) { png_set_palette_to_rgb(png_ptr); color_type = PNG_COLOR_TYPE_RGB; diff --git a/src/encoder/encoder.h b/src/encoder/encoder.h index a68468dc..e20f3a88 100644 --- a/src/encoder/encoder.h +++ b/src/encoder/encoder.h @@ -53,6 +53,7 @@ class EncodeToPngBufferWorker : public NanAsyncWorker { int compression, bool interlaced, bool trans, + char * metadata, NanCallback * callback ); ~EncodeToPngBufferWorker(); @@ -65,6 +66,7 @@ class EncodeToPngBufferWorker : public NanAsyncWorker { int _compression; bool _interlaced; bool _trans; + char * _metadata; char * _pngbuf; size_t _pngbufsize; }; diff --git a/src/encoder/init.cpp b/src/encoder/init.cpp index 5aad47e9..7e5b67df 100644 --- a/src/encoder/init.cpp +++ b/src/encoder/init.cpp @@ -21,7 +21,7 @@ NAN_METHOD(encodeToJpegBuffer) { NanReturnUndefined(); } -// encoder.png(pixbuf, width, height, compression, interlaced, trans, callback) +// encoder.png(pixbuf, width, height, compression, interlaced, trans, metadata, callback) NAN_METHOD(encodeToPngBuffer) { NanScope(); @@ -31,7 +31,18 @@ NAN_METHOD(encodeToPngBuffer) { int compression = args[3].As()->Value(); bool interlaced = args[4]->BooleanValue(); bool trans = args[5]->BooleanValue(); - NanCallback * callback = new NanCallback(args[6].As()); + + char * metadata; + + if (args[6]->IsNull() || args[6]->IsUndefined()) { + metadata = NULL; + } else { + int metadata_len = args[6].As()->Utf8Length(); + metadata = (char *)malloc((metadata_len + 1) * sizeof(char)); + args[6].As()->WriteUtf8(metadata); + } + + NanCallback * callback = new NanCallback(args[7].As()); NanAsyncQueueWorker( new EncodeToPngBufferWorker( @@ -41,6 +52,7 @@ NAN_METHOD(encodeToPngBuffer) { compression, interlaced, trans, + metadata, callback ) ); diff --git a/src/encoder/png_worker.cpp b/src/encoder/png_worker.cpp index 1cfe527e..5efc4cc5 100644 --- a/src/encoder/png_worker.cpp +++ b/src/encoder/png_worker.cpp @@ -10,9 +10,10 @@ EncodeToPngBufferWorker::EncodeToPngBufferWorker( int compression, bool interlaced, bool trans, + char * metadata, NanCallback * callback ): NanAsyncWorker(callback), _width(width), _height(height), - _compression(compression), _interlaced(interlaced), _trans(trans), + _compression(compression), _interlaced(interlaced), _trans(trans), _metadata(metadata), _pngbuf(NULL), _pngbufsize(0) { SaveToPersistent("buff", buff); // make sure buff isn't GC'ed _pixbuf = (unsigned char *) Buffer::Data(buff); @@ -97,6 +98,15 @@ void EncodeToPngBufferWorker::Execute () { PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT ); + + if (_metadata != NULL) { + png_text metadata; + metadata.compression = PNG_TEXT_COMPRESSION_NONE; + metadata.key = "lwip_data"; + metadata.text = _metadata; + png_set_text(png_ptr, info_ptr, &metadata, 1); + } + png_set_compression_level(png_ptr, compLevel); pngWriteCbData buffinf = {NULL, 0}; @@ -164,4 +174,4 @@ void pngWriteCB(png_structp png_ptr, png_bytep data, png_size_t length) { memcpy(buffinf->buff + buffinf->buffsize, data, length); buffinf->buffsize += length; -} +} \ No newline at end of file diff --git a/src/lib/gif/egif_lib.c b/src/lib/gif/egif_lib.c index 39a62b2e..c62e86c1 100644 --- a/src/lib/gif/egif_lib.c +++ b/src/lib/gif/egif_lib.c @@ -58,10 +58,10 @@ EGifOpenFileName(const char *FileName, const bool TestExistence, int *Error) GifFileType *GifFile; if (TestExistence) - FileHandle = open(FileName, O_WRONLY | O_CREAT | O_EXCL, + FileHandle = open(FileName, O_WRONLY | O_CREAT | O_EXCL, S_IREAD | S_IWRITE); else - FileHandle = open(FileName, O_WRONLY | O_CREAT | O_TRUNC, + FileHandle = open(FileName, O_WRONLY | O_CREAT | O_TRUNC, S_IREAD | S_IWRITE); if (FileHandle == -1) { @@ -190,7 +190,7 @@ EGifGetGifVersion(GifFileType *GifFile) GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private; int i, j; - /* + /* * Bulletproofing - always write GIF89 if we need to. * Note, we don't clear the gif89 flag here because * users of the sequential API might have called EGifSetGifVersion() @@ -217,7 +217,7 @@ EGifGetGifVersion(GifFileType *GifFile) || function == APPLICATION_EXT_FUNC_CODE) Private->gif89 = true; } - + if (Private->gif89) return GIF89_STAMP; else @@ -226,7 +226,7 @@ EGifGetGifVersion(GifFileType *GifFile) /****************************************************************************** Set the GIF version. In the extremely unlikely event that there is ever - another version, replace the bool argument with an enum in which the + another version, replace the bool argument with an enum in which the GIF87 value is 0 (numerically the same as bool false) and the GIF89 value is 1 (numerically the same as bool true). That way we'll even preserve object-file compatibility! @@ -241,7 +241,7 @@ void EGifSetGifVersion(GifFileType *GifFile, const bool gif89) /****************************************************************************** All writes to the GIF should go through this. ******************************************************************************/ -static int InternalWrite(GifFileType *GifFileOut, +static int InternalWrite(GifFileType *GifFileOut, const unsigned char *buf, size_t len) { GifFilePrivateType *Private = (GifFilePrivateType*)GifFileOut->Private; @@ -561,7 +561,7 @@ EGifPutExtensionLeader(GifFileType *GifFile, const int ExtCode) Put extension block data (see GIF manual) into a GIF file. ******************************************************************************/ int -EGifPutExtensionBlock(GifFileType *GifFile, +EGifPutExtensionBlock(GifFileType *GifFile, const int ExtLen, const void *Extension) { @@ -660,7 +660,7 @@ size_t EGifGCBToExtension(const GraphicsControlBlock *GCB, Replace the Graphics Control Block for a saved image, if it exists. ******************************************************************************/ -int EGifGCBToSavedExtension(const GraphicsControlBlock *GCB, +int EGifGCBToSavedExtension(const GraphicsControlBlock *GCB, GifFileType *GifFile, int ImageIndex) { int i; @@ -708,7 +708,7 @@ EGifPutCode(GifFileType *GifFile, int CodeSize, const GifByteType *CodeBlock) } /* No need to dump code size as Compression set up does any for us: */ - /* + /* * Buf = CodeSize; * if (InternalWrite(GifFile, &Buf, 1) != 1) { * GifFile->Error = E_GIF_ERR_WRITE_FAILED; @@ -891,7 +891,7 @@ EGifCompressLine(GifFileType *GifFile, while (i < LineLen) { /* Decode LineLen items. */ Pixel = Line[i++]; /* Get next pixel from stream. */ - /* Form a new unique key to search hash table for the code combines + /* Form a new unique key to search hash table for the code combines * CrntCode as Prefix string with Pixel as postfix char. */ NewKey = (((uint32_t) CrntCode) << 8) + Pixel; @@ -1047,9 +1047,9 @@ EGifBufferedOutput(GifFileType *GifFile, ******************************************************************************/ static int -EGifWriteExtensions(GifFileType *GifFileOut, - ExtensionBlock *ExtensionBlocks, - int ExtensionBlockCount) +EGifWriteExtensions(GifFileType *GifFileOut, + ExtensionBlock *ExtensionBlocks, + int ExtensionBlockCount) { if (ExtensionBlocks) { ExtensionBlock *ep; @@ -1072,9 +1072,9 @@ EGifWriteExtensions(GifFileType *GifFileOut, } int -EGifSpew(GifFileType *GifFileOut) +EGifSpew(GifFileType *GifFileOut) { - int i, j; + int i, j; if (EGifPutScreenDesc(GifFileOut, GifFileOut->SWidth, @@ -1094,7 +1094,7 @@ EGifSpew(GifFileType *GifFileOut) if (sp->RasterBits == NULL) continue; - if (EGifWriteExtensions(GifFileOut, + if (EGifWriteExtensions(GifFileOut, sp->ExtensionBlocks, sp->ExtensionBlockCount) == GIF_ERROR) return (GIF_ERROR); @@ -1109,8 +1109,8 @@ EGifSpew(GifFileType *GifFileOut) return (GIF_ERROR); if (sp->ImageDesc.Interlace) { - /* - * The way an interlaced image should be written - + /* + * The way an interlaced image should be written - * offsets and jumps... */ int InterlacedOffset[] = { 0, 4, 2, 1 }; @@ -1118,11 +1118,11 @@ EGifSpew(GifFileType *GifFileOut) int k; /* Need to perform 4 passes on the images: */ for (k = 0; k < 4; k++) - for (j = InterlacedOffset[k]; + for (j = InterlacedOffset[k]; j < SavedHeight; j += InterlacedJumps[k]) { - if (EGifPutLine(GifFileOut, - sp->RasterBits + j * SavedWidth, + if (EGifPutLine(GifFileOut, + sp->RasterBits + j * SavedWidth, SavedWidth) == GIF_ERROR) return (GIF_ERROR); } diff --git a/src/lib/gif/gifalloc.c b/src/lib/gif/gifalloc.c index 9fe3edf8..9862464e 100644 --- a/src/lib/gif/gifalloc.c +++ b/src/lib/gif/gifalloc.c @@ -13,7 +13,7 @@ #define MAX(x, y) (((x) > (y)) ? (x) : (y)) /****************************************************************************** - Miscellaneous utility functions + Miscellaneous utility functions ******************************************************************************/ /* return smallest bitfield size n will fit in */ @@ -29,7 +29,7 @@ GifBitSize(int n) } /****************************************************************************** - Color map object functions + Color map object functions ******************************************************************************/ /* @@ -46,7 +46,7 @@ GifMakeMapObject(int ColorCount, const GifColorType *ColorMap) if (ColorCount != (1 << GifBitSize(ColorCount))) { return ((ColorMapObject *) NULL); } - + Object = (ColorMapObject *)malloc(sizeof(ColorMapObject)); if (Object == (ColorMapObject *) NULL) { return ((ColorMapObject *) NULL); @@ -104,7 +104,7 @@ DumpColorMap(ColorMapObject *Object, #endif /* DEBUG */ /******************************************************************************* - Compute the union of two given color maps and return it. If result can't + Compute the union of two given color maps and return it. If result can't fit into 256 colors, NULL is returned, the allocated union otherwise. ColorIn1 is copied as is to ColorUnion, while colors from ColorIn2 are copied iff they didn't exist before. ColorTransIn2 maps the old @@ -131,14 +131,14 @@ GifUnionColorMap(const ColorMapObject *ColorIn1, if (ColorUnion == NULL) return (NULL); - /* + /* * Copy ColorIn1 to ColorUnion. */ for (i = 0; i < ColorIn1->ColorCount; i++) ColorUnion->Colors[i] = ColorIn1->Colors[i]; CrntSlot = ColorIn1->ColorCount; - /* + /* * Potentially obnoxious hack: * * Back CrntSlot down past all contiguous {0, 0, 0} slots at the end @@ -154,7 +154,7 @@ GifUnionColorMap(const ColorMapObject *ColorIn1, for (i = 0; i < ColorIn2->ColorCount && CrntSlot <= 256; i++) { /* Let's see if this color already exists: */ for (j = 0; j < ColorIn1->ColorCount; j++) - if (memcmp (&ColorIn1->Colors[j], &ColorIn2->Colors[i], + if (memcmp (&ColorIn1->Colors[j], &ColorIn2->Colors[i], sizeof(GifColorType)) == 0) break; @@ -178,7 +178,7 @@ GifUnionColorMap(const ColorMapObject *ColorIn1, if (RoundUpTo != ColorUnion->ColorCount) { register GifColorType *Map = ColorUnion->Colors; - /* + /* * Zero out slots up to next power of 2. * We know these slots exist because of the way ColorUnion's * start dimension was computed. @@ -212,7 +212,7 @@ GifApplyTranslation(SavedImage *Image, GifPixelType Translation[]) } /****************************************************************************** - Extension record functions + Extension record functions ******************************************************************************/ int GifAddExtensionBlock(int *ExtensionBlockCount, @@ -258,7 +258,7 @@ GifFreeExtensions(int *ExtensionBlockCount, return; for (ep = *ExtensionBlocks; - ep < (*ExtensionBlocks + *ExtensionBlockCount); + ep < (*ExtensionBlocks + *ExtensionBlockCount); ep++) (void)free((char *)ep->Bytes); (void)free((char *)*ExtensionBlocks); @@ -267,7 +267,7 @@ GifFreeExtensions(int *ExtensionBlockCount, } /****************************************************************************** - Image block allocation functions + Image block allocation functions ******************************************************************************/ /* Private Function: @@ -277,7 +277,7 @@ void FreeLastSavedImage(GifFileType *GifFile) { SavedImage *sp; - + if ((GifFile == NULL) || (GifFile->SavedImages == NULL)) return; @@ -307,7 +307,7 @@ FreeLastSavedImage(GifFileType *GifFile) } /* - * Append an image block to the SavedImages array + * Append an image block to the SavedImages array */ SavedImage * GifMakeSavedImage(GifFileType *GifFile, const SavedImage *CopyFrom) @@ -327,7 +327,7 @@ GifMakeSavedImage(GifFileType *GifFile, const SavedImage *CopyFrom) if (CopyFrom != NULL) { memcpy((char *)sp, CopyFrom, sizeof(SavedImage)); - /* + /* * Make our own allocated copies of the heap fields in the * copied record. This guards against potential aliasing * problems. @@ -391,7 +391,7 @@ GifFreeSavedImages(GifFileType *GifFile) if (sp->RasterBits != NULL) free((char *)sp->RasterBits); - + GifFreeExtensions(&sp->ExtensionBlockCount, &sp->ExtensionBlocks); } free((char *)GifFile->SavedImages); diff --git a/src/lib/jpeg/jcapimin.c b/src/lib/jpeg/jcapimin.c index 639ce86f..a8cdc4d2 100644 --- a/src/lib/jpeg/jcapimin.c +++ b/src/lib/jpeg/jcapimin.c @@ -37,7 +37,7 @@ jpeg_CreateCompress (j_compress_ptr cinfo, int version, size_t structsize) if (version != JPEG_LIB_VERSION) ERREXIT2(cinfo, JERR_BAD_LIB_VERSION, JPEG_LIB_VERSION, version); if (structsize != SIZEOF(struct jpeg_compress_struct)) - ERREXIT2(cinfo, JERR_BAD_STRUCT_SIZE, + ERREXIT2(cinfo, JERR_BAD_STRUCT_SIZE, (int) SIZEOF(struct jpeg_compress_struct), (int) structsize); /* For debugging purposes, we zero the whole master structure. diff --git a/tests/.DS_Store b/tests/.DS_Store new file mode 100644 index 00000000..51cf4899 Binary files /dev/null and b/tests/.DS_Store differ diff --git a/tests/01.getters/index.js b/tests/01.getters/index.js index 854cf7b1..1db1b414 100644 --- a/tests/01.getters/index.js +++ b/tests/01.getters/index.js @@ -14,6 +14,7 @@ before(function(done) { }); }); + describe('lwip.width', function() { it('should return correct width', function() { assert(image.width() === width); @@ -87,3 +88,19 @@ describe('lwip.extract', function() { }); }); }); + +describe('lwip.getMetadata', function() { + it('should return correct metadata', function(done) { + lwip.open(imgs.png.hasMetadata, function(err, img) { + assert(img.getMetadata() === "Lorem ipsum dolor sit amet"); + done(); + }); + }); + + it('should return null if no metadata found', function(done) { + lwip.open(imgs.png.noMetadata, function(err, img) { + assert(img.getMetadata() === null); + done(); + }); + }); +}); diff --git a/tests/02.operations/121.setMetadata.js b/tests/02.operations/121.setMetadata.js new file mode 100644 index 00000000..52acfe73 --- /dev/null +++ b/tests/02.operations/121.setMetadata.js @@ -0,0 +1,100 @@ +var join = require('path').join, + assert = require('assert'), + mkdirp = require('mkdirp'), + lwip = require('../../'), + imgs = require('../imgs'), + should = require('should'); + +var tmpDir = join(__dirname, '../results'); + +describe('lwip.setMetadata', function() { + + before(function(done) { + mkdirp(tmpDir, done); + }); + + describe('set png metadata', function() { + it('should set metadata and be able to read metadata', function(done) { + var filename = 'setMetadata.png'; + var metadata = 'The quick brown fox jumps over the lazy dog'; + + lwip.create(1, 1, function(err, img) { + if (err) return done(err); + img.setMetadata(metadata); + img.writeFile(join(tmpDir, filename), function(err) { + lwip.open(join(tmpDir, filename), function(err, imgWithMetadata) { + if (err) return done(err); + assert(imgWithMetadata.getMetadata() === metadata); + done(); + }); + }); + }); + }); + + it('should not set metadata if setMetadata is not called', function(done) { + var filename = 'noMetadata.png'; + + lwip.create(1, 1, function(err, img) { + if (err) return done(err); + + img.writeFile(join(tmpDir, filename), function(err) { + lwip.open(join(tmpDir, filename), function(err, imgNoMetadata) { + if (err) return done(err); + assert(imgNoMetadata.getMetadata() === null); + done(); + }); + }); + }); + }); + + it('should throw error if non-string metadata is set', function(done) { + var filename = 'noMetadata.png'; + + lwip.create(1, 1, function(err, img) { + if (err) return done(err);; + img.setMetadata.bind(img, {}).should.throwError(); + img.setMetadata.bind(img, function(){}).should.throwError(); + img.setMetadata.bind(img, 42).should.throwError(); + done(); + }); + }); + + it('should remove metadata if called with null parameter', function(done) { + lwip.open(imgs.png.hasMetadata, function(err, img) { + var filename = 'noMetadata.png'; + + assert(img.getMetadata() === 'Lorem ipsum dolor sit amet'); + + img.setMetadata(null); + + img.writeFile(join(tmpDir, filename), function(err) { + lwip.open(join(tmpDir, filename), function(err, imgWithMetadata) { + if (err) return done(err); + assert(imgWithMetadata.getMetadata() === null); + done(); + }); + }); + }); + }); + + it('can reset metadata on image with existing metadata', function(done) { + lwip.open(imgs.png.hasMetadata, function(err, img) { + var filename = 'changedMetadata.png'; + var oldMetadata = 'Lorem ipsum dolor sit amet'; + var newMetadata = 'The quick brown fox jumps over the lazy dog'; + + assert(img.getMetadata() === oldMetadata); + img.setMetadata(newMetadata); + + img.writeFile(join(tmpDir, filename), function(err) { + lwip.open(join(tmpDir, filename), function(err, imgWithMetadata) { + if (err) return done(err); + assert(imgWithMetadata.getMetadata() === newMetadata); + done(); + }); + }); + }); + }); + }); + +}); diff --git a/tests/images/hasMetadata.png b/tests/images/hasMetadata.png new file mode 100644 index 00000000..1a443b86 Binary files /dev/null and b/tests/images/hasMetadata.png differ diff --git a/tests/images/noMetadata.png b/tests/images/noMetadata.png new file mode 100644 index 00000000..76a899a2 Binary files /dev/null and b/tests/images/noMetadata.png differ diff --git a/tests/imgs.js b/tests/imgs.js index fa957508..21668a5d 100644 --- a/tests/imgs.js +++ b/tests/imgs.js @@ -14,7 +14,9 @@ module.exports = { rgb: join(__dirname, imbase, 'rgb.png'), noex: join(__dirname, imbase, 'rgbpng'), trans: join(__dirname, imbase, 'trans.png'), - inv: join(__dirname, imbase, 'invalid.png') + inv: join(__dirname, imbase, 'invalid.png'), + hasMetadata: join(__dirname, imbase, 'hasMetadata.png'), + noMetadata: join(__dirname, imbase, 'noMetadata.png') }, gif: { gs: join(__dirname, imbase, 'gs.gif'),