├── .gitignore ├── LICENSE ├── Makefile ├── Makefile.common ├── README.md ├── bin ├── Makefile └── redaction_main.cpp ├── lib ├── Makefile ├── bit_shifts.h ├── byte_swapping.cpp ├── byte_swapping.h ├── debug_flag.cpp ├── debug_flag.h ├── iptc.cpp ├── iptc.h ├── jpeg.cpp ├── jpeg.h ├── jpeg_decoder.cpp ├── jpeg_decoder.h ├── jpeg_dht.h ├── jpeg_marker.cpp ├── jpeg_marker.h ├── makernote.h ├── obscura_metadata.h ├── photoshop_3block.h ├── redaction.h ├── tiff_ifd.cpp ├── tiff_ifd.h ├── tiff_tag.cpp └── tiff_tag.h ├── notes.txt └── test ├── .gitignore ├── Makefile ├── bit_shifts_test.cpp ├── exiftest.cpp ├── ifdtest.cpp ├── jpegtest.cpp ├── metadatatest.cpp ├── test_redaction.cpp ├── test_utils.cpp ├── test_utils.h ├── testdata ├── devices │ ├── EvoRed.jpg │ ├── G1Desk.jpg │ ├── NexusOneShower.jpg │ ├── NokiaN810.jpg │ ├── droid.jpg │ ├── failing │ │ └── canon-1999.jpg │ ├── filmworks_filmscan.jpg │ ├── olympusC3030.jpg │ ├── olympusDSLR.jpg │ └── samsung1.jpg ├── ifdtest_golden.log ├── simple.jpg ├── simple_output_golden.log ├── simple_rawgrey_golden.pgm ├── test_noexif_golden.jpg ├── test_noexifgps_golden.jpg ├── test_nosensitive_golden.jpg ├── testexif.jpg ├── testexif_golden.exiftxt ├── testexif_removal_output_golden.log ├── windows.jpg ├── windows_out_redacted.jpg ├── windows_output_golden.log └── windows_rawgrey_golden.pgm ├── testreadwrite.cpp └── testreadwrite.sh /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.o 3 | *.a 4 | tags 5 | *.exe 6 | rawgrey.pgm 7 | backup*/* 8 | *.stackdump 9 | *.bak 10 | www.ozhiker.com/ 11 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2011 Andrew W. Senior andrew.senior[AT]gmail.com 2 | # Part of the Jpeg-Redaction-Library to read, parse, edit redact and 3 | # write JPEG/EXIF/JFIF images. 4 | # See https://github.com/asenior/Jpeg-Redaction-Library 5 | 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | 19 | 20 | # makefile for building JPEG/EXIF tests under cygwin 21 | include Makefile.common 22 | 23 | .PHONY: clean test depend test testbin 24 | 25 | testbin: 26 | cd test; $(MAKE) jpegtest 27 | 28 | run: 29 | test/jpegtest test/testdata/windows.jpg 30 | 31 | test: 32 | cd test; $(MAKE) test 33 | 34 | lib: 35 | cd lib; $(MAKE) lib 36 | 37 | clean: 38 | cd lib; $(MAKE) clean 39 | cd test; $(MAKE) clean 40 | cd bin; $(MAKE) clean 41 | 42 | dependall: 43 | cd lib; $(MAKE) dependlocal 44 | cd test; $(MAKE) dependlocal 45 | cd bin; $(MAKE) dependlocal 46 | 47 | cleandependall: 48 | cd lib; $(MAKE) cleandependlocal 49 | cd test; $(MAKE) cleandependlocal 50 | cd bin; $(MAKE) cleandependlocal 51 | -------------------------------------------------------------------------------- /Makefile.common: -------------------------------------------------------------------------------- 1 | # Common makefile for JPEG/EXIF redaction under cygwin 2 | DEPENDFILE=depends.mak 3 | MAKEDEPEND = makedepend -o.o -f$(DEPENDFILE) 4 | MKDEP_CMD = $(MAKEDEPEND) $(CPP_DEFINE_FLAGS) $(CC_INCLUDEDIR_FLAGS) -a 5 | 6 | CXXOFLAGS = $(INCLUDES) -O3 7 | CXXDFLAGS = $(INCLUDES) -g -DDEBUG 8 | CXXFLAGS = $(CXXDFLAGS) 9 | CFLAGS=$(CXXFLAGS) 10 | LIBS = -lm 11 | MKLIB = $(AR) $(ARFLAGS) $@ $?; ranlib $@ 12 | # cr2.cpp exif_data.cpp iptc.cpp iptc_tag.cpp 13 | 14 | CC = g++ 15 | 16 | .PHONY: default globaldefault 17 | 18 | globaldefault: default 19 | 20 | dependlocal: 21 | makedepend $(CFLAGS) -- *.cpp 22 | 23 | cleandependlocal: 24 | makedepend $(CFLAGS) -- 25 | 26 | dependall: 27 | cd ../; $(MAKE) dependall 28 | 29 | cleandependall: 30 | cd ../; $(MAKE) cleandependall 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | JPEG Redaction Library 2 | ====================== 3 | 4 | This is an open source C++ library that can parse EXIF format files- that is files in the standard EXIF format for representing JPEG-encoded data. 5 | The library can parse JPEG and EXIF contents and is intended to be used to rewrite the files in a modified form. 6 | The library has been developed for the guardianproject/[SecureSmartCam](https://github.com/guardianproject/SecureSmartCam) project. 7 | The main distinguishing feature of the library is the ability to erase regions of an image (for privacy protection) _in the compressed domain_. 8 | 9 | The basic library is now functionally complete, though with rough edges. 10 | 11 | * It can read & parse EXIF/JPEG files 12 | * Write JPEG files 13 | * Parse EXIF tags 14 | * Parse IPTC tags 15 | * Simple operations on EXIF tags 16 | * Redact (wipe) rectangular regions in JPEG images (and thumbnails). 17 | * Reverse image redactions. 18 | 19 | In the future it is intended that the library will support the following: 20 | 21 | * Wider range of redaction operations. (Blur, pixellize, overlay....) 22 | * More extensive API around editing EXIF & IPTC data. 23 | * Extension to MPEG4 video. 24 | 25 | Known problems: 26 | 27 | * Not extensively tested. (Range of test images, redaction region corner cases) 28 | * Doesn't preserve maker notes. 29 | * Redaction regions are stored as strips, so overlapping redaction rectangles cannot be independently encrypted. 30 | * Limited redaction methods (currently just grey rectangle, or horizontal strips of constant colour. 31 | 32 | The main library is in [/lib/](https://github.com/asenior/Jpeg-Redaction-Library/tree/master/lib). Unit tests and sample code can be found in [/test/](https://github.com/asenior/Jpeg-Redaction-Library/tree/master/test). 33 | 34 | [bin/redaction_main.cpp](https://github.com/asenior/Jpeg-Redaction-Library/blob/master/bin/redaction_main.cpp) is a simple command-line program to redact an image. 35 | 36 | Please let me know if you are interested in using the library, or if you have any feature requests. 37 | 38 | -------------------------------------------------------------------------------- /bin/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2011 Andrew W. Senior andrew.senior[AT]gmail.com 2 | # Part of the Jpeg-Redaction-Library to read, parse, edit redact and 3 | # write JPEG/EXIF/JFIF images. 4 | # See https://github.com/asenior/Jpeg-Redaction-Library 5 | 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | 19 | 20 | # Makefile for building JPEG/EXIF reading, writing, redaction 21 | # binaries. 22 | 23 | include ../Makefile.common 24 | 25 | BINARY = redact 26 | 27 | LIB = ../lib/libredact.a 28 | 29 | default: $(BINARY) 30 | 31 | test: 32 | cd ../test; $(MAKE) test 33 | 34 | $(LIB): 35 | cd ../lib; $(MAKE) lib 36 | 37 | $(BINARY): $(LIB) redaction_main.cpp 38 | $(CC) $(CXXFLAGS) -I../lib redaction_main.cpp $(LIBPATH) $(LIB) -o $@ 39 | 40 | .PHONY: clean cleanall clean_rawgrey clean_test \ 41 | $(LIB) 42 | 43 | clean: 44 | rm -f $(BINARY) 45 | 46 | veryclean: cleanall 47 | 48 | cleanall: clean 49 | cd ../lib; $(MAKE) clean 50 | cd ../test; $(MAKE) clean 51 | 52 | depend: 53 | $(MAKE) dependlocal 54 | 55 | # DO NOT DELETE 56 | -------------------------------------------------------------------------------- /bin/redaction_main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Andrew W. Senior andrew.senior[AT]gmail.com 2 | // Part of the Jpeg-Redaction-Library to read, parse, edit redact and 3 | // write JPEG/EXIF/JFIF images. 4 | // See https://github.com/asenior/Jpeg-Redaction-Library 5 | 6 | // This program is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | 16 | // You should have received a copy of the GNU General Public License 17 | // along with this program. If not, see . 18 | 19 | 20 | // Simple binary to apply redaction to a JPEG image 21 | // Or reverse that redaction. 22 | 23 | #include 24 | #include 25 | 26 | #include 27 | #include "jpeg.h" 28 | #include "redaction.h" 29 | using std::string; 30 | namespace jpeg_redaction { 31 | int reverse_redaction(const char * const filename) { 32 | try { 33 | Jpeg j2; 34 | bool success = j2.LoadFromFile(filename, true); 35 | Redaction::Region rect(50, 300, 50, 200); // l,r, t, b 36 | Redaction redaction; 37 | 38 | redaction.AddRegion(rect); 39 | j2.DecodeImage(&redaction, NULL); 40 | if (!redaction.ValidateStrips()) 41 | throw("Strips not valid"); 42 | j2.ReverseRedaction(redaction); 43 | std::string output_filename = "testout/testunredacted.jpg"; 44 | if (j2.Save(output_filename.c_str()) != 0) { 45 | fprintf(stderr, "Couldn't save %s\n", output_filename.c_str()); 46 | return 1; 47 | } 48 | } catch (const char *error) { 49 | fprintf(stderr, "Error: <%s> at outer level\n", error); 50 | return 1; 51 | } 52 | return 0; 53 | } 54 | int redact(const string &filename, 55 | const string &output, 56 | const string ®ions) { 57 | try { 58 | Jpeg j2; 59 | bool success = j2.LoadFromFile(filename.c_str(), true); 60 | Redaction redaction; 61 | 62 | redaction.AddRegions(regions); 63 | j2.DecodeImage(&redaction, NULL); 64 | if (!redaction.ValidateStrips()) 65 | throw("Strips not valid"); 66 | if (j2.Save(output.c_str()) != 0) { 67 | fprintf(stderr, "Couldn't save %s\n", output.c_str()); 68 | return 1; 69 | } 70 | } catch (const char *error) { 71 | fprintf(stderr, "Error: <%s> at outer level\n", error); 72 | return 1; 73 | } 74 | return 0; 75 | } 76 | } // namespace jpeg_redaction 77 | 78 | int main(int argc, char **argv) { 79 | std::string filename; 80 | std::string outfile; 81 | std::string regions; 82 | int start_arg = 1; 83 | if (argc - start_arg <= 2) { 84 | fprintf(stderr, "%s \n" 85 | "method is one of [c]opystrip, [S]olid, [p]ixellate," 86 | "[i]nverse pixellate\n", argv[0]); 87 | exit(1); 88 | } 89 | filename = argv[start_arg]; 90 | outfile = argv[start_arg+1]; 91 | regions = argv[start_arg+2]; 92 | return jpeg_redaction::redact(filename, outfile, regions); 93 | } 94 | -------------------------------------------------------------------------------- /lib/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2011 Andrew W. Senior andrew.senior[AT]gmail.com 2 | # Part of the Jpeg-Redaction-Library to read, parse, edit redact and 3 | # write JPEG/EXIF/JFIF images. 4 | # See https://github.com/asenior/Jpeg-Redaction-Library 5 | 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | 19 | 20 | # makefile for building JPEG/EXIF tests under cygwin 21 | include ../Makefile.common 22 | 23 | LOCALLIB=libredact.a 24 | 25 | default: lib 26 | 27 | lib: $(LOCALLIB) 28 | 29 | SRCS = debug_flag.cpp iptc.cpp jpeg.cpp jpeg_decoder.cpp jpeg_marker.cpp \ 30 | byte_swapping.cpp tiff_ifd.cpp tiff_tag.cpp 31 | 32 | OBJS = $(SRCS:.cpp=.o) 33 | 34 | $(OBJS): $(SRCS:.cpp=.h) 35 | 36 | 37 | $(LOCALLIB): $(OBJS) 38 | $(MKLIB) 39 | 40 | .PHONY: clean lib test 41 | 42 | test: 43 | cd ../test; $(MAKE) test 44 | 45 | clean: 46 | rm -f $(OBJS) $(LOCALLIB) 47 | 48 | depend: 49 | $(MAKE) dependlocal 50 | 51 | # DO NOT DELETE 52 | 53 | -------------------------------------------------------------------------------- /lib/bit_shifts.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Andrew W. Senior andrew.senior[AT]gmail.com 2 | // Part of the Jpeg-Redaction-Library to read, parse, edit redact and 3 | // write JPEG/EXIF/JFIF images. 4 | // See https://github.com/asenior/Jpeg-Redaction-Library 5 | 6 | // This program is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | 16 | // You should have received a copy of the GNU General Public License 17 | // along with this program. If not, see . 18 | 19 | 20 | // Class to apply bit operations to a (JPEG) bit stream. 21 | #ifndef INCLUDE_JPEG_REDACTION_LIBRARY_BIT_SHIFTS 22 | #define INCLUDE_JPEG_REDACTION_LIBRARY_BIT_SHIFTS 23 | #include "debug_flag.h" 24 | 25 | namespace jpeg_redaction { 26 | class BitShifts { 27 | public: 28 | // Shift the block after the start-th bit up by shift, 29 | // increasing the size of data. 30 | static bool ShiftTail(std::vector *data, 31 | int *data_bits, 32 | int start, 33 | int shift) { 34 | if (shift < 0) 35 | throw("ShiftTail: shift is negative"); 36 | 37 | if (debug > 0) 38 | printf("Shifting data (%d bits in %zu bytes) up by %d bits " 39 | "starting at b%d B%d. New length %d\n", 40 | *data_bits, data->size(), shift, start, 41 | start/8, *data_bits + shift); 42 | 43 | // Enough space for everything. 44 | *data_bits += shift; 45 | data->resize((*data_bits + 7) /8); 46 | 47 | // Shift up the end bits (check the ending?) 48 | // This is Extremely approximate 49 | // (*data)[data->size()-1] = 0; 50 | int bit_shift = shift % 8; 51 | const unsigned char low_mask = (1 << bit_shift) -1; 52 | const unsigned char high_mask = 0xff - low_mask; 53 | // The byte where the last bit is located. 54 | const int last_byte = (*data_bits - 1) / 8; 55 | const int first_byte = (start + shift) / 8; 56 | // The byte where the very last bit in the last byte would come from. 57 | int src_byte = (last_byte * 8 + 7 - shift) / 8; 58 | // Go through all the destination bytes i and pull their bits 59 | // from the two source bytes. 60 | unsigned char byte; 61 | for (int i = last_byte; i >= first_byte; --i, --src_byte) { 62 | byte = ((*data)[src_byte] & high_mask) >> bit_shift; 63 | if (src_byte > 0) 64 | byte |= ((*data)[src_byte - 1] & low_mask) << (8 - bit_shift); 65 | if (i == first_byte) { 66 | // Only affect the lowest start_offset bits. 67 | const int start_offset = 8 - ((start + shift) % 8); 68 | const unsigned char final_low_mask = (1 << start_offset) -1; 69 | const unsigned char final_high_mask = 0xff - final_low_mask; 70 | if (debug > 0) 71 | printf("fb %d flm %d fhm %d so %d\n", 72 | i, final_low_mask, final_high_mask, start_offset); 73 | (*data)[i] = ((*data)[i] & final_high_mask) | (byte & final_low_mask); 74 | } else { 75 | (*data)[i] = byte; 76 | } 77 | } 78 | if (debug > 0) 79 | printf("Ended with src_byte %d\n", src_byte); 80 | return true; 81 | } 82 | // Take the first insert_length bits from insertion 83 | // copy them to data at position start, overwriting old bits. 84 | static bool Overwrite(std::vector *data, 85 | int length, 86 | int start, 87 | const std::vector &overwrite, 88 | int overwrite_length) { 89 | if (overwrite_length + start > length) 90 | throw("Length mismatch in Overwrite"); 91 | if (length > 8 * data->size()) 92 | throw("Data length error in Overwrite"); 93 | if (overwrite_length > 8 * overwrite.size()) 94 | throw("Overwrite length error in Overwrite"); 95 | int start_byte = start / 8; 96 | // Byte with the last bit in it. 97 | int end_byte = (start + overwrite_length -1) / 8; 98 | // How much the overwrite has to be shifted up to match the start. 99 | int bit_shift = start % 8; 100 | // The high bit_shift bits. 101 | const unsigned char low_mask = (1 << (8 - bit_shift)) -1; 102 | const unsigned char high_mask = 0xff - low_mask; 103 | int ov = 0; 104 | for (int i = start_byte; i <= end_byte; ++i, ++ov) { 105 | unsigned char byte = 0; 106 | if (i == start_byte) 107 | byte = (*data)[i] & high_mask; 108 | else 109 | byte = (overwrite[ov-1] << (8 - bit_shift)) & high_mask; 110 | byte += (overwrite[ov] >> bit_shift); 111 | if (i == end_byte) { 112 | const int shift_final = 1 + (start + overwrite_length - 1) % 8; 113 | const unsigned char low_mask_final = (1 << (8 - shift_final)) -1; 114 | const unsigned char high_mask_final = 0xff - low_mask_final; 115 | if (debug > 0) 116 | printf("i %d shift_final %d mask h%0x l%x\n", 117 | i, shift_final, high_mask_final, low_mask_final); 118 | byte = (byte & high_mask_final) | ((*data)[i] & low_mask_final); 119 | } 120 | (*data)[i] = byte; 121 | } 122 | return true; 123 | } 124 | // Take the first insert_length bits from insertion 125 | // copy them to data at position start, shifting up old bits. 126 | static bool Insert(std::vector *data, 127 | int *length, 128 | int start, 129 | const std::vector &insertion, 130 | int insert_length) { 131 | ShiftTail(data, length, start, insert_length); 132 | return Overwrite(data, *length, start, insertion, insert_length); 133 | } 134 | // Pad the last byte with ones. 135 | static int PadLastByte(std::vector *data, int bits) { 136 | if (bits > data->size() * 8) throw("too many bits in PadLastByte"); 137 | int used_bits_last_byte = ((bits - 1) % 8) + 1; 138 | if (used_bits_last_byte != 8) { 139 | unsigned char unused_bits_mask = (1 << (8 - used_bits_last_byte)) - 1; 140 | if (debug > 0) 141 | printf("padding %d bits mask %02x\n", 142 | 8-used_bits_last_byte, unused_bits_mask); 143 | data->back() |= unused_bits_mask; 144 | } 145 | } 146 | }; 147 | } // namespace jpeg_redaction 148 | #endif // INCLUDE_JPEG_REDACTION_LIBRARY_BIT_SHIFTS 149 | 150 | -------------------------------------------------------------------------------- /lib/byte_swapping.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Andrew W. Senior andrew.senior[AT]gmail.com 2 | // Part of the Jpeg-Redaction-Library to read, parse, edit redact and 3 | // write JPEG/EXIF/JFIF images. 4 | // See https://github.com/asenior/Jpeg-Redaction-Library 5 | 6 | // This program is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | 16 | // You should have received a copy of the GNU General Public License 17 | // along with this program. If not, see . 18 | 19 | 20 | #include "byte_swapping.h" 21 | 22 | bool ArchBigEndian() { 23 | short s = 256; 24 | return (*(unsigned char*)&s); 25 | } 26 | 27 | void ByteSwapInPlace(unsigned short *d, int num) { 28 | unsigned char *db = (unsigned char *)d; 29 | while (--num >= 0) { 30 | unsigned char c = *db; 31 | *db = *(db + 1); 32 | *(db+1) = c; 33 | db += sizeof(*d); 34 | } 35 | } 36 | void ByteSwapInPlace(unsigned int *d, int num) { 37 | unsigned char *db = (unsigned char *)d; 38 | while (--num >= 0) { 39 | unsigned char c = *db; 40 | *db = *(db + 3); 41 | *(db+3) = c; 42 | c = *(db+1); 43 | *(db +1) = *(db + 2); 44 | *(db+2) = c; 45 | db += sizeof(*d); 46 | } 47 | } 48 | void ByteSwapInPlace(short *d, int num) { 49 | ByteSwapInPlace((unsigned short *)d, num); 50 | } 51 | void ByteSwapInPlace(int *d, int num) { 52 | ByteSwapInPlace((unsigned int *)d, num); 53 | } 54 | void ByteSwapInPlace(unsigned char *d, int num, int typelen) { 55 | if (typelen == 1) 56 | return; 57 | else if (typelen == 2) 58 | ByteSwapInPlace((unsigned short *)d, num); 59 | else if (typelen == 4) 60 | ByteSwapInPlace((unsigned int *)d, num); 61 | else if (typelen == 8) { // Tiff specific: rational 62 | ByteSwapInPlace((unsigned int *)d, num); 63 | ByteSwapInPlace(((unsigned int *)d)+1, num); 64 | } 65 | else throw("Cant swap this length."); 66 | } 67 | -------------------------------------------------------------------------------- /lib/byte_swapping.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Andrew W. Senior andrew.senior[AT]gmail.com 2 | // Part of the Jpeg-Redaction-Library to read, parse, edit redact and 3 | // write JPEG/EXIF/JFIF images. 4 | // See https://github.com/asenior/Jpeg-Redaction-Library 5 | 6 | // This program is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | 16 | // You should have received a copy of the GNU General Public License 17 | // along with this program. If not, see . 18 | 19 | 20 | #ifndef INCLUDE_BYTE_SWAPPING 21 | #define INCLUDE_BYTE_SWAPPING 22 | 23 | // Returns true if current machine is big endian (Motorola) 24 | // vs little endian (Intel) 25 | bool ArchBigEndian(); 26 | 27 | // General byte swapping functions. 28 | // Swap a contiguous block of num objects of a particular type. 29 | void ByteSwapInPlace(unsigned short *d, int num); 30 | void ByteSwapInPlace(unsigned int *d, int num); 31 | void ByteSwapInPlace(short *d, int num); 32 | void ByteSwapInPlace(int *d, int num); 33 | // Block num objects of size typelen 34 | void ByteSwapInPlace(unsigned char *d, int num, int typelen); 35 | 36 | #define byteswap2(x) (((x & 0xff)<<8) | ((x & 0xff00)>>8)) 37 | #define byteswap4(x) (((x & 0xff)<<24) | ((x & 0xff00)<<8) | ((x & 0xff0000)>>8) | ((x & 0xff000000)>>24)) 38 | 39 | #endif // INCLUDE_BYTE_SWAPPING 40 | -------------------------------------------------------------------------------- /lib/debug_flag.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Andrew W. Senior andrew.senior[AT]gmail.com 2 | // Part of the Jpeg-Redaction-Library to read, parse, edit redact and 3 | // write JPEG/EXIF/JFIF images. 4 | // See https://github.com/asenior/Jpeg-Redaction-Library 5 | 6 | // This program is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | 16 | // You should have received a copy of the GNU General Public License 17 | // along with this program. If not, see . 18 | 19 | 20 | // Define a debug flag to turn on printing error messages. 21 | // 22 | #include "debug_flag.h" 23 | 24 | namespace jpeg_redaction { 25 | int debug = 0; 26 | } // namespace redaction 27 | -------------------------------------------------------------------------------- /lib/debug_flag.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Andrew W. Senior andrew.senior[AT]gmail.com 2 | // Part of the Jpeg-Redaction-Library to read, parse, edit redact and 3 | // write JPEG/EXIF/JFIF images. 4 | // See https://github.com/asenior/Jpeg-Redaction-Library 5 | 6 | // This program is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | 16 | // You should have received a copy of the GNU General Public License 17 | // along with this program. If not, see . 18 | 19 | 20 | // iptc.h: interface for the Iptc class. 21 | // 22 | ////////////////////////////////////////////////////////////////////// 23 | 24 | #if !defined(INCLUDE_DEBUG_FLAG) 25 | #define INCLUDE_DEBUG_FLAG 26 | namespace jpeg_redaction { 27 | extern int debug; 28 | } // namespace redaction 29 | #endif // INCLUDE_IPTC 30 | -------------------------------------------------------------------------------- /lib/iptc.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Andrew W. Senior andrew.senior[AT]gmail.com 2 | // Part of the Jpeg-Redaction-Library to read, parse, edit redact and 3 | // write JPEG/EXIF/JFIF images. 4 | // See https://github.com/asenior/Jpeg-Redaction-Library 5 | 6 | // This program is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | 16 | // You should have received a copy of the GNU General Public License 17 | // along with this program. If not, see . 18 | 19 | 20 | #include "iptc.h" 21 | namespace jpeg_redaction { 22 | const unsigned char Iptc::tag_marker_ = 0x1c; 23 | } // namespace redaction 24 | -------------------------------------------------------------------------------- /lib/iptc.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Andrew W. Senior andrew.senior[AT]gmail.com 2 | // Part of the Jpeg-Redaction-Library to read, parse, edit redact and 3 | // write JPEG/EXIF/JFIF images. 4 | // See https://github.com/asenior/Jpeg-Redaction-Library 5 | 6 | // This program is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | 16 | // You should have received a copy of the GNU General Public License 17 | // along with this program. If not, see . 18 | 19 | 20 | // iptc.h: interface for the Iptc class. 21 | // 22 | ////////////////////////////////////////////////////////////////////// 23 | 24 | #if !defined(INCLUDE_IPTC) 25 | #define INCLUDE_IPTC 26 | 27 | #include 28 | #include 29 | #include "byte_swapping.h" 30 | 31 | using std::vector; 32 | 33 | // Parse an IPTC block from a file. 34 | // e.g. within a photoshop 3 block 35 | // See http://www.fileformat.info/format/psd/egff.htm 36 | 37 | namespace jpeg_redaction { 38 | class Iptc 39 | { 40 | public: 41 | class IptcTag 42 | { 43 | public: 44 | /* typedef enum { 45 | iptc_format_unknown =0 , 46 | iptc_format_binary = 1, 47 | iptc_format_byte = 2, 48 | iptc_format_short = 3, 49 | iptc_format_long = 4, 50 | iptc_format_string = 5, 51 | iptc_format_numeric_string = 6, 52 | iptc_format_date = 8, 53 | iptc_format_time =9 54 | } iptc_format; */ 55 | 56 | 57 | typedef enum { 58 | iptc_tag_model_version = 0, /* begin record 1 tags */ 59 | iptc_tag_destination = 5, 60 | iptc_tag_file_format = 20, 61 | iptc_tag_file_version = 22, 62 | iptc_tag_service_id = 30, 63 | iptc_tag_envelope_num = 40, 64 | iptc_tag_product_id = 50, 65 | iptc_tag_envelope_priority = 60, 66 | iptc_tag_date_sent = 70, 67 | iptc_tag_time_sent = 80, 68 | iptc_tag_character_set = 90, 69 | iptc_tag_uno = 100, 70 | iptc_tag_arm_id = 120, 71 | iptc_tag_arm_version = 122, /* end record 1 tags */ 72 | iptc_tag_record_version = 0, /* begin record 2 tags */ 73 | iptc_tag_object_type = 3, 74 | iptc_tag_object_attribute = 4, 75 | iptc_tag_object_name = 5, 76 | iptc_tag_edit_status = 7, 77 | iptc_tag_editorial_update = 8, 78 | iptc_tag_urgency = 10, 79 | iptc_tag_subject_reference = 12, 80 | iptc_tag_category = 15, 81 | iptc_tag_suppl_category = 20, 82 | iptc_tag_fixture_id = 22, 83 | iptc_tag_keywords = 25, 84 | iptc_tag_content_loc_code = 26, 85 | iptc_tag_content_loc_name = 27, 86 | iptc_tag_release_date = 30, 87 | iptc_tag_release_time = 35, 88 | iptc_tag_expiration_date = 37, 89 | iptc_tag_expiration_time = 38, 90 | iptc_tag_special_instructions = 40, 91 | iptc_tag_action_advised = 42, 92 | iptc_tag_reference_service = 45, 93 | iptc_tag_reference_date = 47, 94 | iptc_tag_reference_number = 50, 95 | iptc_tag_date_created = 55, 96 | iptc_tag_time_created = 60, 97 | iptc_tag_digital_creation_date = 62, 98 | iptc_tag_digital_creation_time = 63, 99 | iptc_tag_originating_program = 65, 100 | iptc_tag_program_version = 70, 101 | iptc_tag_object_cycle = 75, 102 | iptc_tag_byline = 80, 103 | iptc_tag_byline_title = 85, 104 | iptc_tag_city = 90, 105 | iptc_tag_sublocation = 92, 106 | iptc_tag_state = 95, 107 | iptc_tag_country_code = 100, 108 | iptc_tag_country_name = 101, 109 | iptc_tag_orig_trans_ref = 103, 110 | iptc_tag_headline = 105, 111 | iptc_tag_credit = 110, 112 | iptc_tag_source = 115, 113 | iptc_tag_copyright_notice = 116, 114 | iptc_tag_picasa_unknown = 117, 115 | iptc_tag_contact = 118, 116 | iptc_tag_caption = 120, 117 | iptc_tag_writer_editor = 122, 118 | iptc_tag_rasterized_caption = 125, 119 | iptc_tag_image_type = 130, 120 | iptc_tag_image_orientation = 131, 121 | iptc_tag_language_id = 135, 122 | iptc_tag_audio_type = 150, 123 | iptc_tag_audio_sampling_rate = 151, 124 | iptc_tag_audio_sampling_res = 152, 125 | iptc_tag_audio_duration = 153, 126 | iptc_tag_audio_outcue = 154, 127 | iptc_tag_preview_format = 200, 128 | iptc_tag_preview_format_ver = 201, 129 | iptc_tag_preview_data = 202, /* end record 2 tags */ 130 | 131 | iptc_tag_size_mode = 10, /* begin record 7 tags */ 132 | iptc_tag_max_subfile_size = 20, 133 | iptc_tag_size_announced = 90, 134 | iptc_tag_max_object_size = 95, /* end record 7 tags */ 135 | 136 | iptc_tag_subfile = 10, /* record 8 tags */ 137 | iptc_tag_confirmed_data_size = 10 /* record 9 tags */ 138 | } iptc_tag; 139 | 140 | IptcTag(FILE *pFile) 141 | { 142 | unsigned char datasetmarker; 143 | int iRV = fread(&datasetmarker, sizeof(unsigned char), 1, pFile); 144 | if (iRV !=1 || datasetmarker != Iptc::tag_marker_) 145 | throw("Got a bad tag marker"); 146 | 147 | iRV = fread(&record_, sizeof(unsigned char), 1, pFile); 148 | if (iRV != 1) throw("IPTC read fail"); 149 | 150 | iRV = fread(&tag_, sizeof(unsigned char), 1, pFile); 151 | if (iRV != 1) throw("IPTC read fail"); 152 | 153 | unsigned long length = 0; 154 | unsigned short shortlength; 155 | iRV = fread(&shortlength, sizeof(unsigned short), 1, pFile); 156 | if (iRV != 1) throw("IPTC read fail"); 157 | const bool arch_big_endian = ArchBigEndian(); 158 | if (!arch_big_endian) ByteSwapInPlace(&shortlength, 1); 159 | 160 | if (shortlength & 0x8000) { // Top bit is set: p14 of IPTC IIMV 161 | shortlength &= 0x7ffff; // Number of bytes in which the actual length is stored. 162 | if (shortlength > 4) throw("Can't handle payloads longer than 2^32 bytes"); 163 | while (shortlength > 0) { 164 | unsigned char b; 165 | iRV = fread(&b, sizeof(unsigned char), 1, pFile); 166 | length += (b << (8*shortlength)); // TODO Maybe wrong-endian?? 167 | --shortlength; 168 | } 169 | } else { 170 | length = shortlength; 171 | } 172 | 173 | data_.resize(length); 174 | iRV = fread(&data_[0], sizeof(unsigned char), length, pFile); 175 | 176 | if (iRV != length) throw("IPTC read fail length"); 177 | printf("IPTC dataset %d,%d len %lu", record_, tag_, length); 178 | for (int i =0; i < length; ++i) 179 | printf("%c", data_[i]); 180 | printf("\n"); 181 | } 182 | 183 | 184 | virtual ~IptcTag() { } 185 | 186 | 187 | // Size of the tag, including the marker, record no, tag & data. 188 | int DataLength() const { 189 | return data_.size() + 5; 190 | } 191 | 192 | // Return number of bytes written. 193 | int Write(FILE *pFile) 194 | { 195 | const unsigned short length = data_.size(); 196 | printf("Writing IPTC tag %d\n", tag_, length); 197 | 198 | int iRV = fwrite(&Iptc::tag_marker_, sizeof(unsigned char), 1, pFile); 199 | if (iRV != 1) throw("IPTC write fail"); 200 | 201 | iRV = fwrite(&record_, sizeof(unsigned char), 1, pFile); 202 | if (iRV != 1) throw("IPTC write fail record"); 203 | 204 | iRV = fwrite(&tag_, sizeof(unsigned char), 1, pFile); 205 | if (iRV != 1) throw("IPTC write fail tag"); 206 | 207 | unsigned short length_swap = length; 208 | const bool arch_big_endian = ArchBigEndian(); 209 | if (!arch_big_endian) ByteSwapInPlace(&length_swap, 1); 210 | 211 | iRV = fwrite(&length_swap, sizeof(unsigned short), 1, pFile); 212 | if (iRV != 1) throw("IPTC write fail length"); 213 | 214 | iRV = fwrite(&data_[0], sizeof(unsigned char), length, pFile); 215 | if (iRV != length) throw("IPTC write fail data"); 216 | 217 | return 3 * sizeof(unsigned char) + sizeof(unsigned short) + length; 218 | } 219 | 220 | unsigned char tag_; 221 | std::vector data_; 222 | unsigned char record_; 223 | }; // End of IptcTag 224 | 225 | 226 | // Read an IPTC block in from a file. 227 | Iptc(FILE *pFile, unsigned int totallength) 228 | { 229 | unsigned char bindummy; 230 | const int mintaglength = 5; 231 | int remaininglength = totallength; 232 | int iRV; 233 | while (remaininglength >= mintaglength) { 234 | IptcTag *tag= new IptcTag(pFile); 235 | if (tag == NULL) 236 | throw("Got null IPTC tag"); 237 | int thistaglength = tag->DataLength(); 238 | tags_.push_back(tag); 239 | if (thistaglength > remaininglength) throw("IPTC tag too long"); 240 | remaininglength -= thistaglength; 241 | } 242 | if (remaininglength != 0) 243 | throw("IPTC block length error"); 244 | 245 | printf("iptc totallength is %d\n", totallength); 246 | if (totallength %2 == 1) // Length is rounded to be even. 247 | iRV = fread(&bindummy, sizeof(unsigned char), 1, pFile); 248 | } 249 | 250 | virtual ~Iptc() 251 | { 252 | for(int i = 0; i< tags_.size(); ++i) { 253 | delete tags_[i]; 254 | } 255 | tags_.clear(); 256 | } 257 | 258 | int Write(FILE *pFile) 259 | { 260 | int iRV = 0; 261 | int length = 0; 262 | for (int i = 0; i < tags_.size(); ++i) { 263 | iRV = tags_[i]->Write(pFile); 264 | length += iRV; 265 | } 266 | 267 | unsigned char bindummy; 268 | if ((length % 2) == 1) {// Length is rounded to be even. 269 | iRV = fwrite(&bindummy, sizeof(unsigned char), 1, pFile); 270 | length++; 271 | } 272 | printf("iptc length is %d\n", length); 273 | return length; 274 | } 275 | static const unsigned char tag_marker_; 276 | static const unsigned int tag_bim_; 277 | static const unsigned short tag_bim_iptc_; 278 | 279 | std::vector tags_; 280 | }; 281 | } // namespace jpeg_redaction 282 | 283 | #endif // INCLUDE_IPTC 284 | -------------------------------------------------------------------------------- /lib/jpeg.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Andrew W. Senior andrew.senior[AT]gmail.com 2 | // Part of the Jpeg-Redaction-Library to read, parse, edit redact and 3 | // write JPEG/EXIF/JFIF images. 4 | // See https://github.com/asenior/Jpeg-Redaction-Library 5 | 6 | // This program is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | 16 | // You should have received a copy of the GNU General Public License 17 | // along with this program. If not, see . 18 | 19 | 20 | // jpeg.h: interface for the Jpeg class. 21 | // 22 | ////////////////////////////////////////////////////////////////////// 23 | 24 | #ifndef INCLUDE_JPEG 25 | #define INCLUDE_JPEG 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include "tiff_ifd.h" 32 | #include "obscura_metadata.h" 33 | 34 | namespace jpeg_redaction { 35 | 36 | class Iptc; 37 | class JpegDHT; 38 | class JpegMarker; 39 | class Redaction; 40 | 41 | class Photoshop3Block; 42 | class Jpeg { 43 | public: 44 | enum markers { jpeg_soi = 0xFFD8, 45 | jpeg_sof0 = 0xFFC0, 46 | jpeg_sof2 = 0xFFC2, 47 | jpeg_dht = 0xFFC4, 48 | jpeg_eoi = 0xFFD9, 49 | jpeg_sos = 0xFFDA, 50 | jpeg_dqt = 0xFFDB, 51 | jpeg_dri = 0xFFDD, 52 | jpeg_com = 0xFFDE, 53 | jpeg_app = JPEG_APP0}; 54 | class JpegComponent { 55 | public: 56 | JpegComponent(unsigned char *d) { 57 | id_ = d[0]; 58 | v_factor_ = d[1] & 0xf; 59 | h_factor_ = d[1] >> 4; 60 | table_ = d[2]; 61 | } 62 | void Print() { 63 | printf("Component %d H %d V %d, Table %d\n", 64 | id_, h_factor_, v_factor_, table_); 65 | } 66 | int id_; 67 | int h_factor_; 68 | int v_factor_; 69 | int table_; 70 | }; 71 | // Trivial constructor. 72 | Jpeg() : filename_(""), width_(0), height_(0), photoshop3_(NULL) {}; 73 | virtual ~Jpeg(); 74 | // Construct from a file. loadall indicates whether to load all 75 | // data blocks, or just parse the file and extract metadata. 76 | bool LoadFromFile(char const * const pczFilename, bool loadall); 77 | int LoadFromFile(FILE *pFile, bool loadall, int offset); 78 | 79 | // Parse the JPEG data and redact if Redaction regions are supplied. 80 | // If pgm_save_filename is provided, write the decoded image to that file. 81 | 82 | void DecodeImage(Redaction *redaction, const char *pgm_save_filename); 83 | // Invert the redaction by pasting in the strips from redaction. 84 | int ReverseRedaction(const Redaction &redaction); 85 | int GetHeight() const { return height_; } 86 | int GetWidth() const { return width_; } 87 | Jpeg *GetThumbnail(); 88 | int RedactThumbnail(Redaction *redaction); 89 | // Save the current (possibly redacted) version of the JPEG out. 90 | // Return 0 on success. 91 | int Save(const char * const filename); 92 | int Save(FILE *pFile); 93 | 94 | const char *MarkerName(int marker) const; 95 | Iptc *GetIptc(); 96 | // If there is one, return the first IFD. 97 | TiffIfd *GetIFD() { 98 | if (ifds_.size() >= 1) 99 | return ifds_[0]; 100 | return NULL; 101 | } 102 | // Set the obscura metadata block, deleting any previous data. 103 | void SetObscuraMetaData(unsigned int length, 104 | const unsigned char *data) { 105 | obscura_metadata_.SetDescriptor(length, data); 106 | } 107 | // Find the metadata if any. 108 | const unsigned char *GetObscuraMetaData(unsigned int *length) const { 109 | return obscura_metadata_.GetDescriptor(length); 110 | } 111 | // Return a pointer to the Exif IFD if present. 112 | ExifIfd *GetExif() { 113 | if (ifds_.size() >= 1) 114 | return ifds_[0]->GetExif(); 115 | // for (int i=0; i < ifds_.size(); ++i) 116 | // if (ifds_[i]->GetExif()) 117 | // return ifds_[i]->GetExif(); 118 | return NULL; 119 | } 120 | 121 | TiffTag *FindTag(int tag_num) { 122 | for (int i = 0; i < ifds_.size(); ++i) { 123 | TiffTag *tag = ifds_[i]->FindTag(tag_num); 124 | if (tag) return tag; 125 | } 126 | } 127 | int RemoveTag(int tag) { 128 | int removed = 0; 129 | for (int i = 0; i < ifds_.size(); ++i) { 130 | bool removed_this = ifds_[i]->RemoveTag(tag); 131 | if (removed_this) ++removed; 132 | } 133 | return removed; 134 | } 135 | // Remove the whole IPTC record. 136 | int RemoveIPTC(); 137 | // Add here a list of all the tags that are considered sensitive. 138 | int RemoveAllSensitive() { 139 | RemoveTag(TiffTag::tag_ExifIFDPointer); 140 | RemoveTag(TiffTag::tag_GpsInfoIFDPointer); 141 | RemoveTag(TiffTag::tag_MakerNote); 142 | RemoveTag(TiffTag::tag_Make); 143 | RemoveTag(TiffTag::tag_Model); 144 | RemoveIPTC(); 145 | // e.g. times, owner 146 | } 147 | 148 | // Return a pointer to the marker of the given type. 149 | JpegMarker *GetMarker(int marker); 150 | // If it's a SOF or SOS we pass a slice. 151 | JpegMarker *AddSOMarker(int location, int length, 152 | FILE *pFile, bool loadall, int slice); 153 | // The length is the length from the file, including the storage for length. 154 | JpegMarker *AddMarker(int marker, int location, int length, 155 | FILE *pFile, bool loadall); 156 | protected: 157 | // After loading an SO Marker, remove the stuff bytes so the bitstream 158 | // can be read more easily. 159 | void RemoveStuffBytes(); 160 | void BuildDHTs(const JpegMarker *dht_block); 161 | int ReadSOSMarker(FILE *pFile, unsigned int blockloc, bool loadall); 162 | int LoadExif(FILE *pFile, unsigned int blockloc, bool loadall); 163 | 164 | std::vector markers_; 165 | int width_; 166 | int height_; 167 | int softype_; 168 | std::vector ifds_; 169 | std::string filename_; 170 | // unsigned int datalen_, dataloc_; 171 | 172 | unsigned int restartinterval_; 173 | 174 | Photoshop3Block *photoshop3_; 175 | std::vector dhts_; 176 | std::vector components_; 177 | ObscuraMetadata obscura_metadata_; 178 | }; // Jpeg 179 | } // namespace redaction 180 | 181 | #endif // INCLUDE_JPEG 182 | -------------------------------------------------------------------------------- /lib/jpeg_decoder.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Andrew W. Senior andrew.senior[AT]gmail.com 2 | // Part of the Jpeg-Redaction-Library to read, parse, edit redact and 3 | // write JPEG/EXIF/JFIF images. 4 | // See https://github.com/asenior/Jpeg-Redaction-Library 5 | 6 | // This program is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | 16 | // You should have received a copy of the GNU General Public License 17 | // along with this program. If not, see . 18 | 19 | 20 | #ifndef INCLUDE_JPEGDECODER 21 | #define INCLUDE_JPEGDECODER 22 | // JpegDecoder parse the JPEG encoded data. 23 | // 2011 Andrew Senior 24 | #include 25 | #include 26 | #include 27 | #include "bit_shifts.h" 28 | #include "jpeg.h" 29 | #include "jpeg_dht.h" 30 | #include "redaction.h" 31 | 32 | extern int debug; 33 | namespace jpeg_redaction { 34 | class JpegDHT; 35 | class Jpeg; 36 | class JpegDecoder { 37 | public: 38 | JpegDecoder(int w, int h, 39 | unsigned char *data, 40 | int length, // in bits of the data. 41 | const std::vector &dhts, 42 | const std::vector *components); 43 | 44 | 45 | // Decode the whole image. 46 | void Decode(Redaction *redaction); 47 | 48 | const std::vector &GetRedactedData() { 49 | if (redaction_bit_pointer_ > (redacted_data_.size() * 8) || 50 | redaction_bit_pointer_ < (redacted_data_.size() * 8) - 8) { 51 | throw("RedactedData length mismatch"); 52 | } 53 | BitShifts::PadLastByte(&redacted_data_, redaction_bit_pointer_); 54 | return redacted_data_; 55 | } 56 | // Write the grey scale decoded image to a file. 57 | // Return 0 on success. 58 | int WriteImageData(const char *const filename) { 59 | FILE *pFile = fopen(filename, "wb"); 60 | if (pFile == NULL) 61 | return 1; 62 | int rv = fprintf(pFile, "P5\n%d %d %d\n", w_blocks_, h_blocks_, 255); 63 | printf("Saving Image %d, %d = %d pixels. %lu bytes\n", 64 | w_blocks_, h_blocks_, w_blocks_*h_blocks_, image_data_.size()); 65 | rv = fwrite(&image_data_[0], sizeof(unsigned char), 66 | image_data_.size(), pFile); 67 | fclose(pFile); 68 | if (rv != image_data_.size()) 69 | return 1; 70 | return 0; 71 | } 72 | // ImageData arrived as zig-zags of blocks within MCU. 73 | // They were added linearly to image_data_ 74 | // Rearrange them into proper raster order. 75 | void ReorderImageData() { 76 | int hf = (*components_)[0]->h_factor_; 77 | int vf = (*components_)[0]->v_factor_; 78 | printf("Reordering Image %d, %d = %d pixels. %lu bytes. h,v %d,%d\n", 79 | w_blocks_, h_blocks_, w_blocks_ * h_blocks_, 80 | image_data_.size(), hf, vf); 81 | int block_size = vf * hf; 82 | std::vector c(w_blocks_ * h_blocks_, 0); 83 | for (int i = 0 ; i < w_blocks_ * h_blocks_; ++i) { 84 | int block = i / block_size; 85 | int sub = i % block_size; 86 | int x = hf * (block % (w_blocks_/hf)) + (sub % hf); 87 | int y = vf * (block / (w_blocks_/hf)) + (sub / hf); 88 | int newpos = x + y* w_blocks_; 89 | if (x > w_blocks_ || y > h_blocks_) 90 | printf("Reordered Data from %d: %d,%d out of bounds\n", 91 | i, x, y); 92 | c[newpos] = image_data_[i]; 93 | } 94 | image_data_.assign(c.begin(), c.end()); 95 | } 96 | // Return the current length of the data block (in bits). 97 | int GetBitLength() const { return length_; } 98 | 99 | 100 | protected: 101 | // Fill current_bits up to a complete word. 102 | // Data bits are "as written" ie first bit is highest order bit of first 103 | // byte. current_bits is a word where the next bit is the highest order 104 | // bit. We shuffle in data so that there are at least N (16?) bits 105 | // at all times. 106 | // Assumes that stuff bytes and tags have already been removed. 107 | // data_pointer is the next bit to shift into current_bits_ 108 | // num_bits_ is the number of bits in current_bits_ 109 | void FillBits() { 110 | int byte = data_pointer_ >> 3; 111 | // The remaining bits in this byte. 112 | int new_bits = 8 - (data_pointer_ & 0x7); 113 | data_pointer_ += word_size_ - num_bits_; 114 | if (data_pointer_ > length_) 115 | data_pointer_ = length_; 116 | while (num_bits_ < word_size_ && byte < ((length_ + 7) >> 3)) { 117 | unsigned int val = (data_[byte] & ((1<>= -shift; 121 | current_bits_ |= val; 122 | num_bits_ = word_size_; 123 | return; 124 | } 125 | current_bits_ |= (val << shift); 126 | num_bits_ += new_bits; 127 | ++byte; 128 | new_bits = 8; 129 | } 130 | } 131 | // Drop the most recent bits from the buffer. 132 | void DropBits(int len) { 133 | if (num_bits_ < len) { 134 | printf("Dropping %d left %d\n", len, num_bits_); 135 | throw("Dropping more bits than we have\n"); 136 | } 137 | current_bits_ <<= len; 138 | num_bits_ -= len; 139 | } 140 | 141 | // Take bits from the original stream and put them in the redacted stream. 142 | void CopyBits(int len) { 143 | InsertBits(current_bits_, len); 144 | } 145 | // Take the top len bits from the uint & add them to redacted data. 146 | void InsertBits(unsigned int bits, int len) { 147 | redacted_data_.resize((len + redaction_bit_pointer_ + 7) / 8, 0); 148 | /* if (debug > 0) { */ 149 | /* std::string scurr = Binary(*(unsigned int*)&redacted_data_[0], 32); */ 150 | /* printf("redacted currently: %s\n", scurr.c_str()); */ 151 | /* } */ 152 | // How many bits are available in the current target byte. 153 | int space = 8 -(redaction_bit_pointer_ % 8); 154 | int byte = redaction_bit_pointer_ / 8; 155 | redaction_bit_pointer_ += len; 156 | while (len > 0) { 157 | if (debug > 3) { 158 | std::string s = Binary(bits >> (32-len), len); 159 | printf("inserting %d bits: %s\n", len, s.c_str()); 160 | } 161 | unsigned char newbits = bits >> (32 - space); 162 | // Clear the trailing bits we're not using. 163 | if (space > len) 164 | newbits &= ~ ((1<< (space - len))-1); 165 | if (byte >= redacted_data_.size()) 166 | throw(0); 167 | redacted_data_[byte] |= newbits; 168 | // if (space <= len && redacted_data_[byte] == 0xff) { // A stuff byte 169 | // redaction_bit_pointer_ += 8; 170 | // redacted_data_.resize((redaction_bit_pointer_ + 7) / 8, 0); 171 | // byte++; 172 | // redacted_data_[byte] == 0x00; 173 | // } 174 | byte++; 175 | len -= space; 176 | bits <<= space; 177 | space = 8; 178 | } 179 | if (debug > 2) printf("Pointer is %d\n", redaction_bit_pointer_); 180 | if (debug > 3) { 181 | std::string scurr = Binary(*(unsigned int*)&redacted_data_[0], 32); 182 | printf("redacted after: %s\n", scurr.c_str()); 183 | } 184 | } 185 | int NextValue(int len); 186 | int DecodeOneBlock(int dht, int comp, int subblock_redaction); 187 | int LookupPixellationValue(int comp); 188 | 189 | int WriteValue(int which_dht, int value); 190 | void WriteZeroLength(int which_dht); 191 | 192 | // Update the redacting_ state flag. According to whether we're in 193 | // a redaction region or on its edge. 194 | void SetRedactingState(Redaction *redaction); 195 | 196 | // Decode all of the blocks from all of the components in a single MCU. 197 | void DecodeOneMCU(); 198 | // At the end of a redaction strip, store the redacted bits in Redaction 199 | // object. 200 | void StoreEndOfStrip(Redaction *redaction); 201 | 202 | void ResetDecoding() { 203 | redacting_ = kRedactingOff; 204 | redacted_data_.clear(); 205 | redaction_bit_pointer_ = 0; 206 | 207 | dct_gain_ = 0; // Number of bits to shift. 208 | current_bits_ = 0; // Buffer of 32 bits. 209 | num_bits_ = 0; // Number of bits remaining in current_bits_ 210 | data_pointer_ = 0; // Next bit to get into current_bits; 211 | mcus_ = 0; 212 | } 213 | 214 | // Return x & y coord of the top left corner of this MCU. 215 | int GetX(int mcu) const { 216 | const int mcu_width = w_blocks_ / mcu_h_; 217 | const int hq = kBlockSize * mcu_h_; 218 | const int mcu_x = mcus_ % mcu_width; 219 | return mcu_x * hq; 220 | } 221 | int GetY(int mcu) const { 222 | const int mcu_width = w_blocks_ / mcu_h_; 223 | const int vq = kBlockSize * mcu_v_; 224 | const int mcu_y = mcus_ / mcu_width; 225 | return mcu_y * vq; 226 | } 227 | 228 | // Does the current MCU overlap a redaction region. 229 | // return the index of the region, -1 if not 230 | int InRedactionRegion(const Redaction *const redaction) const { 231 | const int mcu_width = w_blocks_ / mcu_h_; 232 | const int hq = kBlockSize * mcu_h_; 233 | const int vq = kBlockSize * mcu_v_; 234 | const int mcu_x = mcus_ % mcu_width; 235 | const int mcu_y = mcus_ / mcu_width; 236 | return redaction->InRegion(mcu_x * hq, mcu_y * vq, hq, vq); 237 | } 238 | 239 | // First MCU of redaction region. 240 | static const int kRedactingStarting; 241 | // First MCU after redaction region. 242 | static const int kRedactingEnding; 243 | // Redacting this point of the image. 244 | static const int kRedactingActive; 245 | // Redacting this image but not at this point of the image. 246 | static const int kRedactingInactive; 247 | // No redaction requested for this image. 248 | static const int kRedactingOff; 249 | 250 | // Width/height of a block. (ie 8 pixels) 251 | static const int kBlockSize; 252 | 253 | // Decoding information: 254 | unsigned char *data_; 255 | int length_; // How many bits in data 256 | 257 | unsigned int current_bits_; // Buffer of up to 32 bits. 258 | int num_bits_; // How many bits valid in current_bits_ 259 | 260 | int data_pointer_; // Next bit to get into current_bits; 261 | // int symbols_; 262 | int num_mcus_; // How many we expect. 263 | int mcus_; // How many we've found 264 | //bits in JpegDecoder::current_bits_); 265 | static const int word_size_ = 8 * sizeof(unsigned int); 266 | int mcu_h_; 267 | int mcu_v_; 268 | int width_; 269 | int height_; 270 | int w_blocks_; // Width of the image in MCUs 271 | int h_blocks_; // Height of the image in MCUs 272 | int dct_gain_; 273 | int redacting_; // Are we redacting this image: see kRedacting* flags above. 274 | 275 | // What is the redaction method for the current region. 276 | Redaction::redaction_method redaction_method_; 277 | // Index of the region that we're currently in. 278 | int region_index_; 279 | // The current (input) DC (delta) value for this MCU, one per channel. 280 | std::vector dc_values_; 281 | // The current DC (delta) value for this MCU, one per channel, as output. 282 | // Differs from dc_values_ when we're redacting. 283 | std::vector redaction_dc_; 284 | 285 | // This stores the cumulative DC values for all components, interleaved. 286 | // before down-scaling. 287 | std::vector int_image_data_; 288 | // The DC values scaled to bytes. Currently intensity only. 289 | // Initially in MCU order, but then reordered to raster for writing as a pgm. 290 | std::vector image_data_; 291 | // These are pointers into the Jpeg's table of DHTs 292 | // The decoder does not own the memory. 293 | std::vector dhts_; 294 | const std::vector *components_; 295 | 296 | int redaction_bit_pointer_; 297 | std::vector redacted_data_; 298 | JpegStrip *current_strip_; 299 | // Pointer to the current redaction, while decoding. 300 | Redaction *redaction_; 301 | }; 302 | } // namespace jpeg_redaction 303 | 304 | #endif // INCLUDE_JPEGDECODER 305 | -------------------------------------------------------------------------------- /lib/jpeg_dht.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Andrew W. Senior andrew.senior[AT]gmail.com 2 | // Part of the Jpeg-Redaction-Library to read, parse, edit redact and 3 | // write JPEG/EXIF/JFIF images. 4 | // See https://github.com/asenior/Jpeg-Redaction-Library 5 | 6 | // This program is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | 16 | // You should have received a copy of the GNU General Public License 17 | // along with this program. If not, see . 18 | 19 | 20 | #ifndef INCLUDE_JPEGDHT 21 | #define INCLUDE_JPEGDHT 22 | 23 | #include 24 | 25 | namespace jpeg_redaction { 26 | extern int debug; 27 | inline std::string Binary(unsigned int b, int length) { 28 | if (debug == 0) 29 | throw ("Shouldn't be calling Binary with debug == 0"); 30 | std::string s; 31 | int l = 0; 32 | while(l < length) { 33 | std::string s2((b&1)?"1":"0"); 34 | s = s2 + s; 35 | if (l%8 == 7) { 36 | std::string s3(" "); 37 | s = s3 + s; 38 | } 39 | b >>= 1; 40 | ++l; 41 | } 42 | return s; 43 | } 44 | 45 | class JpegDHT { 46 | public: 47 | JpegDHT() { 48 | lut_len_ = 0; 49 | eob_symbol_ = 0; 50 | } 51 | virtual ~JpegDHT() {} 52 | void PrintTable() { 53 | // print out the table. 54 | int sym = 0; 55 | printf("DHT %d%d\n", class_, id_); 56 | for (int i = 1; i<=16; ++i) { 57 | if (num_symbols_[i-1] > 0) { 58 | printf("Length %2d, %3d symbols: ", i, num_symbols_[i-1]); 59 | for (int n = 0; n < num_symbols_[i-1]; ++n, ++sym) { 60 | if (0) { 61 | printf("length %d code %d sym %d\n", 62 | codes_[sym], lengths_[sym], symbols_[sym]); 63 | std::string binary = Binary(codes_[sym], lengths_[sym]); 64 | printf("%s,%x ", binary.c_str(), symbols_[sym]); 65 | } 66 | } 67 | printf("\n"); 68 | } 69 | } 70 | } 71 | 72 | // Make a lookup table of 'bits' bits. 73 | void BuildLUT(int bits) { 74 | if (debug > 0) 75 | printf("Building lookup table of %d bits.\n", bits); 76 | int not_found = 0; 77 | lut_len_ = bits; 78 | lut_.clear(); 79 | lut_.resize(1 << bits,-1); 80 | for (int code = 0; code < lut_.size(); ++code) { 81 | bool found = false; 82 | for (int i = 0; found == false && i < lengths_.size(); ++i) { 83 | if (lengths_[i] > bits) { 84 | break; 85 | } 86 | unsigned int part = code >> (bits -lengths_[i]); 87 | if (part == codes_[i]) { 88 | lut_[code] = i; 89 | found = true; 90 | } 91 | } 92 | if (!found) ++not_found; 93 | } 94 | printf("Didn't find %d\n", not_found); 95 | } 96 | // Build one DHT from a block of data. 97 | int Build(unsigned char *data, int bytes_left) { 98 | // Get the class and ID of this table. 99 | int bytes_used = 0; 100 | int lut_len = 0; 101 | const int max_lut_len = 10; 102 | class_ = data[0]>>4; 103 | id_ = data[0] & 0xf; 104 | bytes_used++; 105 | // 16 bytes encoding the number of symbols of each length 1..16 bits 106 | num_symbols_.assign(data + 1, data + 17); 107 | bytes_used += 16; 108 | 109 | // Now the symbols. 110 | // The bit std::strings are derived by expanding a binary tree and at each 111 | // depth, assigning the smallest available codes to the N symbols given 112 | // in the next block of data. N for each length is given in the table above. 113 | std::vector prefixes; 114 | prefixes.push_back(0); 115 | for (int length = 1; length <= 16; ++length) { 116 | // For each prefix pop and assign or 117 | std::vector new_prefixes; 118 | int symbols_used = 0; 119 | if (num_symbols_[length-1] > 0 && length <= max_lut_len) 120 | lut_len = length; 121 | for (int prefix = 0; prefix < prefixes.size(); ++prefix) { 122 | // Generate left & right symbols. 123 | for (int suffix = 0; suffix <2; ++suffix) { 124 | unsigned int code = (prefixes[prefix] << 1) + suffix; 125 | // Determine if we have symbols to assign, or make a new prefix. 126 | if (symbols_used < num_symbols_[length-1]) { 127 | lengths_.push_back(length); 128 | codes_.push_back(code); 129 | if (bytes_used > bytes_left) { 130 | fprintf(stderr, "No more bytes left in DHT\n"); 131 | return bytes_used; 132 | } 133 | if (data[bytes_used] == 0) 134 | eob_symbol_ = symbols_.size(); 135 | symbols_.push_back(data[bytes_used++]); 136 | if (symbols_.size() > 256) { 137 | fprintf(stderr, "Too many symbols defined\n"); 138 | throw("too many symbols defined"); 139 | } 140 | ++symbols_used; 141 | } else { 142 | new_prefixes.push_back(code); 143 | } 144 | } 145 | } 146 | prefixes.assign(new_prefixes.begin(), new_prefixes.end()); 147 | } 148 | // PrintTable(); 149 | BuildLUT(lut_len); 150 | return bytes_used; 151 | } 152 | 153 | int Decode(unsigned int current_bits, 154 | const int bits_available, 155 | unsigned int *symbol) { 156 | if (debug > 3) { 157 | std::string allbits = Binary(current_bits, 32); 158 | printf("Decoding from %s\n", allbits.c_str()); 159 | } 160 | if (bits_available <= 0 || bits_available > 32) { 161 | printf("Only %d bits left\n", bits_available); 162 | throw("Bad number of bits left"); 163 | } 164 | unsigned int lut_code = current_bits >> (32 - lut_len_); 165 | int code_index = lut_[lut_code]; 166 | if (code_index >= 0) { 167 | *symbol = symbols_[code_index]; 168 | if (debug >= 2) { 169 | std::string bin = Binary(lut_code, lut_len_); 170 | printf("LUTDecoding %d bits of %s as %u\n", 171 | lengths_[code_index], bin.c_str(), *symbol); 172 | } 173 | return lengths_[code_index]; 174 | } 175 | if (debug >= 3) { 176 | std::string s = Binary(current_bits, 32); 177 | printf("Table %d%d Trying to decode %s / %04x\n", 178 | class_, id_, s.c_str(), current_bits); 179 | } 180 | for (int i = 0; i < lengths_.size(); ++i) { 181 | if (lengths_[i] > bits_available) { 182 | printf("Can't decode with only %d bits left\n", bits_available); 183 | throw("not enough bits left"); 184 | } 185 | unsigned int part = current_bits >> (32 -lengths_[i]); 186 | if (part == codes_[i]) { 187 | *symbol = symbols_[i]; 188 | if (debug >= 2) { 189 | std::string bin = Binary(part, lengths_[i]); 190 | printf("Decoding %s as %u\n", bin.c_str(), *symbol); 191 | } 192 | return lengths_[i]; 193 | } 194 | } 195 | std::string bin = Binary(current_bits, 32); 196 | printf("Can't decode %s in table %d%d. %d bits left.\n", 197 | bin.c_str(), class_, id_, bits_available); 198 | throw("can't decode"); 199 | } 200 | // Find the table entry that codes a particular value. 201 | // Return -1 if not in the table. 202 | int Lookup(int value) { 203 | for (int i = 0; i < symbols_.size(); ++i) { 204 | if (symbols_[i] == value) 205 | return i; 206 | } 207 | if (debug > 0) 208 | fprintf(stderr, "Can't find value %d\n", value); 209 | return -1; 210 | } 211 | // Class is 0 for DC, 1 for AC. 212 | int class_; 213 | // Table number, as referenced by SOF. 214 | int id_; 215 | int eob_symbol_; 216 | std::vector num_symbols_; 217 | std::vector lengths_; 218 | std::vector codes_; 219 | std::vector symbols_; 220 | // Look up table - for a given bit pattern, which code is this. 221 | // -1 if more than N (typ 8) bits. 222 | int lut_len_; 223 | std::vector lut_; 224 | }; 225 | } // namespace redaction 226 | 227 | #endif // INCLUDE_JPEGDHT 228 | -------------------------------------------------------------------------------- /lib/jpeg_marker.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Andrew W. Senior andrew.senior[AT]gmail.com 2 | // Part of the Jpeg-Redaction-Library to read, parse, edit redact and 3 | // write JPEG/EXIF/JFIF images. 4 | // See https://github.com/asenior/Jpeg-Redaction-Library 5 | 6 | // This program is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | 16 | // You should have received a copy of the GNU General Public License 17 | // along with this program. If not, see . 18 | 19 | 20 | // jpeg_marker.cpp: implementation of the JpegMarker class to store 21 | // one marker from a JPEG file. 22 | 23 | #include 24 | #include "byte_swapping.h" 25 | #include "debug_flag.h" 26 | #include "jpeg.h" 27 | #include "jpeg_marker.h" 28 | 29 | namespace jpeg_redaction { 30 | // Save this (preloaded) marker to disk. 31 | int JpegMarker::Save(FILE *pFile) { 32 | const int location = ftell(pFile); 33 | if (pFile == NULL) 34 | throw("Null File in JpegMarker::Save."); 35 | unsigned short markerswapped = byteswap2(marker_); 36 | int rv = fwrite(&markerswapped, sizeof(unsigned short), 1, pFile); 37 | if (rv != 1) return 0; 38 | unsigned short slice_or_length = length_; 39 | if (marker_ == Jpeg::jpeg_sos) 40 | slice_or_length = slice_; 41 | ByteSwapInPlace(&slice_or_length, 1); 42 | rv = fwrite(&slice_or_length, sizeof(unsigned short), 1, pFile); 43 | if (rv != 1) return 0; 44 | if (marker_ == Jpeg::jpeg_sos) { 45 | WriteWithStuffBytes(pFile); 46 | // Add the EOI 47 | unsigned char eoi0 = 0xff; 48 | unsigned char eoi1 = 0xd9; 49 | fwrite(&eoi0, sizeof(eoi0), 1, pFile); 50 | fwrite(&eoi1, sizeof(eoi1), 1, pFile); 51 | } else { 52 | rv = fwrite(&data_[0], sizeof(char), data_.size(), pFile); 53 | if (rv != data_.size()) return 0; 54 | } 55 | if (debug > 0) 56 | printf("Saved marker %04x length %u at %d\n", marker_, length_, location); 57 | return 1; 58 | } 59 | // Write out the marker inserting stuff (0) bytes when there's an ff. 60 | void JpegMarker::WriteWithStuffBytes(FILE *pFile) { 61 | int written = 0; // Next byte to write out. 62 | int check = 10; // Next byte to check. 63 | int rv = 0; 64 | unsigned char zero = 0x00; 65 | int stuff_bytes = 0; 66 | // Repeatedly look for the next ff. 67 | // Then write all the data up to & including it and write out the 68 | // stuff byte (00). 69 | if (check > data_.size()) { 70 | fprintf(stderr, "data size is %zu\n", data_.size()); 71 | throw("data too short in stuffing."); 72 | } 73 | while (check < data_.size()) { 74 | if (data_[check] == 0xff) { 75 | rv = fwrite(&data_[written], sizeof(char), check + 1 - written, pFile); 76 | if (rv != check + 1 - written) 77 | throw("Failed to write enough bytes in WriteWithStuffBytes"); 78 | rv = fwrite(&zero, sizeof(char), 1, pFile); 79 | if (rv != 1) 80 | throw("Failed to write stuffbyte in WriteWithStuffBytes"); 81 | written = check + 1; 82 | ++stuff_bytes; 83 | } 84 | ++check; 85 | } 86 | if (check != data_.size()) throw("data_.size() mismatch in stuffing."); 87 | // Write out the last chunk of data. 88 | if (written < check) 89 | rv = fwrite(&data_[written], sizeof(char), check - written, pFile); 90 | if (debug > 0) 91 | printf("Inserted %d stuff_bytes in %zu now %zu\n", stuff_bytes, 92 | data_.size(), data_.size() + stuff_bytes); 93 | } 94 | void JpegMarker::RemoveStuffBytes() { 95 | if (data_.size() != length_ - 2) { 96 | fprintf(stderr, "Data %zu len %d\n", data_.size(), length_); 97 | throw("Data length mismatch in RemoveStuffBytes"); 98 | } 99 | const int start_of_huffman = 10; 100 | if (data_.size() < start_of_huffman) { 101 | fprintf(stderr, "Data %zu len %d\n", data_.size(), length_); 102 | throw("Data too short in RemoveStuffBytes"); 103 | } 104 | 105 | int dest = start_of_huffman; 106 | int src = start_of_huffman; 107 | int stuff_bytes = 0; 108 | for (int src = start_of_huffman; src < length_ - 2; ++dest, ++src) { 109 | data_[dest] = data_[src]; 110 | if (data_[src] == 0xff && data_[src+1] == 0x00) { 111 | ++src; 112 | ++stuff_bytes; 113 | } 114 | } 115 | if (debug > 0) 116 | printf("Removed %d stuff_bytes in %zu now %zu\n", 117 | stuff_bytes, data_.size(), data_.size() - stuff_bytes); 118 | length_ -= stuff_bytes; 119 | data_.resize(data_.size() - stuff_bytes); 120 | } 121 | } // namespace jpeg_redaction 122 | -------------------------------------------------------------------------------- /lib/jpeg_marker.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Andrew W. Senior andrew.senior[AT]gmail.com 2 | // Part of the Jpeg-Redaction-Library to read, parse, edit redact and 3 | // write JPEG/EXIF/JFIF images. 4 | // See https://github.com/asenior/Jpeg-Redaction-Library 5 | 6 | // This program is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | 16 | // You should have received a copy of the GNU General Public License 17 | // along with this program. If not, see . 18 | 19 | #ifndef INCLUDE_JPEG_MARKER 20 | #define INCLUDE_JPEG_MARKER 21 | 22 | #include 23 | 24 | namespace jpeg_redaction { 25 | class JpegMarker { 26 | public: 27 | // length_ is payload size- includes storage for length itself. 28 | // The actual data_ buffer is of size length_ - 2 29 | JpegMarker(unsigned short marker, unsigned int location, 30 | int length) { 31 | marker_ = marker; 32 | location_ = location; 33 | length_ = length; 34 | } 35 | // Create a marker from a block of data of given length. 36 | JpegMarker(unsigned short marker, 37 | const unsigned char *data, 38 | unsigned int length) { 39 | marker_ = marker; 40 | location_ = 0; 41 | length_ = length + 2; 42 | data_.resize(length); 43 | memcpy(&data_.front(), data, length); 44 | bit_length_ = length * 8; 45 | } 46 | void LoadFromLocation(FILE *pFile) { 47 | fseek(pFile, location_ + 4, SEEK_SET); 48 | LoadHere(pFile); 49 | } 50 | // Print a summary of the marker. 51 | void Print() const { 52 | printf("Marker %x length %d in bits %d datalen %zu location %d.\n", 53 | marker_, length_, bit_length_, data_.size(), location_); 54 | } 55 | void LoadHere(FILE *pFile) { 56 | data_.resize(length_-2); 57 | int rv = fread(&data_[0], sizeof(char), length_-2, pFile); 58 | bit_length_ = 8 * (length_ - 2); 59 | if (rv != length_-2) { 60 | printf("Failed to read marker %x at %d\n", marker_, length_); 61 | throw("Failed to read marker"); 62 | } 63 | } 64 | int GetBitLength() const { return bit_length_; } 65 | void SetBitLength(int bit_length) { 66 | if (bit_length > data_.size() * 8) 67 | throw("Setting bit length longer than buffer"); 68 | bit_length_ = bit_length; 69 | } 70 | void WriteWithStuffBytes(FILE *pFile); 71 | void RemoveStuffBytes(); 72 | int Save(FILE *pFile); 73 | unsigned short slice_; 74 | // Length is payload size- includes storage for length itself. 75 | int length_; 76 | unsigned int location_; 77 | unsigned short marker_; 78 | int bit_length_; 79 | std::vector data_; 80 | }; // JpegMarker 81 | } // namespace redaction 82 | 83 | #endif // INCLUDE_JPEG_MARKER 84 | -------------------------------------------------------------------------------- /lib/makernote.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Andrew W. Senior andrew.senior[AT]gmail.com 2 | // Part of the Jpeg-Redaction-Library to read, parse, edit redact and 3 | // write JPEG/EXIF/JFIF images. 4 | // See https://github.com/asenior/Jpeg-Redaction-Library 5 | 6 | // This program is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | 16 | // You should have received a copy of the GNU General Public License 17 | // along with this program. If not, see . 18 | 19 | // makernote.h 20 | // classes for reading, writing & accessing makernote sections of 21 | // EXIF files. Currently only some of these are partially implemented. 22 | 23 | // Makernotes for various manufacturers are described in 24 | // http://www.ozhiker.com/electronics/pjmt/jpeg_info/makernotes.html 25 | 26 | #ifndef JPEG_REDACTION_LIB_MAKERNOTE 27 | #define JPEG_REDACTION_LIB_MAKERNOTE 28 | 29 | #include 30 | #include 31 | #include 32 | #include "tiff_ifd.h" 33 | #include "debug_flag.h" 34 | 35 | namespace jpeg_redaction { 36 | 37 | class MakerNote { 38 | public: 39 | MakerNote() {} 40 | virtual void Print() const = 0; 41 | virtual int Read(FILE *pFile, int subfileoffset, int length) = 0; 42 | virtual int Write(FILE *pFile, int subfileoffset) const = 0; 43 | }; 44 | 45 | // A Generic Makernote where we just read a block of data. 46 | // without parsing, hoping that it is relocatable. 47 | class GenericMakerNote : public MakerNote { 48 | public: 49 | GenericMakerNote() {} 50 | ~GenericMakerNote() {} 51 | virtual void Print() const { 52 | printf("Generic makernote length %zu\n", data_.size()); 53 | } 54 | virtual int Read(FILE *pFile, int subfileoffset, int length) { 55 | data_.resize(length); 56 | int iRV = fread(&data_.front(), sizeof(unsigned char), length, pFile); 57 | if (iRV != length) { 58 | data_.clear(); 59 | return 0; 60 | } 61 | return 1; 62 | }; 63 | virtual int Write(FILE *pFile, int subfileoffset) const { 64 | int iRV = fwrite(&data_.front(), sizeof(unsigned char), 65 | data_.size(), pFile); 66 | if (iRV != data_.size()) { 67 | return 0; 68 | } 69 | return 1; 70 | }; 71 | std::vector data_; 72 | }; 73 | 74 | // A makernote stored in a standard TiffIfd, e.g. Canon. 75 | class IfdMakerNote : public MakerNote { 76 | public: 77 | IfdMakerNote() {} 78 | virtual void Print() const { 79 | printf("IDF makernote...\n"); 80 | ifd_->Print(); 81 | } 82 | virtual int Read(FILE *pFile, int subfileoffset, int length) {}; 83 | virtual int Write(FILE *pFile, int subfileoffset) const {}; 84 | protected: 85 | TiffIfd *ifd_; 86 | }; 87 | 88 | class Panasonic: public MakerNote { 89 | public: 90 | Panasonic() : ifd_(NULL) {} 91 | ~Panasonic() { 92 | delete ifd_; 93 | ifd_ = NULL; 94 | } 95 | virtual void Print() const { 96 | if (debug > 1) 97 | printf("Panasonic makernote... %p\n", this); 98 | if (ifd_ == NULL) 99 | throw("Panasonic ifd is NULL"); 100 | ifd_->Print(); 101 | } 102 | virtual int Read(FILE *pFile, int subfileoffset, int length) { 103 | int start = ftell(pFile); 104 | char header[12]; 105 | int iRV = fread(header, sizeof(char), 12, pFile); 106 | if (strcmp(header, "Panasonic") != 0) { 107 | fseek(pFile, start, SEEK_SET); 108 | return 0; 109 | } 110 | printf("Loading Panasonic\n"); 111 | ifd_ = new TiffIfd(pFile, start+12, true, subfileoffset); 112 | TiffTag *tag; 113 | tag = ifd_->FindTag(0x69); 114 | if (tag) printf("Panasonic69: %s\n", (const char *)tag->GetData()); 115 | tag = ifd_->FindTag(0x6b); 116 | if (tag) printf("Panasonic6b: %s\n", (const char *)tag->GetData()); 117 | tag = ifd_->FindTag(0x6d); 118 | if (tag) printf("Panasonic6d: %s\n", (const char *)tag->GetData()); 119 | tag = ifd_->FindTag(0x6f); 120 | if (tag) printf("Panasonic6f: %s\n", (const char *)tag->GetData()); 121 | 122 | fseek(pFile, start, SEEK_SET); 123 | return 1; 124 | } 125 | virtual int Write(FILE *pFile, int subfileoffset) const { 126 | if (ifd_ == NULL) throw("Trying to save NULL Panasonic makernote"); 127 | fwrite("Panasonic\0\0\0", sizeof(char), 12, pFile); 128 | unsigned int urv = ifd_->Write(pFile, 0, subfileoffset); 129 | return 1; 130 | } 131 | protected: 132 | TiffIfd *ifd_; 133 | }; 134 | 135 | class MakerNoteFactory { 136 | public: 137 | MakerNoteFactory() {} 138 | MakerNote *Read(FILE *pFile, int subfileoffset, int length) { 139 | int rv; 140 | size_t start_location = ftell(pFile); 141 | /* IfdMakerNote *ifdmn = new IfdMakerNote; */ 142 | /* rv = ifdmn->Read(pFile, subfileoffset, length); */ 143 | /* if (rv == 1) */ 144 | /* return ifdmn; */ 145 | /* delete ifdmn; */ 146 | /* rv = fseek(pFile, start_location, SEEK_SET); */ 147 | 148 | Panasonic *panasonic = new Panasonic; 149 | rv = panasonic->Read(pFile, subfileoffset, length); 150 | if (rv == 1) 151 | return panasonic; 152 | delete panasonic; 153 | rv = fseek(pFile, start_location, SEEK_SET); 154 | GenericMakerNote *generic = new GenericMakerNote; 155 | rv = generic->Read(pFile, subfileoffset, length); 156 | if (rv == 1) 157 | return generic; 158 | delete generic; 159 | return NULL; 160 | } 161 | }; 162 | 163 | } // namespace jpeg_redaction 164 | #endif // JPEG_REDACTION_LIB_MAKERNOTE 165 | -------------------------------------------------------------------------------- /lib/obscura_metadata.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Andrew W. Senior andrew.senior[AT]gmail.com 2 | // Part of the Jpeg-Redaction-Library to read, parse, edit redact and 3 | // write JPEG/EXIF/JFIF images. 4 | // See https://github.com/asenior/Jpeg-Redaction-Library 5 | 6 | // This program is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | 16 | // You should have received a copy of the GNU General Public License 17 | // along with this program. If not, see . 18 | 19 | 20 | // obscura_metadata.h 21 | 22 | // A class to store metadata specific to ObscuraCam in APPn (n=7) blocks 23 | // of JPEG files. 24 | // A general metadata block to be parsed by the application. 25 | // and a redaction-reversal block to be parsed by this library. 26 | 27 | #ifndef INCLUDE_OBSCURA_METADATA 28 | #define INCLUDE_OBSCURA_METADATA 29 | 30 | #include 31 | #include "jpeg.h" // For jpeg_app marker. 32 | #include "debug_flag.h" 33 | #include "jpeg_marker.h" 34 | #define JPEG_APP0 0xFFE0 35 | namespace jpeg_redaction { 36 | 37 | class ObscuraMetadata { 38 | public: 39 | ObscuraMetadata() {} 40 | void SetDescriptor(unsigned int length, 41 | const unsigned char *data) { 42 | descriptor_.resize(length); 43 | if (length > 0) 44 | memcpy(&descriptor_[0], data, length); 45 | printf("Set Obscura Metadata length %zu\n", descriptor_.size()); 46 | } 47 | const unsigned char *GetDescriptor(unsigned int *length) const { 48 | if (descriptor_.size() > 0) { 49 | *length = descriptor_.size(); 50 | return &descriptor_[0]; 51 | } 52 | *length = 0; 53 | return NULL; 54 | } 55 | 56 | // Return true if the marker was understood and stored in this structure. 57 | bool ImportMarker(JpegMarker * marker) { 58 | if (marker == NULL || marker->marker_ != kObscuraMarker) { 59 | printf("Got a marker not obscura but %x\n", marker->marker_); 60 | return false; 61 | } 62 | if (strcmp((char*)&marker->data_.front(), kDescriptorType) == 0) { 63 | if (debug > 0) { 64 | printf("Reading Obscura metadata \"%s\" %d %zu - %zu\n", 65 | kDescriptorType, marker->length_, 66 | marker->data_.size(), strlen(kDescriptorType)); 67 | marker->Print(); 68 | } 69 | int marker_no; 70 | int num_markers; 71 | int marker_len; 72 | int marker_start; 73 | int descriptor_len; 74 | unsigned char * marker_ptr = 75 | &(marker->data_.front()) + strlen(kDescriptorType) + 1; 76 | memcpy(&marker_no, marker_ptr, sizeof(int)); 77 | marker_ptr += sizeof(int); 78 | memcpy(&num_markers, marker_ptr, sizeof(int)); 79 | marker_ptr += sizeof(int); 80 | memcpy(&marker_len, marker_ptr, sizeof(int)); 81 | marker_ptr += sizeof(int); 82 | memcpy(&marker_start, marker_ptr, sizeof(int)); 83 | marker_ptr += sizeof(int); 84 | memcpy(&descriptor_len, marker_ptr, sizeof(int)); 85 | marker_ptr += sizeof(int); 86 | if (debug > 0) { 87 | printf("Read Obscura marker %d/%d len %d at %d of %d\n", 88 | marker_no, num_markers, 89 | marker_len, marker_start, descriptor_len); 90 | } 91 | if (marker_no <0 || num_markers <=0 || marker_no >= num_markers) 92 | throw("marker numbers don't match"); 93 | if (marker_len < 0 || descriptor_len <=0 || marker_no >= num_markers || 94 | marker_len >= 64 * 1024 || 95 | marker_start + marker_len > descriptor_len || 96 | marker_start < 0) 97 | throw("marker lengths don't match"); 98 | if (marker_no == 0) 99 | descriptor_.resize(descriptor_len); 100 | else 101 | if (descriptor_.size() != descriptor_len) 102 | throw("Marker store size != descriptor_len"); 103 | memcpy(&descriptor_[marker_start], marker_ptr, marker_len); 104 | if (marker_no == num_markers -1) { 105 | if (marker_start + marker_len != descriptor_len) { 106 | printf("Marker length @ %d /%d inexact %d %d %d\n", 107 | marker_no, num_markers, 108 | marker_start, marker_len, descriptor_len); 109 | throw("marker length inexact"); 110 | } 111 | // SetDescriptor(marker->length_ - 2 - (strlen(kDescriptorType) + 1), 112 | // &marker->data_[strlen(kDescriptorType) + 1]); 113 | } 114 | return true; 115 | } 116 | fprintf(stderr, "Unknown AppN Marker %d: %s\n", 117 | marker->GetBitLength(), (char*)&marker->data_.front()); 118 | return false; 119 | } 120 | // Store all the metadata in a file as APPN JPEG Markers. 121 | int Write(FILE *pFile) { 122 | // Convert the data to markers and write. 123 | std::vector descriptors; 124 | MakeDescriptorMarkers(&descriptors); 125 | if (descriptors.size() != 0) { 126 | if (debug > 0) 127 | printf("Writing Obscura descriptor at %zu\n", 128 | ftell(pFile)); 129 | for (int i=0; i < descriptors.size(); ++i) { 130 | descriptors[i]->Save(pFile); 131 | delete descriptors[i]; 132 | } 133 | } 134 | return 0; 135 | } 136 | 137 | static const int kObscuraMarker; 138 | static const char *kDescriptorType; 139 | protected: 140 | // Make a JPEG marker containing the descriptor information. 141 | void MakeDescriptorMarkers(std::vector *all_markers) { 142 | if (descriptor_.size() == 0) 143 | return; 144 | const int header_len = strlen(kDescriptorType) + 1 + 5 * sizeof(int); 145 | const int marker_size = 64 * 1024 - header_len - 4; 146 | const int des_length = descriptor_.size(); 147 | const int num_markers = (des_length + marker_size - 1) / marker_size; 148 | int marker_start = 0; 149 | for (int count = 0; count < num_markers; ++count) { 150 | int marker_len = marker_size; 151 | if (des_length < marker_start + marker_len) 152 | marker_len = des_length - marker_start; 153 | const int total_length = marker_len + header_len; 154 | // Create a temporary buffer to store the string header and the 155 | // marker data. 156 | std::vector long_data(total_length); 157 | unsigned char *data_ptr = &long_data[0]; 158 | memcpy(data_ptr, kDescriptorType, strlen(kDescriptorType)+1); 159 | data_ptr += strlen(kDescriptorType)+1; 160 | memcpy(data_ptr, &count, sizeof(int)); 161 | data_ptr += sizeof(int); 162 | memcpy(data_ptr, &num_markers, sizeof(int)); 163 | data_ptr += sizeof(int); 164 | memcpy(data_ptr, &marker_len, sizeof(int)); 165 | data_ptr += sizeof(int); 166 | memcpy(data_ptr, &marker_start, sizeof(int)); 167 | data_ptr += sizeof(int); 168 | memcpy(data_ptr, &des_length, sizeof(int)); 169 | data_ptr += sizeof(int); 170 | memcpy(data_ptr, &descriptor_[marker_start], marker_len); 171 | data_ptr += marker_len; 172 | if (debug > 0) { 173 | printf("Making descriptor %d/%d len %d at %d of %d\n", 174 | count, num_markers, marker_len, marker_start, des_length); 175 | } 176 | if (data_ptr - &long_data[0] != total_length) 177 | throw("Length mismatch making markers"); 178 | JpegMarker *marker = new JpegMarker(kObscuraMarker, &long_data.front(), 179 | total_length); 180 | marker->Print(); 181 | all_markers->push_back(marker); 182 | marker_start += marker_len; 183 | } 184 | if (marker_start != des_length) 185 | throw("ObscuraMarker lenght mismatch after marker creation."); 186 | } 187 | std::vector descriptor_; 188 | }; 189 | } // namespace redaction 190 | 191 | #endif 192 | -------------------------------------------------------------------------------- /lib/photoshop_3block.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Andrew W. Senior andrew.senior[AT]gmail.com 2 | // Part of the Jpeg-Redaction-Library to read, parse, edit redact and 3 | // write JPEG/EXIF/JFIF images. 4 | // See https://github.com/asenior/Jpeg-Redaction-Library 5 | 6 | // This program is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | 16 | // You should have received a copy of the GNU General Public License 17 | // along with this program. If not, see . 18 | 19 | 20 | // photoshop_3block.h: interface for the Photoshop3Block class. 21 | // 22 | ////////////////////////////////////////////////////////////////////// 23 | 24 | #ifndef INCLUDE_PHOTOSHOP_3BLOCK 25 | #define INCLUDE_PHOTOSHOP_3BLOCK 26 | 27 | #if _MSC_VER > 1000 28 | #pragma once 29 | #endif // _MSC_VER > 1000 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #include "iptc.h" 37 | #include "byte_swapping.h" 38 | 39 | using std::string; 40 | using std::vector; 41 | 42 | // const unsigned short Iptc::tag_bim_iptc_ = 0x0404; // The bim type for IPTC data 43 | // 44 | #define tag_8bim 0x3842494d // 0x4d494238 // "8BIM" 45 | #define tag_bim_iptc_ 0x0404 // The bim type for IPTC data 46 | 47 | namespace jpeg_redaction { 48 | class Photoshop3Block 49 | { 50 | public: 51 | class BIM 52 | { 53 | public: 54 | BIM(FILE *pFile) : iptc_(NULL) { 55 | const bool arch_big_endian = ArchBigEndian(); 56 | int iRV = fread(&magic_, sizeof(magic_), 1, pFile); // short 57 | 58 | if (!arch_big_endian) 59 | ByteSwapInPlace(&magic_, 1); 60 | 61 | if (magic_ != tag_8bim) 62 | printf("BIM was 0x%x not 0x%x\n", magic_, tag_8bim); 63 | 64 | // short 65 | iRV = fread(&bim_type_, sizeof(bim_type_), 1, pFile); 66 | 67 | iRV = fread(&pascalstringlength_, sizeof(pascalstringlength_), 1, pFile); 68 | 69 | // The pascal string storage (including the length byte) must be even. 70 | // How many more bytes do we have to read? 71 | unsigned char pascalstringlengthrounded = 72 | pascalstringlength_ + (1-(pascalstringlength_%2)); 73 | pascalstring_.resize(pascalstringlengthrounded); 74 | iRV = fread(&pascalstring_[0], sizeof(unsigned char), 75 | pascalstringlengthrounded, pFile); 76 | 77 | iRV = fread(&bim_length_, sizeof(bim_length_), 1, pFile); 78 | 79 | if (!arch_big_endian) 80 | ByteSwapInPlace(&bim_length_, 1); 81 | printf("bim_length read is %d\n", bim_length_); 82 | unsigned int bim_length_rounded = 83 | bim_length_ + (bim_length_%2); // Rounded to be even. 84 | if (bim_type_ == tag_bim_iptc_) { 85 | iptc_ = new Iptc(pFile, bim_length_); 86 | } else { 87 | printf("Got a BIM of type %x size %d\n", bim_type_, bim_length_rounded); 88 | data_.resize(bim_length_rounded); 89 | iRV = fread(&data_[0], sizeof(unsigned char), 90 | bim_length_rounded, pFile); 91 | } 92 | // throw("Got an unsupported BIM"); 93 | } 94 | 95 | virtual ~BIM() { } 96 | 97 | int Length() const { return bim_length_; } 98 | // BIM data (rounded) , Bimlength (4) , Bim type (2) , 99 | // pascal string (length + rounded) 100 | int TotalLength() const { 101 | return sizeof(bim_type_) + sizeof(magic_) + bim_length_ + 102 | (bim_length_%2) + sizeof(bim_length_) + 103 | pascalstringlength_ + sizeof(pascalstringlength_) + 104 | 1 - (pascalstringlength_%2); 105 | } 106 | 107 | int Write(FILE *pFile) { 108 | printf("Writing BIM3 at %zu\n", ftell(pFile)); 109 | const bool arch_big_endian = ArchBigEndian(); 110 | int length = 0; 111 | int iRV; 112 | unsigned int magic = tag_8bim; 113 | if (!arch_big_endian) 114 | ByteSwapInPlace(&magic, 1); 115 | iRV = fwrite(&magic, sizeof(magic), 1, pFile); 116 | length += sizeof(magic); 117 | 118 | unsigned short bim_type = bim_type_; 119 | if (!arch_big_endian) 120 | ByteSwapInPlace(&bim_type, 1); 121 | iRV = fwrite(&bim_type, sizeof(bim_type), 1, pFile); 122 | length += sizeof(bim_type); 123 | 124 | // Number of bytes to write out - to make the (length + string) 125 | // structure even length. 126 | 127 | iRV = fwrite(&pascalstringlength_, sizeof(pascalstringlength_), 1, pFile); 128 | length += sizeof(pascalstringlength_); 129 | 130 | unsigned char pascalstringlengthrounded = 131 | pascalstringlength_ + (1-(pascalstringlength_%2)); 132 | iRV = fwrite(&pascalstring_[0], sizeof(unsigned char), 133 | pascalstringlengthrounded, pFile); 134 | length += iRV; 135 | 136 | // Rounded to be even. 137 | const unsigned int bim_length_rounded = bim_length_ + (bim_length_%2); 138 | unsigned int bim_length_rounded_swap = bim_length_rounded; 139 | if (!arch_big_endian) 140 | ByteSwapInPlace(&bim_length_rounded_swap, 1); 141 | 142 | iRV = fwrite(&bim_length_rounded_swap, sizeof(unsigned int), 1, pFile); 143 | length += sizeof(unsigned int); 144 | 145 | printf("Writing BIM length %d, %p %zu\n", 146 | bim_length_rounded, &data_[0], data_.size()); 147 | if (bim_type_ == tag_bim_iptc_) { 148 | if (iptc_ == NULL) throw("IPTC is null in write"); 149 | iRV = iptc_->Write(pFile); 150 | length += iRV; 151 | printf("Wrote tag_bim_iptc_ %d\n", iRV); 152 | } else { 153 | iRV = fwrite(&data_[0], sizeof(unsigned char), 154 | bim_length_rounded, pFile); 155 | length += iRV; 156 | printf("Wrote BIM block %d", iRV); 157 | } 158 | return length; 159 | } 160 | 161 | unsigned short type() const { return bim_type_;} 162 | unsigned char pascalstringlength_; 163 | unsigned short bim_type_; 164 | vector pascalstring_; 165 | unsigned int bim_length_; 166 | vector data_; 167 | Iptc *iptc_; 168 | unsigned int magic_; 169 | }; 170 | 171 | 172 | // Read photoshop block from a file (ie a JPEG APP 13 block) 173 | Photoshop3Block(FILE *pFile, unsigned int recordlength) { 174 | char c; 175 | unsigned int remaininglength = recordlength; 176 | while((c=fgetc(pFile))!='\0'){ 177 | headerstring_ += c; 178 | } 179 | 180 | if (strcmp(headerstring_.c_str(), "Photoshop 3.0")!=0) 181 | throw("Bad header in photoshop 3 block"); 182 | remaininglength -= (headerstring_.length() + 1); 183 | while (remaininglength > 4) { 184 | unsigned int magic; 185 | BIM *bim = new BIM(pFile); 186 | int bimlength = bim->TotalLength(); 187 | bims_.push_back(bim); 188 | remaininglength -= (bimlength); 189 | } 190 | if (remaininglength != 0) throw("Photoshop block length mismatch"); 191 | } 192 | 193 | virtual ~Photoshop3Block() { 194 | for(int i = 0; i < bims_.size(); ++i) { 195 | delete bims_[i]; 196 | } 197 | bims_.clear(); 198 | } 199 | 200 | int Write(FILE *pFile) { 201 | int length = 0; 202 | int headerlen = headerstring_.length(); 203 | int iRV = fwrite(headerstring_.c_str(), sizeof(unsigned char), 204 | headerlen + 1, pFile); 205 | if (iRV != headerlen + 1) 206 | throw("Can't write"); 207 | length += iRV; 208 | 209 | for (int i = 0; i < bims_.size(); ++i) { 210 | iRV = bims_[i]->Write(pFile); 211 | length += iRV; 212 | } 213 | return length; 214 | } 215 | 216 | Iptc *GetIptc() { 217 | throw("This bit unimplemented\n"); 218 | // for (int i = 0; i< bims_.size(); ++i) 219 | // if (bims_[i]->GetIptc()) 220 | // return bims_[i]->GetIptc(); 221 | return NULL; 222 | } 223 | 224 | std::string headerstring_; 225 | 226 | vector bims_; 227 | }; 228 | } // namespace jpeg_redaction 229 | #endif // INCLUDE_PHOTOSHOP_3BLOCK 230 | -------------------------------------------------------------------------------- /lib/tiff_ifd.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Andrew W. Senior andrew.senior[AT]gmail.com 2 | // Part of the Jpeg-Redaction-Library to read, parse, edit redact and 3 | // write JPEG/EXIF/JFIF images. 4 | // See https://github.com/asenior/Jpeg-Redaction-Library 5 | 6 | // This program is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | 16 | // You should have received a copy of the GNU General Public License 17 | // along with this program. If not, see . 18 | 19 | 20 | // TiffIIfd.cpp: implementation of the TiffIfd class. 21 | // (c) 2011 Andrew Senior andrewsenior@google.com 22 | 23 | #include "debug_flag.h" 24 | #include "tiff_ifd.h" 25 | #include "tiff_tag.h" 26 | #include "jpeg.h" 27 | #include "byte_swapping.h" 28 | 29 | ////////////////////////////////////////////////////////////////////// 30 | // Construction/Destruction 31 | ////////////////////////////////////////////////////////////////////// 32 | 33 | 34 | namespace jpeg_redaction { 35 | TiffIfd::TiffIfd(FILE *pFile, unsigned int ifdoffset, 36 | bool loadall, unsigned int subfileoffset, 37 | bool byte_swapping) : 38 | subfileoffset_(subfileoffset), byte_swapping_(byte_swapping) { 39 | 40 | if (pFile == NULL) 41 | return; 42 | int iRV= fseek(pFile, ifdoffset, SEEK_SET); 43 | if (iRV != 0) { 44 | printf("Failed seek"); 45 | fclose(pFile); 46 | return; 47 | } 48 | short nr; 49 | iRV = fread(&nr, sizeof(short), 1, pFile); 50 | if (iRV != 1) return; 51 | if (byte_swapping) ByteSwapInPlace(&nr, 1); 52 | printf("IFDSize: %d \n", nr); 53 | for(int tagindex=0 ; tagindex < nr; ++tagindex) { 54 | int pos = ftell(pFile); 55 | // printf("File pointer %d\n", pos); 56 | TiffTag *tag = new TiffTag(pFile, byte_swapping_); 57 | tags_.push_back(tag); 58 | } 59 | printf("\n DIDIFD\n"); 60 | iRV = fread(&nextifdoffset_, sizeof(unsigned int), 1, pFile); 61 | if (iRV != 1) { 62 | throw("Couldn't read nextifdoffset"); 63 | } 64 | if (byte_swapping) ByteSwapInPlace(&nextifdoffset_, 1); 65 | 66 | // This disrupts the file pointer. 67 | LoadImageData(pFile, loadall); 68 | printf("Loaded Image data\n"); 69 | if (loadall) 70 | LoadAll(pFile); 71 | Print(); 72 | } 73 | 74 | // Print all tags to stdout. 75 | void TiffIfd::Print() const { 76 | if (debug >= 0) 77 | printf("%s:%d Listing %zu tags\n", __FILE__, __LINE__, GetNTags()); 78 | TiffTag *width_tag = FindTag(TiffTag::tag_ImageWidth); 79 | TiffTag *height_tag = FindTag(TiffTag::tag_ImageHeight); 80 | if (width_tag && height_tag) { 81 | printf("Image: %dx%d ", 82 | width_tag->GetUIntValue(0), height_tag->GetUIntValue(0)); 83 | TiffTag *compression_tag = FindTag(TiffTag::tag_Compression); 84 | if(compression_tag) 85 | printf("(%d) ", compression_tag->GetUIntValue(0)); 86 | } 87 | 88 | for(int tagindex=0 ; tagindex < tags_.size(); ++tagindex) { 89 | tags_[tagindex]->Print(); 90 | printf("\n"); 91 | } 92 | printf("\n"); 93 | } 94 | 95 | 96 | // Save the ifd out to an open file. Return this ifdlocation 97 | unsigned int TiffIfd::Write(FILE *pFile, 98 | unsigned int nextifdoffset, 99 | int subfileoffset) const 100 | { 101 | // Values that need to be filled in later. 102 | // Record where in the file and then what value will be put there. 103 | std::vector pending_pointers_where; 104 | std::vector pending_pointers_what; 105 | 106 | int iRV = fseek(pFile, 0, SEEK_END); 107 | if (iRV != 0 ) 108 | fprintf(stderr, "Failed to fseek in TiffIfd::Write\n"); 109 | 110 | // Write the ifd block out. 111 | const unsigned int ifdstart = ftell(pFile); 112 | // Write out the IFD with dummies for pointers, and pointer to the 113 | // image data offset 114 | short nr = tags_.size(); 115 | iRV = fwrite(&nr, sizeof(short), 1, pFile); 116 | int length_where = -1; 117 | for(int tagindex=0 ; tagindex < tags_.size(); ++tagindex) { 118 | unsigned int pointer = tags_[tagindex]->Write(pFile); // 0 if no pointer. 119 | if (pointer) { 120 | if (tags_[tagindex]->GetTag() == TiffTag::tag_StripByteCounts || 121 | tags_[tagindex]->GetTag() == TiffTag::tag_ThumbnailLength) { 122 | length_where = pointer; 123 | } else { 124 | pending_pointers_where.push_back(pointer); 125 | } 126 | } 127 | } 128 | iRV = fwrite(&nextifdoffset, sizeof(unsigned int), 1, pFile); 129 | 130 | unsigned int data_length = 0; 131 | // Write the image or thumbnail data. 132 | 133 | // Write all the subsidiary data that doesn't fit in tags 134 | // as well as thumbnail/image data. 135 | for(int tagindex=0 ; tagindex < tags_.size(); ++tagindex) { 136 | if (tags_[tagindex]->GetTag() == TiffTag::tag_StripOffsets || 137 | tags_[tagindex]->GetTag() == TiffTag::tag_ThumbnailOffset) { 138 | if (data_.empty()) throw("No data to write"); 139 | unsigned int data_start = ftell(pFile); 140 | // Write the image data out. 141 | printf("Saving thumbnail to file\n"); 142 | if (jpeg_ == NULL) throw("No JPEG to write out"); 143 | jpeg_->Save(pFile); 144 | // iRV = fwrite(&data_.front(), sizeof(unsigned char), data_.size(), 145 | // pFile); 146 | printf("thumbnail written\n"); 147 | data_length = ftell(pFile) - data_start; 148 | pending_pointers_what.push_back(data_start); 149 | continue; 150 | } 151 | 152 | // Returns 0 if the data is short enough to be in the tag and not 153 | // in a data block. 154 | unsigned int pointer = 155 | tags_[tagindex]->WriteDataBlock(pFile, subfileoffset); 156 | // "What": where we wrote the datablock. 157 | if (pointer) 158 | pending_pointers_what.push_back(pointer); 159 | } 160 | 161 | // If we're keeping track of pending pointers, fill them in now. 162 | if (pending_pointers_where.size() != pending_pointers_what.size()) { 163 | printf("Pending pointers where %zu what %zu\n", 164 | pending_pointers_where.size(), pending_pointers_what.size()); 165 | throw("pending pointers don't match"); 166 | } 167 | if (length_where >= 0) { 168 | if (data_length == 0) 169 | throw("Data length not set"); 170 | fseek(pFile, length_where, SEEK_SET); // Where 171 | iRV = fwrite(&data_length, sizeof(unsigned int), 1, pFile); 172 | } 173 | for(int i = 0; i < pending_pointers_what.size(); ++i) { 174 | printf("Write TiffIfd Locs Where: %d What: %d-%d\n", 175 | pending_pointers_where[i], pending_pointers_what[i], subfileoffset); 176 | fseek(pFile, pending_pointers_where[i], SEEK_SET); // Where 177 | const unsigned int ifdloc = pending_pointers_what[i]-subfileoffset; // What 178 | iRV = fwrite(&ifdloc, sizeof(unsigned int), 1, pFile); 179 | } 180 | // Go back to the end of the file. 181 | iRV = fseek(pFile, 0, SEEK_END); 182 | // Return the location of this ifdstart (from which we can calculate 183 | // where we have to write the nextifdoffset if we hadn't precalculated it) 184 | return ifdstart; 185 | } 186 | 187 | int TiffIfd::LoadAll(FILE *pFile) { 188 | for(int tagindex=0; tagindexLoad(pFile, subfileoffset_, byte_swapping_); 190 | } 191 | return 1; 192 | } 193 | 194 | void TiffIfd::Reset() { 195 | for(int tagindex=0 ; tagindex < tags_.size(); ++tagindex) { 196 | delete tags_[tagindex]; 197 | } 198 | tags_.clear(); 199 | } 200 | 201 | 202 | int TiffIfd::LoadImageData(FILE *pFile, bool loadall) 203 | { 204 | TiffTag *tagdataoffs = FindTag(TiffTag::tag_StripOffsets); 205 | TiffTag *tagdatabytes = FindTag(TiffTag::tag_StripByteCounts); 206 | if (tagdataoffs == NULL) { 207 | if (tagdatabytes !=NULL) throw("offs but no length in data"); 208 | tagdataoffs = FindTag(TiffTag::tag_ThumbnailOffset); 209 | tagdatabytes = FindTag(TiffTag::tag_ThumbnailLength); 210 | if (tagdataoffs) { 211 | if (tagdatabytes == NULL) throw("thumbnail offs but no length in data"); 212 | printf("Loading thumbnail\n"); 213 | } 214 | } 215 | data_.clear(); 216 | if (tagdataoffs && tagdatabytes) { 217 | const unsigned int dataoffs = tagdataoffs->GetUIntValue(0); 218 | const unsigned int databytes = tagdatabytes->GetUIntValue(0); 219 | printf("Loading internal image data at %d,%d\n", dataoffs, databytes); 220 | int iRV = fseek(pFile, dataoffs + subfileoffset_, SEEK_SET); 221 | if (iRV != 0) 222 | throw("Can't seek to data"); 223 | jpeg_ = new Jpeg(); 224 | jpeg_->LoadFromFile(pFile, true, dataoffs + subfileoffset_); 225 | if (loadall) { 226 | printf("TiffIfd:LoadImageData all\n"); 227 | iRV = fseek(pFile, dataoffs + subfileoffset_, SEEK_SET); 228 | data_.resize(databytes); 229 | iRV = fread(&data_.front(), sizeof(unsigned char), databytes, pFile); 230 | if (iRV != databytes) 231 | throw("Couldn't read ImageData"); 232 | } 233 | 234 | return databytes; 235 | } 236 | return 0; 237 | } 238 | 239 | // Add the tag- return 0 if it replaced another tag. 240 | int TiffIfd::AddTag(TiffTag *tag, bool allowmultiple) { 241 | int tagno = tag->GetTag(); 242 | if (!allowmultiple) 243 | for(int tagindex=0 ; tagindex < tags_.size(); ++tagindex) { 244 | if (tags_[tagindex]->GetTag() == tagno) { 245 | delete tags_[tagindex]; 246 | tags_[tagindex] = tag; 247 | return 0; 248 | } 249 | } 250 | 251 | for(int tagindex=0 ; tagindex < tags_.size(); ++tagindex) { 252 | if (tags_[tagindex]->GetTag() > tagno) { 253 | tags_.insert(tags_.begin() + tagindex, tag); 254 | return 1; 255 | } 256 | } 257 | tags_.push_back(tag); 258 | return 1; 259 | } 260 | }; // namespace redaction 261 | -------------------------------------------------------------------------------- /lib/tiff_ifd.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Andrew W. Senior andrew.senior[AT]gmail.com 2 | // Part of the Jpeg-Redaction-Library to read, parse, edit redact and 3 | // write JPEG/EXIF/JFIF images. 4 | // See https://github.com/asenior/Jpeg-Redaction-Library 5 | 6 | // This program is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | 16 | // You should have received a copy of the GNU General Public License 17 | // along with this program. If not, see . 18 | 19 | 20 | // tiff_ifd.h: interface for the TiffIfd class. 21 | 22 | #ifndef JPEG_REDACTION_LIB_TIFF_IFD 23 | #define JPEG_REDACTION_LIB_TIFF_IFD 24 | 25 | #include 26 | 27 | #include "tiff_tag.h" 28 | 29 | namespace jpeg_redaction { 30 | class TiffTag; 31 | class Jpeg; 32 | class ExifIfd; 33 | 34 | class TiffIfd 35 | { 36 | public: 37 | TiffIfd(FILE *pFile, unsigned int ifdoffset, bool loadall = false, 38 | unsigned int subfileoffset=0, bool byte_swapping = false); 39 | TiffIfd() { } 40 | virtual ~TiffIfd() { 41 | Reset(); 42 | } 43 | 44 | int AddTag(TiffTag *tag, bool allowmultiple); 45 | int LoadImageData(FILE *pFile, bool loadall); 46 | unsigned int Write(FILE *pFile, 47 | unsigned int nextifdoffset, 48 | int subfileoffset) const; 49 | ExifIfd *GetExif() { return (ExifIfd*)FindTag(TiffTag::tag_ExifIFDPointer); } 50 | 51 | // Find a tag with a particular number. Return -1 if not in this IFD. 52 | TiffTag *FindTag(const int tagno) const { 53 | const int tagindex = FindTagIndex(tagno); 54 | return (tagindex < 0) ? NULL : tags_[tagindex]; 55 | } 56 | 57 | int FindTagIndex(const int tagno) const { 58 | for(int tagindex=0 ; tagindex < tags_.size(); ++tagindex) { 59 | if (tags_[tagindex]->GetTag() == tagno) 60 | return tagindex; 61 | } 62 | return -1; 63 | } 64 | 65 | // Remove one tag of type tagno. If all is specified delete all of them 66 | // e.g. for keywords. 67 | bool RemoveTag(const int tagno, bool all = false) { 68 | bool found = false; 69 | for(int tagindex=0 ; tagindex < tags_.size(); ++tagindex) { 70 | if (tags_[tagindex]->GetTag() == tagno) { 71 | delete tags_[tagindex]; 72 | tags_.erase(tags_.begin() + tagindex); 73 | found = true; 74 | if (!all) 75 | return found; 76 | } 77 | } 78 | return found; 79 | } 80 | 81 | // For anything referenced by a pointer, then seek to it and load it 82 | // into data_blocks_ 83 | int LoadAll(FILE *pFile); 84 | int GetNextIfdOffset() const {return nextifdoffset_;} 85 | 86 | TiffTag *GetTag(int tagindex) const { 87 | if (tagindex < 0 || tagindex >= tags_.size()) 88 | return NULL; 89 | return(tags_[tagindex]); 90 | } 91 | size_t GetNTags() const { return tags_.size(); } 92 | // Print all tags to stdout. 93 | void Print() const; 94 | Jpeg *GetJpeg() { return jpeg_;} 95 | protected: 96 | void Reset(); 97 | 98 | bool byte_swapping_; 99 | unsigned int nextifdoffset_; 100 | std::vector tags_; 101 | std::vector data_; 102 | unsigned int subfileoffset_; 103 | Jpeg *jpeg_; 104 | }; 105 | 106 | // Subclass if it is an exif block 107 | class ExifIfd : public TiffIfd { 108 | public: 109 | ExifIfd(FILE *pFile, unsigned int ifdoffset, bool loadall = false, 110 | unsigned int subfileoffset=0): 111 | TiffIfd(pFile, ifdoffset, loadall, subfileoffset) { 112 | } 113 | }; 114 | } // namespace jpeg_redaction 115 | #endif // JPEG_REDACTION_LIB_TIFF_IFD 116 | -------------------------------------------------------------------------------- /lib/tiff_tag.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Andrew W. Senior andrew.senior[AT]gmail.com 2 | // Part of the Jpeg-Redaction-Library to read, parse, edit redact and 3 | // write JPEG/EXIF/JFIF images. 4 | // See https://github.com/asenior/Jpeg-Redaction-Library 5 | 6 | // This program is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | 16 | // You should have received a copy of the GNU General Public License 17 | // along with this program. If not, see . 18 | 19 | 20 | // tiff_tag.cpp: implementation of the TiffTag class. 21 | // 22 | ////////////////////////////////////////////////////////////////////// 23 | 24 | #include "debug_flag.h" 25 | #include "tiff_tag.h" 26 | #include "tiff_ifd.h" 27 | #include "byte_swapping.h" 28 | #include "makernote.h" 29 | 30 | ////////////////////////////////////////////////////////////////////// 31 | // Construction/Destruction 32 | ////////////////////////////////////////////////////////////////////// 33 | 34 | namespace jpeg_redaction { 35 | TiffTag::TiffTag(FILE *pFile, bool byte_swapping) : 36 | data_(NULL), subifd_(NULL), makernote_(NULL) { 37 | if (pFile == NULL) 38 | throw("NULL file"); 39 | int iRV = fread(&tagid_, sizeof(short), 1, pFile); 40 | if (iRV != 1) throw("Can't read file"); 41 | if (byte_swapping) 42 | ByteSwapInPlace(&tagid_, 1); 43 | iRV = fread(&type_, sizeof(short), 1, pFile); 44 | if (iRV != 1) throw("Can't read file"); 45 | if (byte_swapping) 46 | ByteSwapInPlace(&type_, 1); 47 | iRV = fread(&count_, sizeof(unsigned int), 1, pFile); 48 | if (iRV != 1) throw("Can't read file"); 49 | if (byte_swapping) 50 | ByteSwapInPlace(&count_, 1); 51 | 52 | unsigned int value = 0; 53 | iRV = fread(&value, sizeof(unsigned int), 1, pFile); 54 | if (iRV != 1) throw("Can't read file"); 55 | int totallength = GetDataLength(); 56 | if ((totallength > 4 || TagIsSubIFD()) && byte_swapping) { 57 | // printf("Swapping pointer\n");// If it's a pointer. 58 | ByteSwapInPlace(&value, 1); 59 | } 60 | if (totallength < 0 || totallength > 1e8) { 61 | fprintf(stderr, "tag %d totallength %d", tagid_, totallength); 62 | throw("totallength is broken"); 63 | } 64 | // Some types are pointers that will be stored in an ifd. 65 | if (totallength <= 4 && ! (TagIsSubIFD()) ) { 66 | if (byte_swapping) { 67 | // printf("Swapping non-pointer\n");// If it's a pointer. 68 | ByteSwapInPlace((unsigned char *)&value, 69 | 4/LengthOfType(type_), LengthOfType(type_)); 70 | } 71 | loaded_ = true; 72 | data_ = new unsigned char [totallength]; 73 | memcpy(data_, &value, totallength); 74 | valpointer_ = 0; 75 | } else { 76 | loaded_ = false; 77 | valpointer_ = value; 78 | // Too long to fit in the tag. 79 | } 80 | if (debug > 1) 81 | printf("Read tag %d/0x%x, type %s count %d value %d/0x%x " 82 | "bytes %d\n", 83 | tagid_, tagid_, TypeName((tag_types)type_), count_, 84 | value, value, totallength); 85 | } 86 | TiffTag::TiffTag(int tagid, enum tag_types type, int count, 87 | unsigned char *data) : makernote_(NULL) { 88 | tagid_ = tagid; 89 | type_ = type; 90 | count_ = count; 91 | int totallength = count * LengthOfType(type); 92 | data_ = new unsigned char[totallength]; 93 | memcpy(data_, data, totallength); 94 | subifd_ = NULL; 95 | loaded_ = true; 96 | } 97 | 98 | const char *TiffTag::GPSTagName(const int tag) { 99 | switch (tag) { 100 | case gps_version: return "GPSversion"; 101 | case gps_lat_ref: return "GPSlat_ref"; 102 | case gps_lat: return "GPSlat"; 103 | case gps_long_ref: return "GPSlong_ref"; 104 | case gps_long: return "GPSlong"; 105 | case gps_alt_ref: return "GPSalt_ref"; 106 | case gps_alt: return "GPSalt"; 107 | case gps_time_stamp: return "GPStime_stamp"; 108 | case gps_satellites: return "GPSsatellites"; 109 | case gps_status: return "GPSstatus"; 110 | case gps_measure_mode: return "GPSmeasure_mode"; 111 | case gps_dop: return "GPSdop"; 112 | case gps_speed_ref: return "GPSspeed_ref"; 113 | case gps_speed: return "GPSspeed"; 114 | case gps_track_ref: return "GPStrack_ref"; 115 | case gps_track: return "GPStrack"; 116 | case gps_img_direction_ref: return "GPSimg_direction_ref"; 117 | case gps_img_direction: return "GPSimg_direction"; 118 | case gps_map_datum: return "GPSmap_datum"; 119 | case gps_dest_lat_ref: return "GPSdest_lat_ref"; 120 | case gps_dest_lat: return "GPSdest_lat"; 121 | case gps_dest_long_ref: return "GPSdest_long_ref"; 122 | case gps_dest_long: return "GPSdest_long"; 123 | case gps_dest_bearing_ref: return "GPSdest_bearing_ref"; 124 | case gps_dest_bearing: return "GPSdest_bearing"; 125 | case gps_dest_dist_ref: return "GPSdest_dist_ref"; 126 | case gps_dest_dist: return "GPSdest_dist"; 127 | } 128 | return NULL; 129 | } 130 | const char *TiffTag::TagName(const int tag) { 131 | switch (tag) { 132 | case tag_ImageWidth: return "ImageWidth"; 133 | case tag_ImageHeight: return "ImageHeight"; 134 | case tag_BitsPerSample: return "BitsPerSample"; 135 | case tag_Compression: return "Compression"; 136 | case tag_PhotometricInterpretation: return "PhotometricInterpretation"; 137 | case tag_Title: return "Title"; 138 | case tag_Make: return "Make"; 139 | case tag_Model: return "Model"; 140 | case tag_StripOffsets: return "StripOffsets"; 141 | case tag_Orientation: return "Orientation"; 142 | case tag_SamplesPerPixel: return "SamplesPerPixel"; 143 | case tag_RowsPerStrip: return "RowsPerStrip"; 144 | case tag_StripByteCounts: return "StripByteCounts"; 145 | case tag_XResolution: return "XResolution"; 146 | case tag_YResolution: return "YResolution"; 147 | case tag_PlanarConfiguration: return "PlanarConfiguration"; 148 | case tag_ResolutionUnit: return "ResolutionUnit";// 2= inches 3=centimeters 149 | case tag_TransferFunction: return "TransferFunction"; 150 | case tag_Software: return "Software"; 151 | case tag_DateChange: return "DateChange"; 152 | case tag_Artist: return "Artist"; 153 | case tag_WhitePoint: return "WhitePoint"; 154 | case tag_PrimaryChromaticities: return "PrimaryChromaticities"; 155 | case tag_ThumbnailOffset: return "ThumbnailOffset"; 156 | case tag_ThumbnailLength: return "ThumbnailLength"; 157 | case tag_YCbCrCoefficients: return "YCbCrCoefficients"; 158 | case tag_YCbCrSubSampling: return "YCbCrSubSampling"; 159 | case tag_YCbCrPositioning: return "YCbCrPositioning"; 160 | case tag_ReferenceBlackWhite: return "ReferenceBlackWhite"; 161 | case tag_Copyright: return "Copyright"; 162 | case tag_ExposureTime: return "ExposureTime"; 163 | case tag_FNumber: return "FNumber"; 164 | case tag_ExposureProgram: return "ExposureProgram"; 165 | case tag_ExifIFDPointer: return "ExifIFDPointer"; 166 | case tag_SpectralSensitivity: return "SpectralSensitivity"; 167 | case tag_GpsInfoIFDPointer: return "GpsInfoIFDPointer"; 168 | case tag_ISOSpeedRatings: return "ISOSpeedRatings"; 169 | case tag_OECF: return "OECF"; 170 | 171 | case tag_ExifVersion: return "ExifVersion"; 172 | case tag_DateTimeOriginal: return "DateTimeOriginal"; 173 | case tag_DateTimeDigitized: return "DateTimeDigitized"; 174 | 175 | case tag_ShutterSpeedValue: return "ShutterSpeedValue"; 176 | case tag_ApertureValue: return "ApertureValue"; 177 | case tag_BrightnessValue: return "BrightnessValue"; 178 | case tag_ExposureBiasValue: return "ExposureBiasValue"; 179 | case tag_MaxApertureValue: return "MaxApertureValue"; 180 | case tag_SubjectDistance: return "SubjectDistance"; 181 | case tag_MeteringMode: return "MeteringMode"; 182 | case tag_LightSource: return "LightSource"; 183 | case tag_Flash: return "Flash"; 184 | case tag_FocalLength: return "FocalLength"; 185 | case tag_MakerNote: return "MakerNote"; 186 | 187 | case tag_Interoperability: return "Interoperability"; 188 | } 189 | const char *gps_tag = GPSTagName(tag); 190 | if (gps_tag != NULL) 191 | return gps_tag; 192 | return "Unknown"; 193 | } 194 | // Return the name of the given type. 195 | const char *TiffTag::TypeName(tag_types t) { 196 | switch (t) { 197 | case tiff_uint8 : 198 | return "uint8"; 199 | case tiff_string: 200 | return "string"; 201 | case tiff_uint16: 202 | return "uint16"; 203 | case tiff_uint32 : 204 | return "uint32"; 205 | case tiff_urational: 206 | return "urational"; 207 | case tiff_int8: 208 | return "int8"; 209 | case tiff_bytes: 210 | return "bytes"; 211 | case tiff_int16: 212 | return "int16"; 213 | case tiff_int32: 214 | return "int32"; 215 | case tiff_rational: 216 | return "rational"; 217 | case tiff_float: 218 | return "float"; 219 | case tiff_double: 220 | return "double"; 221 | default: 222 | case tiff_unknown: 223 | return "unknown"; 224 | } 225 | } 226 | 227 | TiffTag::~TiffTag() { 228 | delete subifd_; 229 | delete [] data_; 230 | } 231 | 232 | // Write out return 0 if the tag contains the data. 233 | // If a pointer must be written, write a dummy, but return the offset 234 | // so it can be corrected later. 235 | int TiffTag::Write(FILE *pFile) const { 236 | int pointer_location = 0; 237 | int iRV = fwrite(&tagid_, sizeof(short), 1, pFile); 238 | if (iRV != 1) throw("Can't write file"); 239 | iRV = fwrite(&type_, sizeof(short), 1, pFile); 240 | if (iRV != 1) throw("Can't write file"); 241 | iRV = fwrite(&count_, sizeof(unsigned int), 1, pFile); 242 | if (iRV != 1) throw("Can't write file"); 243 | 244 | const int totallength = GetDataLength(); 245 | pointer_location = ftell(pFile); 246 | // Pad with zeros so output is consistent even for outputs < 4 bytes. 247 | unsigned char raw[4] = {0,0,0,0}; 248 | if (totallength <= 4 && data_) 249 | memcpy(raw, data_, totallength); 250 | iRV = fwrite(&raw, sizeof(unsigned char), 4, pFile); 251 | if (iRV != 4) throw("Can't write file"); 252 | // Return where the pointer has to be written. 253 | if (totallength > 4 || TagIsSubIFD() || 254 | tagid_ == tag_StripOffsets || tagid_ == tag_ThumbnailOffset || 255 | tagid_ == tag_StripByteCounts || tagid_ == tag_ThumbnailLength) { 256 | return pointer_location; 257 | } else { 258 | return 0; 259 | } 260 | } 261 | 262 | // Write out datablocks, returning the pointer. Return if no datablock. 263 | int TiffTag::WriteDataBlock(FILE *pFile, int subfileoffset) { 264 | int totallength = count_ * LengthOfType(type_); 265 | // int subfileoffset = 0; // TODO(aws) should this be non-zero? 266 | if (TagIsSubIFD()) { 267 | unsigned int zero = 0; 268 | if (subifd_ == NULL) 269 | throw("subifd is nul in TiffTag::WriteDataBlock"); 270 | valpointerout_ = subifd_->Write(pFile, zero, subfileoffset); 271 | return valpointerout_; // Will get subtracted later. 272 | } 273 | if (tagid_ == tag_MakerNote) { 274 | if (makernote_ != NULL) { 275 | valpointerout_ = ftell(pFile); 276 | makernote_->Write(pFile, 0); 277 | return valpointerout_; 278 | } 279 | } 280 | if (totallength > 4) { 281 | if (!loaded_) 282 | throw("Trying to write when data was never read"); 283 | valpointerout_ = ftell(pFile); 284 | fwrite(data_, sizeof(unsigned char), totallength, pFile); 285 | return valpointerout_; 286 | } 287 | 288 | return 0; 289 | } 290 | 291 | // Load a type that didn't fit in the 4 bytes 292 | int TiffTag::Load(FILE *pFile, unsigned int subfileoffset, 293 | bool byte_swapping) { 294 | if (loaded_) 295 | return 0; 296 | if (pFile == NULL) 297 | throw("NULL file"); 298 | int position = valpointer_ + subfileoffset; 299 | if (TagIsSubIFD()) { 300 | // IFDs use absolute position, normal tags are relative to subfileoffset. 301 | //if (tagid_ == tag_MakerNote) 302 | // position = valpointer_; 303 | if (debug > 0) 304 | printf("Loading SUB IFD 0x%x at %d (%d + %d) ", tagid_, position, 305 | valpointer_, subfileoffset); 306 | 307 | subifd_ = new TiffIfd(pFile, position, true, 308 | subfileoffset, byte_swapping); 309 | loaded_ = true; 310 | return 1; 311 | } 312 | int iRV = fseek(pFile, position, SEEK_SET); 313 | if (tagid_ == tag_MakerNote) { 314 | printf("Reading Makernote\n"); 315 | MakerNoteFactory factory; 316 | makernote_ = factory.Read(pFile, subfileoffset, count_); 317 | if (makernote_ != NULL) { 318 | loaded_ = true; 319 | return count_; 320 | } else { 321 | fprintf(stderr, "Failed to read Makernote."); 322 | // Otherwise fall through to general handling. 323 | } 324 | // position = valpointer_; 325 | } 326 | const int type_len = LengthOfType(type_); 327 | const int totallength = count_ * type_len; 328 | data_ = new unsigned char [totallength]; 329 | iRV = fread(data_, sizeof(char), totallength, pFile); 330 | if (iRV != totallength) 331 | throw("Couldn't read data block."); 332 | if (byte_swapping) { 333 | if (type_ == tiff_rational || type_ == tiff_urational) 334 | ByteSwapInPlace(data_, count_ * 2, type_len/2); 335 | else 336 | ByteSwapInPlace(data_, count_, type_len); 337 | } 338 | loaded_ = true; 339 | return totallength; 340 | } 341 | 342 | void TiffTag::SetValOut(unsigned int val) { 343 | valpointerout_ = val; 344 | } 345 | 346 | bool TiffTag::TagIsSubIFD() const { 347 | return(tagid_ == tag_ExifIFDPointer || tagid_ == tag_GpsInfoIFDPointer || 348 | /* tagid_ == tag_MakerNote || */ tagid_ == tag_Interoperability); 349 | } 350 | 351 | void TiffTag::Print() const { 352 | printf("0x%04x %-17s %2dx%d %-10s (", tagid_, TagName(tagid_), count_, 353 | LengthOfType(type_), TypeName((tag_types)type_)); 354 | TraceValue(4); 355 | printf(") "); 356 | } 357 | 358 | void TiffTag::TraceValue(int maxvals) const { 359 | if (TagIsSubIFD()) { 360 | printf("IFD %d", valpointer_); 361 | return; 362 | } 363 | if (tagid_ == tag_MakerNote) { 364 | if (makernote_) { 365 | printf("Now printing makernote\n"); 366 | makernote_->Print(); 367 | } else 368 | printf("unresolved makernote..."); 369 | return; 370 | } 371 | for(int i=0; i. 18 | 19 | 20 | // tiff_tag.h: interface for the TiffTag class. 21 | // 22 | ////////////////////////////////////////////////////////////////////// 23 | 24 | #ifndef INCLUDE_TIFF_TAG 25 | #define INCLUDE_TIFF_TAG 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | // 0x8769 EXIF 32 | // 0x8825 GPS 33 | // 0x927c Makernote 34 | 35 | namespace jpeg_redaction { 36 | class TiffIfd; 37 | class MakerNote; 38 | class TiffTag 39 | { 40 | public: 41 | enum tag_types {tiff_unknown=0, 42 | tiff_uint8 = 1, 43 | tiff_string=2, 44 | tiff_uint16=3, 45 | tiff_uint32 =4, 46 | tiff_urational=5, 47 | tiff_int8=6, 48 | tiff_bytes=7, 49 | tiff_int16=8, 50 | tiff_int32=9, 51 | tiff_rational=10, 52 | tiff_float=11, 53 | tiff_double=12}; 54 | TiffTag(FILE *pFile, bool byte_swapping); 55 | // Create a tag from raw data. 56 | TiffTag(int tag, enum tag_types type, int count, unsigned char *data); 57 | ~TiffTag(); 58 | int GetDataLength() const; 59 | void TraceValue(int maxvals) const; 60 | void Print() const; 61 | TiffIfd *GetExif() { 62 | throw("Not implemented yet"); 63 | } 64 | void SetValOut(unsigned int val); 65 | // Return the name of the given type. 66 | static const char *TypeName(tag_types t); 67 | // Return the name of the given type. 68 | static const char *GPSTagName(int tag); 69 | static const char *TagName(int tag); 70 | enum gps_tag_names { 71 | gps_version = 0x0, 72 | gps_lat_ref = 0x1, 73 | gps_lat = 0x2, 74 | gps_long_ref = 0x3, 75 | gps_long = 0x4, 76 | gps_alt_ref = 0x5, 77 | gps_alt = 0x6, 78 | gps_time_stamp = 0x7, 79 | gps_satellites = 0x8, 80 | gps_status = 0x9, 81 | gps_measure_mode = 0xa, 82 | gps_dop = 0xb, 83 | gps_speed_ref = 0xc, 84 | gps_speed = 0xd, 85 | gps_track_ref = 0xe, 86 | gps_track = 0xf, 87 | gps_img_direction_ref = 0x10, 88 | gps_img_direction = 0x11, 89 | gps_map_datum = 0x12, 90 | gps_dest_lat_ref = 0x13, 91 | gps_dest_lat = 0x14, 92 | gps_dest_long_ref = 0x15, 93 | gps_dest_long = 0x16, 94 | gps_dest_bearing_ref = 0x17, 95 | gps_dest_bearing = 0x18, 96 | gps_dest_dist_ref = 0x19, 97 | gps_dest_dist = 0x1a, 98 | }; 99 | enum tag_names { 100 | tag_ImageWidth = 0x100, 101 | tag_ImageHeight=0x101, 102 | tag_BitsPerSample = 0x102, 103 | tag_Compression = 0x103, 104 | tag_PhotometricInterpretation = 0x106, 105 | tag_Title = 0x10e, 106 | tag_Make = 0x10f, 107 | tag_Model = 0x110, 108 | tag_StripOffsets = 0x111, 109 | tag_Orientation = 0x112, 110 | tag_SamplesPerPixel = 0x115, 111 | tag_RowsPerStrip = 0x116, 112 | tag_StripByteCounts = 0x117, 113 | tag_XResolution = 0x11A, 114 | tag_YResolution = 0x11B, 115 | tag_PlanarConfiguration = 0x11C, 116 | tag_ResolutionUnit = 0x128, // 2= inches 3=centimeters 117 | tag_TransferFunction = 0x12D, 118 | tag_Software = 0x131, 119 | tag_DateChange = 0x132, 120 | tag_Artist = 0x13B, 121 | tag_WhitePoint = 0x13E, 122 | tag_PrimaryChromaticities = 0x13F, 123 | tag_ThumbnailOffset = 0x201, 124 | tag_ThumbnailLength = 0x202, 125 | tag_YCbCrCoefficients = 0x211, 126 | tag_YCbCrSubSampling = 0x212, 127 | tag_YCbCrPositioning = 0x213, 128 | tag_ReferenceBlackWhite = 0x214, 129 | tag_Copyright = 0x8298, 130 | tag_ExposureTime = 0x829A, 131 | tag_FNumber = 0x829D, 132 | tag_ExposureProgram = 0x8822, 133 | tag_ExifIFDPointer = 0x8769, 134 | tag_SpectralSensitivity = 0x8824, 135 | tag_GpsInfoIFDPointer = 0x8825, 136 | tag_ISOSpeedRatings = 0x8827, 137 | tag_OECF = 0x8828, 138 | 139 | tag_ExifVersion = 0x9000, 140 | tag_DateTimeOriginal = 0x9003, 141 | tag_DateTimeDigitized = 0x9004, 142 | 143 | tag_ShutterSpeedValue = 0x9201, 144 | tag_ApertureValue = 0x9202, 145 | tag_BrightnessValue = 0x9203, 146 | tag_ExposureBiasValue = 0x9204, 147 | tag_MaxApertureValue = 0x9205, 148 | tag_SubjectDistance = 0x9206, 149 | tag_MeteringMode = 0x9207, 150 | tag_LightSource = 0x9208, 151 | tag_Flash = 0x9209, 152 | tag_FocalLength = 0x920A, 153 | tag_MakerNote = 0x927c , 154 | 155 | tag_Interoperability = 0xa005 156 | }; 157 | 158 | // Must call WriteDataBlock first to set valpointerout_ 159 | // and write the data block if appropriate. 160 | int Write(FILE *pFile) const; 161 | // Call this first to write out the data block and set the valpointerout_ 162 | int WriteDataBlock(FILE *pFile, int subfileoffset); 163 | 164 | // Load a type that didn't fit in the 4 bytes 165 | int Load(FILE *pFile, unsigned int subfileoffset, bool byte_swapping); 166 | tag_types GetType() const { return (tag_types)type_; } 167 | int GetTag() const { return tagid_; } 168 | int GetCount() const { return count_; } 169 | double GetFloatValue(unsigned int pos = 0) const { 170 | if (!loaded_) throw("Not loaded"); 171 | if (!data_) throw("No data"); 172 | if (pos >= count_) 173 | throw("Index too high"); 174 | switch (type_) { 175 | case tiff_float: 176 | return(((float*)data_)[pos]); 177 | case tiff_double: 178 | return(((double*)data_)[pos]); 179 | case tiff_urational: 180 | if ((( int*)data_)[pos*2] == 0) 181 | return 0; 182 | return(((unsigned int*)data_)[pos*2] / 183 | (double)((unsigned int*)data_)[pos*2+1]); 184 | case tiff_rational: 185 | if ((( int*)data_)[pos*2] == 0) 186 | return 0; 187 | return((( int*)data_)[pos*2] / 188 | (double)(( int*)data_)[pos*2+1]); 189 | } 190 | throw("wrong type not float/double/rational"); 191 | } 192 | 193 | unsigned int GetUIntValue(unsigned int pos) const { 194 | if (!loaded_) throw("Not loaded"); 195 | if (!data_) throw("No data"); 196 | if (pos >= count_) 197 | throw("Index too high"); 198 | switch (type_) { 199 | case tiff_uint8: 200 | case tiff_bytes: 201 | return(((unsigned char*)data_)[pos]); 202 | case tiff_uint16: 203 | return(((unsigned short*)data_)[pos]); 204 | case tiff_uint32: 205 | return(((unsigned int*)data_)[pos]); 206 | } 207 | throw("wrong type not UInt"); 208 | } 209 | int GetIntValue(unsigned int pos) const { 210 | if (!loaded_) throw("Not loaded"); 211 | if (!data_) throw("No data"); 212 | if (pos >= count_) 213 | throw("Index too high"); 214 | switch (type_) { 215 | case tiff_int8: 216 | return((( char*)data_)[pos]); 217 | case tiff_int16: 218 | return((( short*)data_)[pos]); 219 | case tiff_int32: 220 | return((( int*)data_)[pos]); 221 | } 222 | throw("wrong type not Int"); 223 | } 224 | const char * GetStringValue() const { 225 | if (!loaded_) throw("Not loaded"); 226 | if (!data_) throw("No data"); 227 | if (type_!= tiff_string) 228 | throw("Not a string"); 229 | return((const char *)data_); 230 | } 231 | const unsigned char * GetData() const { 232 | return(data_); 233 | } 234 | void SetStringValue(const char *s) { 235 | if (type_ != tiff_string) 236 | throw("SetStringValue on non-string."); 237 | int totallength = strlen(s) + 1; 238 | count_ = totallength; 239 | delete [] data_; 240 | data_ = new unsigned char [totallength]; 241 | strncpy((char*)data_, s, totallength); 242 | } 243 | static int LengthOfType(short type) { 244 | if (type == tiff_int8 || type == tiff_uint8 || 245 | type == tiff_string|| type == tiff_bytes) 246 | return 1; 247 | if (type == tiff_uint16 || type == tiff_int16) 248 | return 2; 249 | if (type == tiff_double || type == tiff_rational || type == tiff_urational) 250 | return 8; 251 | if (type > tiff_double || type <=0) 252 | return -1; 253 | return 4; 254 | } 255 | 256 | protected: 257 | unsigned short tagid_; 258 | unsigned short type_; 259 | unsigned int count_; 260 | 261 | bool loaded_; 262 | unsigned char *data_; 263 | unsigned int valpointer_; 264 | mutable unsigned int valpointerout_; 265 | TiffIfd *subifd_; // Makernote or Exif 266 | MakerNote *makernote_; 267 | private: 268 | bool TagIsSubIFD() const; 269 | }; 270 | } // namespace jpeg_redaction 271 | #endif // INCLUDE_TIFF_TAG 272 | -------------------------------------------------------------------------------- /notes.txt: -------------------------------------------------------------------------------- 1 | # Issues now handled in github 2 | 3 | ################################################## 4 | General notes. 5 | 6 | 03/16/11 7 | Seem to have Exif & IFD reading sorted out (read makernote as a block 8 | now). Not currently writing them out properly. (Exiftool says bad 9 | IFD1) 10 | 11 | Some unit tests. 12 | 13 | todo: 14 | 15 | Need to fill in API: 16 | Load only. 17 | Write current 18 | Redact 19 | Erase EXIF 20 | Modify EXIF. (add tag, remove tag, remove all GPS, set tag, add EXIF) 21 | Modify thumbnail too. 22 | return data, reinject data. 23 | 24 | Bigger test suite (images from other cameras etc.) 25 | 26 | 27 | Describe the basic use case in the README. 28 | 29 | 06/15/11 30 | Hooked up to ssc app. 31 | 32 | 33 | 11/26/11 34 | Need to 35 | 1) Create IDF 36 | 2) Add tag 37 | 3) Create tag. 38 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | testout/* 2 | ifdtest 3 | jpegtest 4 | -------------------------------------------------------------------------------- /test/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2011 Andrew W. Senior andrew.senior[AT]gmail.com 2 | # Part of the Jpeg-Redaction-Library to read, parse, edit redact and 3 | # write JPEG/EXIF/JFIF images. 4 | # See https://github.com/asenior/Jpeg-Redaction-Library 5 | 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | 19 | 20 | # Makefile for building & running JPEG/EXIF reading, writing, redaction 21 | # unit tests. 22 | 23 | include ../Makefile.common 24 | 25 | BINARY = jpegtest 26 | READWRITE = testreadwrite 27 | METADATATESTBINARY = metadatatest 28 | IFDTESTBINARY = ifdtest 29 | EXIF_REMOVE = exiftest 30 | TEST_REDACTION = testredaction 31 | BITSHIFTS = bit_shifts_test 32 | EXIFTOOL = exiftool 33 | 34 | LIB = ../lib/libredact.a 35 | 36 | default: all 37 | 38 | all: $(BINARY) $(BITSHIFTS) $(EXIF_REMOVE) $(IFDTESTBINARY) 39 | 40 | test: $(BINARY) test_ifd test_simple test_windows test_exif_removal \ 41 | test_bit_shifts test_metadata test_devices test_redaction 42 | 43 | $(LIB): 44 | cd ../lib; $(MAKE) lib 45 | 46 | $(BITSHIFTS): bit_shifts_test.cpp ../lib/bit_shifts.h testout_dir ../lib/debug_flag.cpp 47 | $(CC) $(CXXFLAGS) -I../lib bit_shifts_test.cpp ../lib/debug_flag.cpp $(LIBPATH) -o $@ 48 | 49 | $(BINARY): $(LIB) jpegtest.cpp test_utils.cpp testout_dir 50 | $(CC) $(CXXFLAGS) -I../lib jpegtest.cpp test_utils.cpp $(LIBPATH) $(LIB) -o $@ 51 | 52 | $(TEST_REDACTION): $(LIB) test_redaction.cpp test_utils.cpp testout_dir 53 | $(CC) $(CXXFLAGS) -I../lib test_redaction.cpp test_utils.cpp $(LIBPATH) $(LIB) -o $@ 54 | 55 | $(READWRITE): $(LIB) testreadwrite.cpp testout_dir test_utils.cpp testout_dir 56 | $(CC) $(CXXFLAGS) -I../lib testreadwrite.cpp test_utils.cpp $(LIBPATH) $(LIB) -o $@ 57 | 58 | $(EXIF_REMOVE): $(LIB) exiftest.cpp testout_dir 59 | $(CC) $(CXXFLAGS) -I../lib exiftest.cpp $(LIBPATH) $(LIB) -o $@ 60 | 61 | $(IFDTESTBINARY): $(LIB) ifdtest.cpp testout_dir 62 | $(CC) $(CXXFLAGS) -I../lib ifdtest.cpp $(LIBPATH) $(LIB) -o $@ 63 | 64 | $(METADATATESTBINARY): $(LIB) metadatatest.cpp testout_dir 65 | $(CC) $(CXXFLAGS) -I../lib metadatatest.cpp $(LIBPATH) $(LIB) -o $@ 66 | 67 | .PHONY: clean cleanall clean_rawgrey clean_test \ 68 | test testout_dir test_exif test_simple test_windows test_ifd \ 69 | test_exif_removal \ 70 | $(LIB) 71 | 72 | clean_test: clean_rawgrey 73 | rm -f testout/windows_output.log 74 | rm -f testout/simple_output.log 75 | rm -f testout/testexif_output.log testout/testexif.exiftxt 76 | 77 | clean_rawgrey: 78 | @rm -f testout/rawgrey.pgm 79 | 80 | clean: 81 | rm -f $(BINARY) $(IFDTESTBINARY) 82 | rm -rf testout 83 | 84 | veryclean: cleanall 85 | 86 | cleanall: clean 87 | cd ../lib; $(MAKE) clean 88 | 89 | depend: 90 | $(MAKE) dependlocal 91 | 92 | testout_dir: 93 | @mkdir -p testout 94 | 95 | test_ifd: $(IFDTESTBINARY) testout_dir 96 | ./$(IFDTESTBINARY) > testout/ifdtest.log 97 | diff testdata/ifdtest_golden.log testout/ifdtest.log > /dev/null 98 | @echo "== " $@ " passed" 99 | 100 | test_simple: $(BINARY) testout_dir clean_rawgrey 101 | ./$(BINARY) testdata/simple.jpg > testout/simple_output.log 102 | diff testdata/simple_output_golden.log testout/simple_output.log > /dev/null 103 | @cmp testdata/simple_rawgrey_golden.pgm testout/rawgrey.pgm 104 | @echo "== " $@ " passed" 105 | 106 | 107 | test_windows: $(BINARY) testout_dir clean_rawgrey 108 | ./$(BINARY) testdata/windows.jpg > testout/windows_output.log 109 | diff testdata/windows_output_golden.log testout/windows_output.log > /dev/null 110 | @cmp testdata/windows_out_redacted.jpg testout/testoutput.jpg 111 | @cmp testdata/windows_rawgrey_golden.pgm testout/rawgrey.pgm 112 | @echo "== " $@ " passed" 113 | 114 | 115 | test_exif: $(BINARY) testout_dir clean_rawgrey 116 | ./$(BINARY) testdata/testexif.jpg > testout/testexif_output.log 117 | $(EXIFTOOL) testoutput.jpg | grep -v "^File" > testout/testexif.exiftxt 118 | diff testdata/testexif_golden.exiftxt testout/testexif.exiftxt > /dev/null 119 | @echo "== " $@ " passed" 120 | 121 | test_redaction: $(TEST_REDACTION) testout_dir 122 | ./$(TEST_REDACTION) > testout/test_redaction_output.log 123 | @echo "== " $@ " passed" 124 | 125 | test_exif_removal: $(EXIF_REMOVE) testout_dir clean_rawgrey 126 | ./$(EXIF_REMOVE) testdata/windows.jpg > testout/testexif_removal_output.log 127 | @cmp testout/test_noexif.jpg testdata/test_noexif_golden.jpg 128 | @cmp testout/test_noexifgps.jpg testdata/test_noexifgps_golden.jpg 129 | @cmp testout/test_nosensitive.jpg testdata/test_nosensitive_golden.jpg 130 | diff testout/testexif_removal_output.log testdata/testexif_removal_output_golden.log > /dev/null 131 | @echo "== " $@ " passed" 132 | 133 | test_metadata: $(METADATATESTBINARY) testout_dir 134 | ./$(METADATATESTBINARY) testdata/windows.jpg > testout/test_metadata_output.log 135 | @echo "== " $@ " passed" 136 | 137 | test_bit_shifts: $(BITSHIFTS) 138 | ./$(BITSHIFTS) > testout/test_bit_shifts_output.log 139 | @echo "== " $@ " passed" 140 | 141 | test_devices: $(READWRITE) testout_dir 142 | ./testreadwrite.sh 143 | 144 | 145 | # DO NOT DELETE 146 | -------------------------------------------------------------------------------- /test/bit_shifts_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Andrew W. Senior andrew.senior[AT]gmail.com 2 | // Part of the Jpeg-Redaction-Library to read, parse, edit redact and 3 | // write JPEG/EXIF/JFIF images. 4 | // See https://github.com/asenior/Jpeg-Redaction-Library 5 | 6 | // This program is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | 16 | // You should have received a copy of the GNU General Public License 17 | // along with this program. If not, see . 18 | 19 | 20 | // Test for bit shift class. 21 | #include 22 | #include 23 | #include 24 | #include "../lib/bit_shifts.h" 25 | 26 | using std::vector; 27 | typedef vector bitstream; 28 | 29 | namespace jpeg_redaction { 30 | namespace tests { 31 | // Return the nth bit from a stream, assuming the 0th bit is the 32 | // highest order bit of the first byte. 33 | int BitFromStream(const bitstream &stream, int bit) { 34 | if (bit > stream.size() * 8) 35 | throw("Index outside stream"); 36 | return (stream[bit / 8] >> (7 - (bit % 8))) & 1; 37 | } 38 | 39 | // Check that a range of bits of a given length in old_bits 40 | // (length old_length) matches a range of bits in new_bits. 41 | bool VerifyRange(const bitstream &old_bits, int old_start, int old_length, 42 | const bitstream &new_bits, int new_start, int new_length, 43 | int length) { 44 | if (old_start + length > old_length) throw("Old length error"); 45 | if (new_start + length > new_length) throw("New length error"); 46 | for (int i = 0; i < length; ++i) { 47 | if (BitFromStream(old_bits, old_start + i) != 48 | BitFromStream(new_bits, new_start + i)) { 49 | fprintf(stderr, "Can't verify range %d (%d): %d (%d) at " 50 | "bit %d, (byte %d) of %d.\n", 51 | old_start, old_length, 52 | new_start, new_length, 53 | i, i/8, length); 54 | throw("Failed to verify range"); 55 | } 56 | } 57 | return true; 58 | } 59 | void FillRand(bitstream *stream) { 60 | for (int i= 0; i < stream->size(); ++i) { 61 | (*stream)[i] = rand() % 256; 62 | } 63 | } 64 | 65 | void FillStream(bitstream *stream, unsigned char b) { 66 | for (int i= 0; i < stream->size(); ++i) { 67 | (*stream)[i] = b; 68 | } 69 | } 70 | 71 | // Verify that the Verification works. 72 | bool TestNoShift(int length) { 73 | printf("Testing NoShift\n"); 74 | int bytes = (length + 7)/8; 75 | bitstream original(bytes); 76 | FillRand(&original); 77 | bitstream test(bytes); 78 | test.assign(original.begin(), original.end()); 79 | 80 | // Check the start still matches. 81 | VerifyRange(test, 0, length, 82 | original, 0, length, 83 | 0); 84 | VerifyRange(test, 0, length, 85 | original, 0, length, 86 | length/2); 87 | VerifyRange(test, length/2, length, 88 | original, length/2, length, 89 | length/2); 90 | return true; 91 | } 92 | 93 | bool TestShift(int length, int start, int shift) { 94 | printf("Testing Shift %d %d %d\n", length, start, shift); 95 | int bytes = (length + 7)/8; 96 | bitstream original(bytes); 97 | FillRand(&original); 98 | bitstream test(bytes); 99 | test.assign(original.begin(), original.end()); 100 | int new_length = length; 101 | bool rv = BitShifts::ShiftTail(&test, &new_length, start, shift); 102 | if (new_length != length + shift) { 103 | fprintf(stderr, "new_length %d length %d shift %d\n", 104 | new_length, length, shift); 105 | throw("TestShift length mismatch"); 106 | } 107 | // Check the start still matches. 108 | VerifyRange(original, 0, length, 109 | test, 0, new_length, 110 | start); 111 | // Check the end matches. 112 | VerifyRange(original, start, length, 113 | test, start + shift, new_length, 114 | length-start); 115 | return true; 116 | } 117 | 118 | 119 | bool TestInsert(int length, int start, int shift) { 120 | printf("Testing insert %d %d %d\n", length, start, shift); 121 | int bytes = (length + 7)/8; 122 | int insert_bytes = (shift + 7)/8; 123 | bitstream original(bytes); 124 | bitstream insertion(insert_bytes); 125 | FillRand(&original); 126 | FillRand(&insertion); 127 | bitstream test(bytes); 128 | test.assign(original.begin(), original.end()); 129 | 130 | int new_length = length; 131 | bool rv = BitShifts::Insert(&test, &new_length, start, insertion, shift); 132 | 133 | if (new_length != length + shift) { 134 | fprintf(stderr, "new_length %d length %d shift %d\n", 135 | new_length, length, shift); 136 | throw("TestSplice length mismatch"); 137 | } 138 | // Check the start still matches. 139 | printf("Check start\n"); 140 | VerifyRange(original, 0, length, 141 | test, 0, new_length, 142 | start); 143 | // Check the end matches. 144 | printf("Check end\n"); 145 | VerifyRange(original, start, length, 146 | test, start + shift, new_length, 147 | length - start); 148 | // Check the insertion matches. 149 | printf("Check insertion\n"); 150 | VerifyRange(insertion, 0, shift, 151 | test, start, new_length, 152 | shift); 153 | return true; 154 | } 155 | 156 | bool TestOverwrite(int length, int start, int insert_bits) { 157 | printf("Testing overwrite startlen %d start %d insert_bits %d\n", 158 | length, start, insert_bits); 159 | if (start + insert_bits > length) 160 | throw("Bad specification of TestSplice"); 161 | int bytes = (length + 7)/8; 162 | int insert_bytes = (insert_bits + 7)/8; 163 | bitstream original(bytes); 164 | bitstream insertion(insert_bytes); 165 | FillRand(&original); 166 | FillRand(&insertion); 167 | bitstream test(bytes); 168 | test.assign(original.begin(), original.end()); 169 | 170 | const int new_length = length; 171 | bool rv = BitShifts::Overwrite(&test, length, start, insertion, insert_bits); 172 | 173 | // Check the start still matches. 174 | printf("Check start\n"); 175 | VerifyRange(original, 0, length, 176 | test, 0, new_length, 177 | start); 178 | // Check the end matches. 179 | printf("Check end\n"); 180 | VerifyRange(original, start + insert_bits, length, 181 | test, start + insert_bits, length, 182 | length - start - insert_bits); 183 | // Check the insertion matches. 184 | printf("Check overwrite\n"); 185 | VerifyRange(insertion, 0, insert_bits, 186 | test, start, length, 187 | insert_bits); 188 | return true; 189 | } 190 | 191 | bool TestBitFromStream(int len, unsigned char b) { 192 | printf("TestBitFromStream len %d byte %x\n", len, b); 193 | bitstream ones(len); 194 | FillStream(&ones, b); 195 | for (int i = 0; i < len; ++i) { 196 | if (BitFromStream(ones, i) != (1 & (b >> (7-(i % 8)))) ) { 197 | fprintf(stderr, "Bad bit %d from stream. Tested with len %d byte 0x%x.", 198 | i, len, b); 199 | throw("Fail in TestBitFromStream"); 200 | } 201 | } 202 | return true; 203 | } 204 | 205 | bool AllShiftTests() { 206 | try { 207 | TestBitFromStream(21, 0xff); 208 | TestBitFromStream(53, 0x00); 209 | TestBitFromStream(19, 0xca); 210 | 211 | TestNoShift(100); 212 | // Whole byte shifts. 213 | TestShift(57, 0, 8); 214 | TestShift(195, 32, 16); 215 | 216 | // Shift by fractions of a byte. 217 | TestShift(100, 50, 25); 218 | // TestShift(299, 80, 5); 219 | // TestShift(299, 3, 5); 220 | TestShift(32, 16, 15); 221 | TestShift(127, 32, 199); 222 | TestShift(27, 8, 199); 223 | 224 | TestOverwrite(227, 16, 16); 225 | TestOverwrite(227, 13, 16); 226 | TestOverwrite(227, 16, 19); 227 | TestOverwrite(227, 13, 25); 228 | TestOverwrite(7, 3, 3); 229 | 230 | TestInsert(27, 8, 199); 231 | } catch (const char *message) { 232 | fprintf(stderr, "Caught message: %s in bit_shifts_test\n", message); 233 | exit(1); 234 | } 235 | } 236 | } // namespace tests 237 | } // namespace jpeg_redaction 238 | 239 | int main(int argc, char **argv) { 240 | jpeg_redaction::tests::AllShiftTests(); 241 | return 0; 242 | } 243 | -------------------------------------------------------------------------------- /test/exiftest.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Andrew W. Senior andrew.senior[AT]gmail.com 2 | // Part of the Jpeg-Redaction-Library to read, parse, edit redact and 3 | // write JPEG/EXIF/JFIF images. 4 | // See https://github.com/asenior/Jpeg-Redaction-Library 5 | 6 | // This program is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | 16 | // You should have received a copy of the GNU General Public License 17 | // along with this program. If not, see . 18 | 19 | 20 | #include 21 | #include 22 | 23 | #include 24 | #include "jpeg.h" 25 | #include "redaction.h" 26 | 27 | namespace jpeg_redaction { 28 | namespace tests { 29 | int test_exif_removal(const char * const filename) { 30 | try { 31 | Jpeg j2; 32 | bool success = j2.LoadFromFile(filename, true); 33 | if (!success) exit(1); 34 | j2.RemoveTag(TiffTag::tag_ExifIFDPointer); 35 | j2.Save("testout/test_noexif.jpg"); 36 | } catch (const char *error) { 37 | fprintf(stderr, "Error: <%s> at outer level\n", error); 38 | exit(1); 39 | } 40 | return 0; 41 | } 42 | 43 | int test_sensitive_removal(const char * const filename) { 44 | try { 45 | Jpeg j2; 46 | bool success = j2.LoadFromFile(filename, true); 47 | if (!success) exit(1); 48 | j2.RemoveAllSensitive(); 49 | j2.Save("testout/test_nosensitive.jpg"); 50 | } catch (const char *error) { 51 | fprintf(stderr, "Error: <%s> at outer level\n", error); 52 | exit(1); 53 | } 54 | return 0; 55 | } 56 | 57 | int test_gps_removal(const char * const filename) { 58 | try { 59 | Jpeg j2; 60 | bool success = j2.LoadFromFile(filename, true); 61 | if (!success) exit(1); 62 | int rem = j2.RemoveTag(TiffTag::tag_ExifIFDPointer); 63 | if (rem != 1) throw("Didn't remove one exif tag"); 64 | rem = j2.RemoveTag(TiffTag::tag_GpsInfoIFDPointer); 65 | if (rem != 1) throw("Didn't remove one gps tag"); 66 | j2.Save("testout/test_noexifgps.jpg"); 67 | } catch (const char *error) { 68 | fprintf(stderr, "Error: <%s> at outer level\n", error); 69 | exit(1); 70 | } 71 | return 0; 72 | } 73 | 74 | int test_string_tag_construction() { 75 | try { 76 | const char *new_title_string = "My new title string"; 77 | TiffTag *new_title = new TiffTag(TiffTag::tag_Title, 78 | TiffTag::tiff_string, 79 | strlen(new_title_string) + 1, 80 | (unsigned char *)new_title_string); 81 | if (new_title->GetTag() != TiffTag::tag_Title || 82 | new_title->GetType() != TiffTag::tiff_string || 83 | new_title->GetCount() != strlen(new_title_string) + 1 || 84 | strcmp(new_title->GetStringValue(), new_title_string) != 0) 85 | throw("Failed validating new title creation"); 86 | 87 | } catch (const char *error) { 88 | fprintf(stderr, "Error: <%s> at outer level\n", error); 89 | exit(1); 90 | } 91 | return 0; 92 | } 93 | 94 | int test_overwrite_old_string(const char * const filename) { 95 | try { 96 | Jpeg j2; 97 | const char *new_maker = "Replace the maker with this string"; 98 | const char *new_title_string = "New title"; 99 | bool success = j2.LoadFromFile(filename, true); 100 | if (!success) exit(1); 101 | TiffTag *make = j2.FindTag(TiffTag::tag_Make); 102 | if (make == NULL) throw("Couldn't find Make tag"); 103 | if (strcmp(make->GetStringValue(), "Panasonic") != 0) 104 | throw("Maker not \"Panasonic\""); 105 | make->SetStringValue(new_maker); 106 | if (strcmp(make->GetStringValue(), new_maker) != 0) 107 | throw("Maker not reset"); 108 | TiffTag *title = j2.FindTag(TiffTag::tag_Title); 109 | if (title != NULL) 110 | throw("Title shouldn't be already set."); 111 | TiffIfd *main_ifd = j2.GetIFD(); 112 | if (main_ifd == NULL) 113 | throw("Main IFD was NULL."); 114 | 115 | TiffTag *new_title = new TiffTag(TiffTag::tag_Title, 116 | TiffTag::tiff_string, 117 | strlen(new_title_string) + 1, 118 | (unsigned char *)new_title_string); 119 | int creation = main_ifd->AddTag(new_title, false); 120 | title = j2.FindTag(TiffTag::tag_Title); 121 | if (title == NULL) 122 | throw("Title should be set now."); 123 | if (strcmp(title->GetStringValue(), new_title_string) != 0) 124 | throw("New title not set."); 125 | 126 | j2.Save("testout/test_newmaker.jpg"); 127 | } catch (const char *error) { 128 | fprintf(stderr, "Error: <%s> at outer level\n", error); 129 | exit(1); 130 | } 131 | return 0; 132 | } 133 | } // namespace tests 134 | } // namespace jpeg_redaction 135 | 136 | int main(int argc, char **argv) { 137 | std::string filename("testdata/windows.jpg"); 138 | if (argc > 1) 139 | filename = argv[1]; 140 | jpeg_redaction::tests::test_string_tag_construction(); 141 | jpeg_redaction::tests::test_exif_removal(filename.c_str()); 142 | jpeg_redaction::tests::test_gps_removal(filename.c_str()); 143 | jpeg_redaction::tests::test_sensitive_removal(filename.c_str()); 144 | jpeg_redaction::tests::test_overwrite_old_string(filename.c_str()); 145 | } 146 | -------------------------------------------------------------------------------- /test/ifdtest.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Andrew W. Senior andrew.senior[AT]gmail.com 2 | // Part of the Jpeg-Redaction-Library to read, parse, edit redact and 3 | // write JPEG/EXIF/JFIF images. 4 | // See https://github.com/asenior/Jpeg-Redaction-Library 5 | 6 | // This program is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | 16 | // You should have received a copy of the GNU General Public License 17 | // along with this program. If not, see . 18 | 19 | 20 | // Simple test of reading an IFD at a given location. 21 | #include 22 | #include 23 | 24 | #include 25 | #include "tiff_ifd.h" 26 | 27 | int main(int argc, char **argv) { 28 | std::string filename("testdata/windows.jpg"); 29 | unsigned int ifd_loc = 20; 30 | unsigned int tiff_offset = 12; 31 | bool byte_swapping = false; 32 | if (argc > 1) 33 | filename = argv[1]; 34 | if (argc > 2) 35 | ifd_loc = atoi(argv[2]); 36 | printf("Dumping IFD in %s at position %u byteswapping %s\n", 37 | filename.c_str(), ifd_loc, (byte_swapping ? "on" : "off")); 38 | try { 39 | FILE *pFile = fopen(filename.c_str(), "rb"); 40 | if (pFile == NULL) { 41 | fprintf(stderr, "Can't open file %s\n", filename.c_str()); 42 | exit(1); 43 | } 44 | bool load_all = true; 45 | jpeg_redaction::TiffIfd ifd(pFile, ifd_loc, load_all, 46 | tiff_offset, byte_swapping); 47 | printf("%zu tags in IFD\n", ifd.GetNTags()); 48 | if (ifd.GetNTags() != 14) throw("Wrong number of tags"); 49 | const int tag_types[] = {0x10f, 0x110, 0x112, 0x11a, 0x11b, 0x128, 50 | 0x131, 0x132, 0x213, 0x8769, 0x8825, 0xc4a5, 51 | 0xc6d2, 0xc6d3}; 52 | const int tag_counts[] = {10,8, 1, 1, 1, 1, 10, 20, 1, 1, 1, 208, 64, 128}; 53 | for (int i=0; i < 14; ++i) { 54 | if (ifd.GetTag(i)->GetCount() != tag_counts[i]) 55 | throw("Wrong length at tag N"); 56 | if (ifd.GetTag(i)->GetTag() != tag_types[i]) 57 | throw("Wrong type at tag N"); 58 | } 59 | } catch (const char *error) { 60 | fprintf(stderr, "Error: <%s> at outer level\n", error); 61 | exit(1); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /test/jpegtest.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Andrew W. Senior andrew.senior[AT]gmail.com 2 | // Part of the Jpeg-Redaction-Library to read, parse, edit redact and 3 | // write JPEG/EXIF/JFIF images. 4 | // See https://github.com/asenior/Jpeg-Redaction-Library 5 | 6 | // This program is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | 16 | // You should have received a copy of the GNU General Public License 17 | // along with this program. If not, see . 18 | 19 | 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | #include "../lib/debug_flag.h" 26 | #include "jpeg.h" 27 | #include "redaction.h" 28 | #include "test_utils.h" 29 | 30 | int main(int argc, char **argv) { 31 | std::string filename("testdata/windows.jpg"); 32 | if (argc > 1) 33 | filename = argv[1]; 34 | // We'll compare goldens of stdout for each file, so dump 35 | // interesting info to stdout. 36 | jpeg_redaction::debug = 1; 37 | int rv = jpeg_redaction::tests::test_loadallfalse(filename.c_str()); 38 | if (rv) { 39 | fprintf(stderr, "Failed on loadallfalse\n"); 40 | exit(1); 41 | } 42 | rv = jpeg_redaction::tests::test_readwrite(filename.c_str()); 43 | if (rv) { 44 | fprintf(stderr, "Failed on test_readwrite\n"); 45 | exit(1); 46 | } 47 | rv = jpeg_redaction::tests::test_redaction(filename.c_str(), 48 | ";50,300,50,200;"); 49 | if (rv) { 50 | fprintf(stderr, "Failed on test_redaction\n"); 51 | exit(1); 52 | } 53 | rv = jpeg_redaction::tests::test_reversingredactions_multi(filename); 54 | if (rv) { 55 | fprintf(stderr, "Failed on test_reversingredactions_multi\n"); 56 | exit(1); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /test/metadatatest.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Andrew W. Senior andrew.senior[AT]gmail.com 2 | // Part of the Jpeg-Redaction-Library to read, parse, edit redact and 3 | // write JPEG/EXIF/JFIF images. 4 | // See https://github.com/asenior/Jpeg-Redaction-Library 5 | 6 | // This program is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | 16 | // You should have received a copy of the GNU General Public License 17 | // along with this program. If not, see . 18 | 19 | 20 | // Simple test of inserting a metadata blob into a JPEG file and 21 | // retrieving it. 22 | 23 | #include 24 | #include 25 | 26 | #include 27 | #include "jpeg.h" 28 | #include "debug_flag.h" 29 | #include "redaction.h" 30 | 31 | namespace jpeg_redaction { 32 | namespace tests { 33 | int test_insert_and_save(const char * const filename, int blobsize) { 34 | try { 35 | Jpeg original; 36 | printf("*********** Loading original image*********** ***************\n"); 37 | bool success = original.LoadFromFile(filename, true); 38 | if (!success) exit(1); 39 | std::vector blob; 40 | blob.resize(blobsize); 41 | for (int i = 0; i < blob.size(); ++i) { 42 | blob[i] = (unsigned char)(rand() * 255); 43 | } 44 | original.SetObscuraMetaData(blob.size(), &blob.front()); 45 | 46 | printf("*********** Writing image with added metadata ***************\n"); 47 | std::string output_filename = "testout/test_with_metadata.jpg"; 48 | debug = 1; 49 | original.Save(output_filename.c_str()); 50 | 51 | printf("*********** Reloading image with added metadata *************\n"); 52 | Jpeg reread; 53 | success = reread.LoadFromFile(output_filename.c_str(), true); 54 | if (!success) { 55 | fprintf(stderr, "Failed read"); 56 | exit(1); 57 | } 58 | unsigned int length = 0; 59 | const unsigned char *retrieved_blob = reread.GetObscuraMetaData(&length); 60 | if (length != blob.size()) { 61 | fprintf(stderr, "Original length %zu, new length %u\n", 62 | blob.size(), length); 63 | throw("Retrieved metadata wrong length"); 64 | } 65 | for (int i = 0; i < length; ++i) { 66 | if (retrieved_blob[i] != blob[i]) { 67 | printf("Metadata mismatch at %d/%d\n", i, length); 68 | throw("Retrieved metadata not identical."); 69 | } 70 | } 71 | } catch (const char *error) { 72 | fprintf(stderr, "Error: <%s> at outer level\n", error); 73 | exit(1); 74 | } 75 | return 0; 76 | } 77 | 78 | // Take an image, redact it. 79 | // Write redaction regions in the redacted JPEG file. 80 | // Reload and reverse the redaction. 81 | int test_save_redaction_regions(const char * const filename) { 82 | try { 83 | Jpeg original; 84 | printf("*********** Loading original image*********** ***************\n"); 85 | bool success = original.LoadFromFile(filename, true); 86 | if (!success) exit(1); 87 | Redaction redaction; 88 | Redaction::Region rect(50, 300, 64, 79); // l,r, t, b 89 | redaction.AddRegion(rect); 90 | original.DecodeImage(&redaction, NULL); 91 | if (!redaction.ValidateStrips()) 92 | throw("Strips not valid"); 93 | // sos_block = j2.GetMarker(Jpeg::jpeg_sos); 94 | // SaveBytes(sos_block->data_, "testout/redactedsos"); 95 | std::vector redaction_pack; 96 | redaction.Pack(&redaction_pack); 97 | printf("Packed redaction object is %zu bytes\n", redaction_pack.size()); 98 | original.SetObscuraMetaData(redaction_pack.size(), 99 | &redaction_pack.front()); 100 | 101 | printf("*********** Writing image with added metadata ***************\n"); 102 | std::string output_filename = "testout/test_with_metadata.jpg"; 103 | debug = 1; 104 | original.Save(output_filename.c_str()); 105 | 106 | printf("*********** Reloading image with added metadata *************\n"); 107 | Jpeg reread; 108 | success = reread.LoadFromFile(output_filename.c_str(), true); 109 | if (!success) { 110 | fprintf(stderr, "Failed read"); 111 | exit(1); 112 | } 113 | unsigned int length = 0; 114 | const unsigned char *retrieved_blob = reread.GetObscuraMetaData(&length); 115 | if (length != redaction_pack.size()) { 116 | fprintf(stderr, "Original length %zu, new length %u\n", 117 | redaction_pack.size(), length); 118 | throw("Retrieved metadata wrong length"); 119 | } 120 | // Check that the two packs are the same. 121 | if (memcmp(retrieved_blob, &redaction_pack.front(), length) != 0) 122 | throw("Retrieved metadata not identical."); 123 | 124 | // Now use the pack to reverse the redaction. 125 | std::vector loaded_pack(length); 126 | memcpy(&loaded_pack[0], retrieved_blob, length); 127 | Redaction unpacked_redaction; 128 | unpacked_redaction.Unpack(loaded_pack); 129 | reread.ReverseRedaction(unpacked_redaction); 130 | output_filename = "testout/testunredacted_pack.jpg"; 131 | if (reread.Save(output_filename.c_str()) != 0) { 132 | fprintf(stderr, "Couldn't save %s\n", output_filename.c_str()); 133 | return 1; 134 | } 135 | } catch (const char *error) { 136 | fprintf(stderr, "Error: <%s> at outer level\n", error); 137 | exit(1); 138 | } 139 | return 0; 140 | } 141 | } // namespace tests 142 | } // namespace jpeg_redaction 143 | 144 | int main(int argc, char **argv) { 145 | std::string filename("testdata/windows.jpg"); 146 | if (argc > 1) 147 | filename = argv[1]; 148 | jpeg_redaction::tests::test_insert_and_save(filename.c_str(), 2000); 149 | jpeg_redaction::tests::test_insert_and_save(filename.c_str(), 64* 1024 * 5); 150 | jpeg_redaction::tests::test_save_redaction_regions(filename.c_str()); 151 | } 152 | -------------------------------------------------------------------------------- /test/test_redaction.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Andrew W. Senior andrew.senior[AT]gmail.com 2 | // Part of the Jpeg-Redaction-Library to read, parse, edit redact and 3 | // write JPEG/EXIF/JFIF images. 4 | // See https://github.com/asenior/Jpeg-Redaction-Library 5 | 6 | // This program is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | 16 | // You should have received a copy of the GNU General Public License 17 | // along with this program. If not, see . 18 | 19 | 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | #include "../lib/debug_flag.h" 26 | #include "jpeg.h" 27 | #include "redaction.h" 28 | #include "test_utils.h" 29 | 30 | int TestRedaction(const std::string &filename, const char *const regions) { 31 | int rv = jpeg_redaction::tests::test_redaction(filename.c_str(), regions); 32 | if (rv) { 33 | fprintf(stderr, "Failed on test_redaction %s\n", regions); 34 | return 1; 35 | } 36 | return 0; 37 | } 38 | 39 | int TestRedactionPack(const std::string &filename, const char *const regions) { 40 | int rv = jpeg_redaction::tests::test_redaction_pack_unpack(filename.c_str(), 41 | regions); 42 | if (rv) { 43 | fprintf(stderr, "Failed on test_redaction_pack_unpack %s\n", regions); 44 | return 1; 45 | } 46 | return 0; 47 | } 48 | 49 | int main(int argc, char **argv) { 50 | // An 1152 x 693 JPEG without EXIF. 51 | // redaction strings are l,r,t,b[:method];... 52 | // [c]opystrip, [S]olid, [p]ixellate,[i]nverse pixellate 53 | std::string filename("testdata/devices/samsung1.jpg"); 54 | if (argc > 1) 55 | filename = argv[1]; 56 | jpeg_redaction::debug = 0; 57 | 58 | if (TestRedactionPack(filename, ";50,300,50,200:p;")) return 1; 59 | 60 | // Different redaction types. 61 | if (TestRedaction(filename, ";50,300,50,200:p;")) return 1; 62 | if (TestRedaction(filename, ";50,300,50,200:s;")) return 1; 63 | if (TestRedaction(filename, ";50,300,50,200:c;")) return 1; 64 | if (TestRedaction(filename, ";50,300,50,200:i;")) return 1; 65 | 66 | // Corner cases. 67 | // Completely off left. 68 | if (TestRedaction(filename, "-50,-10,50,200:p;")) return 1; 69 | // Overlapping top left. 70 | if (TestRedaction(filename, "-50,800,-50,200:p;")) return 1; 71 | if (TestRedaction(filename, "50,199,-500,-200:p;")) return 1; 72 | if (TestRedaction(filename, "2000,2200,50,200:p;")) return 1; 73 | if (TestRedaction(filename, "-10,1200,50,200:p;")) return 1; 74 | 75 | // Multiple regions. 76 | if (TestRedaction(filename, ";50,300,50,200:p;200,500,120,500:p")) return 1; 77 | // ... with different types. 78 | if (TestRedaction(filename, ";50,300,50,200:s;200,500,120,500:p")) return 1; 79 | if (TestRedaction(filename, ";50,300,50,200:p;200,500,120,500:s")) return 1; 80 | if (TestRedaction(filename, ";50,300,50,200:s;200,500,120,500:s")) return 1; 81 | } 82 | -------------------------------------------------------------------------------- /test/test_utils.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Andrew W. Senior andrew.senior[AT]gmail.com 2 | // Part of the Jpeg-Redaction-Library to read, parse, edit redact and 3 | // write JPEG/EXIF/JFIF images. 4 | // See https://github.com/asenior/Jpeg-Redaction-Library 5 | 6 | // This program is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | 16 | // You should have received a copy of the GNU General Public License 17 | // along with this program. If not, see . 18 | 19 | 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | #include "jpeg.h" 26 | #include "jpeg_marker.h" 27 | #include "redaction.h" 28 | #include "test_utils.h" 29 | 30 | namespace jpeg_redaction { 31 | namespace tests { 32 | 33 | // Start with several test helper functions. 34 | // return true if files are byte identical. 35 | bool compare_to_golden(const char * const filename, 36 | const char * const golden_name) { 37 | FILE *output = fopen(filename, "rb"); 38 | if (output == NULL) throw("Can't open output file"); 39 | FILE *golden = fopen(golden_name, "rb"); 40 | if (golden == NULL) throw("Can't open golden file"); 41 | int count = 0; 42 | bool success = false; 43 | 44 | while (1) { 45 | unsigned char o_byte, g_byte; 46 | fread(&o_byte, sizeof(char), 1, output); 47 | fread(&g_byte, sizeof(char), 1, golden); 48 | if (feof(output)) { 49 | if (feof(golden)) 50 | success = true; 51 | else 52 | fprintf(stderr, "output %s shorter (%d bytes) than golden %s\n", 53 | filename, count, golden_name); 54 | break; 55 | } else { 56 | if (feof(golden)) { 57 | fprintf(stderr, "golden %s shorter (%d bytes) than output %s\n", 58 | golden_name, count, filename); 59 | break; 60 | } 61 | } 62 | if (o_byte != g_byte) { 63 | fprintf(stderr, "golden %s differs from output %s at byte %d\n", 64 | golden_name, filename, count); 65 | break; 66 | } 67 | ++count; 68 | } 69 | return success; 70 | } 71 | // Check whether two blocks of bytes are identical or not. 72 | bool compare_bytes(const std::vector &orig, 73 | const std::vector &other) { 74 | if (orig.size() != other.size()) { 75 | fprintf(stderr, "Size mismatch: orig %zu vs new %zu\n", 76 | orig.size(), other.size()); 77 | return false; 78 | } 79 | for(int i = 0; i < orig.size(); ++i) { 80 | if (orig[i] != other[i]) { 81 | fprintf(stderr, 82 | "Byte mismatch at %d of %zu: new %02x vs orig %02x\n", 83 | i, orig.size(), other[i], orig[i]); 84 | return false; 85 | } 86 | } 87 | return true; 88 | } 89 | void SaveBytes(const std::vector &bytes, 90 | const char *const fn) { 91 | FILE *file = fopen(fn, "wb"); 92 | if (file == NULL) { 93 | fprintf(stderr, "Can't write to file %s\n", fn); 94 | return; 95 | } 96 | int rv = fwrite(&(bytes[0]), sizeof(unsigned char), bytes.size(), file); 97 | if (rv != bytes.size()) throw("can't save bytes"); 98 | fclose(file); 99 | } 100 | 101 | // Test reading and parsing a jpeg file, without loading all the data. 102 | int test_loadallfalse(const char * const filename) { 103 | printf("Testing with loadall=false\n"); 104 | try { 105 | Jpeg j2; 106 | bool success = j2.LoadFromFile(filename, false); 107 | } catch (const char *error) { 108 | fprintf(stderr, "Error: <%s> at outer level\n", error); 109 | return 1; 110 | } 111 | return 0; 112 | } 113 | 114 | // Test reading a jpeg and writing it out again. 115 | int test_readwrite(const char * const filename) { 116 | printf("Testing with loadall=true\n"); 117 | try { 118 | Jpeg j2; 119 | bool success = j2.LoadFromFile(filename, true); 120 | 121 | j2.DecodeImage(NULL, "testout/rawgrey.pgm"); 122 | std::string output_filename = "testout/testplainoutput.jpg"; 123 | if (j2.Save(output_filename.c_str()) != 0) { 124 | fprintf(stderr, "Couldn't save %s\n", output_filename.c_str()); 125 | return 1; 126 | } 127 | } catch (const char *error) { 128 | fprintf(stderr, "Error: <%s> at outer level\n", error); 129 | return 1; 130 | } 131 | return 0; 132 | } 133 | 134 | // Test wiping a region from a jpeg file. 135 | int test_redaction(const char * const filename, 136 | const char *const regions) { 137 | printf("Testing redaction\n"); 138 | try { 139 | Jpeg j2; 140 | bool success = j2.LoadFromFile(filename, true); 141 | Redaction redaction; 142 | 143 | redaction.AddRegions(regions); 144 | j2.DecodeImage(&redaction, NULL); 145 | if (!redaction.ValidateStrips()) 146 | throw("Strips not valid"); 147 | std::string output_filename = "testout/testoutput.jpg"; 148 | if (j2.Save(output_filename.c_str()) != 0) { 149 | fprintf(stderr, "Couldn't save %s\n", output_filename.c_str()); 150 | return 1; 151 | } 152 | } catch (const char *error) { 153 | fprintf(stderr, "Error: <%s> at outer level\n", error); 154 | return 1; 155 | } 156 | return 0; 157 | } 158 | 159 | // Test wiping a region from a jpeg file, packing data up in a blob 160 | // and unpacking it. 161 | int test_redaction_pack_unpack(const char * const filename, 162 | const char *const regions) { 163 | printf("Testing redaction\n"); 164 | try { 165 | Jpeg j2; 166 | bool success = j2.LoadFromFile(filename, true); 167 | Redaction redaction; 168 | 169 | redaction.AddRegions(regions); 170 | j2.DecodeImage(&redaction, NULL); 171 | if (!redaction.ValidateStrips()) 172 | throw("Strips not valid"); 173 | std::vector redaction_pack; 174 | redaction.Pack(&redaction_pack); 175 | printf("Packed redaction object is %zu bytes\n", redaction_pack.size()); 176 | 177 | Redaction unpacked_redaction; 178 | unpacked_redaction.Unpack(redaction_pack); 179 | j2.ReverseRedaction(unpacked_redaction); 180 | } catch (const char *error) { 181 | fprintf(stderr, "Error: <%s> at outer level\n", error); 182 | return 1; 183 | } 184 | return 0; 185 | } 186 | 187 | // Test wiping a region and then restoring it again from 188 | // saved redaction data (strips). 189 | int test_reversingredaction(const char * const filename, 190 | const Redaction::Region &rect) { 191 | try { 192 | Jpeg j2; 193 | bool success = j2.LoadFromFile(filename, true); 194 | JpegMarker *sos_block = j2.GetMarker(Jpeg::jpeg_sos); 195 | std::vector original(sos_block->data_); 196 | SaveBytes(sos_block->data_, "testout/originalsos"); 197 | // Redaction::Region rect(50, 300, 50, 200); // l,r, t, b 198 | // One strip 199 | Redaction redaction; 200 | redaction.AddRegion(rect); 201 | j2.DecodeImage(&redaction, NULL); 202 | if (!redaction.ValidateStrips()) 203 | throw("Strips not valid"); 204 | // sos_block = j2.GetMarker(Jpeg::jpeg_sos); 205 | // SaveBytes(sos_block->data_, "testout/redactedsos"); 206 | const JpegStrip *strip = redaction.GetStrip(0); 207 | j2.ReverseRedaction(redaction); 208 | sos_block = j2.GetMarker(Jpeg::jpeg_sos); 209 | if (! compare_bytes(original, sos_block->data_)) 210 | throw("test_reversingredaction Compare test fail"); 211 | SaveBytes(sos_block->data_, "testout/unredactedsos"); 212 | std::string output_filename = "testout/testunredacted.jpg"; 213 | if (j2.Save(output_filename.c_str()) != 0) { 214 | fprintf(stderr, "Couldn't save %s\n", output_filename.c_str()); 215 | return 1; 216 | } 217 | } catch (const char *error) { 218 | fprintf(stderr, "Error: <%s> at outer level\n", error); 219 | return 1; 220 | } 221 | return 0; 222 | } 223 | // TODO this can fail- we need to keep track of the padding bits both when redacting and 224 | // reversing redaction. 225 | int test_reversingredactions_multi(const std::string &filename) { 226 | Redaction::Region rectonestrip(50, 300, 64, 79); // l,r, t, b 227 | int rv = test_reversingredaction(filename.c_str(), rectonestrip); 228 | if (rv) return 1; 229 | Redaction::Region rect(50, 500, 89, 245); // l,r, t, b 230 | rv = test_reversingredaction(filename.c_str(), rect); 231 | if (rv) return 1; 232 | return 0; 233 | } 234 | 235 | } // namespace tests 236 | } // namespace jpeg_redaction 237 | -------------------------------------------------------------------------------- /test/test_utils.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Andrew W. Senior andrew.senior[AT]gmail.com 2 | // Part of the Jpeg-Redaction-Library to read, parse, edit redact and 3 | // write JPEG/EXIF/JFIF images. 4 | // See https://github.com/asenior/Jpeg-Redaction-Library 5 | 6 | // This program is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | 16 | // You should have received a copy of the GNU General Public License 17 | // along with this program. If not, see . 18 | 19 | 20 | #ifndef INCLUDE_JPEG_REDACTION_TEST_TEST_UTILS 21 | #define INCLUDE_JPEG_REDACTION_TEST_TEST_UTILS 22 | 23 | #include 24 | #include 25 | namespace jpeg_redaction { 26 | // class Redaction::Rect; 27 | namespace tests { 28 | bool compare_to_golden(const char * const filename, 29 | const char * const golden_name); 30 | bool compare_bytes(const std::vector &orig, 31 | const std::vector &other); 32 | void SaveBytes(const std::vector &bytes, 33 | const char *const fn); 34 | int test_loadallfalse(const char * const filename); 35 | int test_readwrite(const char * const filename); 36 | int test_redaction(const char * const filename, 37 | const char * const regions); 38 | int test_redaction_pack_unpack(const char * const filename, 39 | const char *const regions); 40 | int test_reversingredaction(const char * const filename, 41 | const Redaction::Region &rect); 42 | int test_reversingredactions_multi(const std::string &filename); 43 | } // namespace tests 44 | } // namespace jpeg_redaction 45 | #endif // INCLUDE_JPEG_REDACTION_TEST_TEST_UTILS 46 | -------------------------------------------------------------------------------- /test/testdata/devices/EvoRed.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asenior/Jpeg-Redaction-Library/0286e45e6f761e938b50e9924abf302b72bd122a/test/testdata/devices/EvoRed.jpg -------------------------------------------------------------------------------- /test/testdata/devices/G1Desk.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asenior/Jpeg-Redaction-Library/0286e45e6f761e938b50e9924abf302b72bd122a/test/testdata/devices/G1Desk.jpg -------------------------------------------------------------------------------- /test/testdata/devices/NexusOneShower.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asenior/Jpeg-Redaction-Library/0286e45e6f761e938b50e9924abf302b72bd122a/test/testdata/devices/NexusOneShower.jpg -------------------------------------------------------------------------------- /test/testdata/devices/NokiaN810.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asenior/Jpeg-Redaction-Library/0286e45e6f761e938b50e9924abf302b72bd122a/test/testdata/devices/NokiaN810.jpg -------------------------------------------------------------------------------- /test/testdata/devices/droid.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asenior/Jpeg-Redaction-Library/0286e45e6f761e938b50e9924abf302b72bd122a/test/testdata/devices/droid.jpg -------------------------------------------------------------------------------- /test/testdata/devices/failing/canon-1999.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asenior/Jpeg-Redaction-Library/0286e45e6f761e938b50e9924abf302b72bd122a/test/testdata/devices/failing/canon-1999.jpg -------------------------------------------------------------------------------- /test/testdata/devices/filmworks_filmscan.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asenior/Jpeg-Redaction-Library/0286e45e6f761e938b50e9924abf302b72bd122a/test/testdata/devices/filmworks_filmscan.jpg -------------------------------------------------------------------------------- /test/testdata/devices/olympusC3030.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asenior/Jpeg-Redaction-Library/0286e45e6f761e938b50e9924abf302b72bd122a/test/testdata/devices/olympusC3030.jpg -------------------------------------------------------------------------------- /test/testdata/devices/olympusDSLR.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asenior/Jpeg-Redaction-Library/0286e45e6f761e938b50e9924abf302b72bd122a/test/testdata/devices/olympusDSLR.jpg -------------------------------------------------------------------------------- /test/testdata/devices/samsung1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asenior/Jpeg-Redaction-Library/0286e45e6f761e938b50e9924abf302b72bd122a/test/testdata/devices/samsung1.jpg -------------------------------------------------------------------------------- /test/testdata/ifdtest_golden.log: -------------------------------------------------------------------------------- 1 | Dumping IFD in testdata/windows.jpg at position 20 byteswapping off 2 | IFDSize: 14 3 | 4 | DIDIFD 5 | Loaded Image data 6 | IFDSize: 34 7 | 8 | DIDIFD 9 | Loaded Image data 10 | Reading Makernote 11 | Loading Panasonic 12 | IFDSize: 79 13 | 14 | DIDIFD 15 | Loaded Image data 16 | tiff_ifd.cpp:77 Listing 79 tags 17 | 0x0001 GPSlat_ref 1x2 uint16 (2) 18 | 0x0002 GPSlat 4x1 bytes (0x00 0x01 0x00 0x00) 19 | 0x0003 GPSlong_ref 1x2 uint16 (1) 20 | 0x0007 GPStime_stamp 1x2 uint16 (1) 21 | 0x000f GPStrack 2x1 uint8 (16 0) 22 | 0x001a GPSdest_dist 1x2 uint16 (2) 23 | 0x001c Unknown 1x2 uint16 (2) 24 | 0x001f Unknown 1x2 uint16 (7) 25 | 0x0020 Unknown 1x2 uint16 (2) 26 | 0x0021 Unknown 8200x1 bytes (0x44 0x56 0x01 0x02 ) 27 | 0x0022 Unknown 1x2 uint16 (0) 28 | 0x0023 Unknown 1x2 uint16 (0) 29 | 0x0024 Unknown 1x2 uint16 (0) 30 | 0x0025 Unknown 16x1 bytes (0x46 0x35 0x33 0x31 ) 31 | 0x0026 Unknown 4x1 bytes (0x30 0x33 0x30 0x30) 32 | 0x0027 Unknown 1x2 uint16 (0) 33 | 0x0028 Unknown 1x2 uint16 (1) 34 | 0x0029 Unknown 1x4 uint32 (3713) 35 | 0x002a Unknown 1x2 uint16 (0) 36 | 0x002b Unknown 1x4 uint32 (0) 37 | 0x002c Unknown 1x2 uint16 (0) 38 | 0x002d Unknown 1x2 uint16 (0) 39 | 0x002e Unknown 1x2 uint16 (1) 40 | 0x002f Unknown 1x2 uint16 (1) 41 | 0x0030 Unknown 1x2 uint16 (1) 42 | 0x0031 Unknown 1x2 uint16 (4) 43 | 0x0032 Unknown 1x2 uint16 (0) 44 | 0x0033 Unknown 20x1 string ("9999:99:99 00:00:00") 45 | 0x0034 Unknown 1x2 uint16 (1) 46 | 0x0035 Unknown 1x2 uint16 (1) 47 | 0x0036 Unknown 1x2 uint16 (65535) 48 | 0x0037 Unknown 1x2 uint16 (257) 49 | 0x0038 Unknown 1x2 uint16 (2) 50 | 0x0039 Unknown 1x2 uint16 (0) 51 | 0x003a Unknown 1x2 uint16 (1) 52 | 0x003b Unknown 1x2 uint16 (1) 53 | 0x003c Unknown 1x2 uint16 (65535) 54 | 0x003d Unknown 1x2 uint16 (1) 55 | 0x003e Unknown 1x2 uint16 (1) 56 | 0x003f Unknown 1x2 uint16 (0) 57 | 0x0040 Unknown 1x2 uint16 (0) 58 | 0x0041 Unknown 1x2 uint16 (0) 59 | 0x0049 Unknown 1x2 uint16 (0) 60 | 0x004d Unknown 2x8 urational (0.500000 0.500000) 61 | 0x004e Unknown 42x1 bytes (0x00 0x00 0x00 0x00 ) 62 | 0x004f Unknown 1x2 uint16 (0) 63 | 0x0050 Unknown 1x2 uint16 (0) 64 | 0x0057 Unknown 1x2 uint16 (0) 65 | 0x0059 Unknown 4x1 bytes (0x00 0x00 0x00 0x00) 66 | 0x005d Unknown 1x2 uint16 (0) 67 | 0x005e Unknown 4x1 bytes (0x00 0x00 0x00 0x06) 68 | 0x0061 Unknown 148x1 bytes (0x00 0x00 0x00 0x00 ) 69 | 0x0062 Unknown 1x2 uint16 (0) 70 | 0x0063 Unknown 4x1 bytes (0x00 0x00 0x00 0x00) 71 | 0x0065 Unknown 64x1 bytes (0x00 0x00 0x00 0x00 ) 72 | 0x0066 Unknown 64x1 bytes (0x00 0x00 0x00 0x00 ) 73 | 0x0067 Unknown 64x1 bytes (0x00 0x00 0x00 0x00 ) 74 | 0x0068 Unknown 1x1 uint8 (1) 75 | 0x0069 Unknown 72x1 bytes (0x45 0x4e 0x47 0x4c ) 76 | 0x006a Unknown 1x1 uint8 (1) 77 | 0x006b Unknown 72x1 bytes (0x4c 0x4f 0x4e 0x44 ) 78 | 0x006c Unknown 1x1 uint8 (1) 79 | 0x006d Unknown 72x1 bytes (0x4c 0x4f 0x4e 0x44 ) 80 | 0x006e Unknown 1x1 uint8 (1) 81 | 0x006f Unknown 128x1 bytes (0x52 0x4f 0x53 0x53 ) 82 | 0x0070 Unknown 1x1 uint8 (0) 83 | 0x0071 Unknown 128x1 bytes (0x00 0x00 0x00 0x00 ) 84 | 0x8000 Unknown 4x1 bytes (0x30 0x31 0x33 0x33) 85 | 0x8001 Unknown 1x2 uint16 (0) 86 | 0x8002 Unknown 1x2 uint16 (1) 87 | 0x8003 Unknown 1x2 uint16 (1) 88 | 0x8004 Unknown 1x2 uint16 (1691) 89 | 0x8005 Unknown 1x2 uint16 (1054) 90 | 0x8006 Unknown 1x2 uint16 (1984) 91 | 0x8007 Unknown 1x2 uint16 (1) 92 | 0x8008 Unknown 1x2 uint16 (1) 93 | 0x8009 Unknown 1x2 uint16 (1) 94 | 0x8010 Unknown 20x1 string ("9999:99:99 00:00:00") 95 | 0x8012 Unknown 4x1 bytes (0x00 0x00 0x00 0x00) 96 | 97 | Panasonic69: ENGLAND 98 | Panasonic6b: LONDON 99 | Panasonic6d: LONDON 100 | Panasonic6f: ROSSLYN HILL UNITARIAN CHAPEL 101 | IFDSize: 2 102 | 103 | DIDIFD 104 | Loaded Image data 105 | tiff_ifd.cpp:77 Listing 2 tags 106 | 0x0001 GPSlat_ref 4x1 string ("R98") 107 | 0x0002 GPSlat 4x1 bytes (0x30 0x31 0x30 0x30) 108 | 109 | tiff_ifd.cpp:77 Listing 34 tags 110 | 0x829a ExposureTime 1x8 urational (0.016667) 111 | 0x829d FNumber 1x8 urational (4.300000) 112 | 0x8822 ExposureProgram 1x2 uint16 (3) 113 | 0x8827 ISOSpeedRatings 1x2 uint16 (80) 114 | 0x9000 ExifVersion 4x1 bytes (0x30 0x32 0x32 0x31) 115 | 0x9003 DateTimeOriginal 20x1 string ("2011:03:12 14:45:33") 116 | 0x9004 DateTimeDigitized 20x1 string ("2011:03:12 14:45:33") 117 | 0x9101 Unknown 4x1 bytes (0x01 0x02 0x03 0x00) 118 | 0x9102 Unknown 1x8 urational (4.000000) 119 | 0x9204 ExposureBiasValue 1x8 rational (0.000000) 120 | 0x9205 MaxApertureValue 1x8 urational (3.440000) 121 | 0x9207 MeteringMode 1x2 uint16 (5) 122 | 0x9208 LightSource 1x2 uint16 (0) 123 | 0x9209 Flash 1x2 uint16 (16) 124 | 0x920a FocalLength 1x8 urational (15.800000) 125 | 0x927c MakerNote 10088x1 bytes (Now printing makernote 126 | tiff_ifd.cpp:77 Listing 79 tags 127 | 0x0001 GPSlat_ref 1x2 uint16 (2) 128 | 0x0002 GPSlat 4x1 bytes (0x00 0x01 0x00 0x00) 129 | 0x0003 GPSlong_ref 1x2 uint16 (1) 130 | 0x0007 GPStime_stamp 1x2 uint16 (1) 131 | 0x000f GPStrack 2x1 uint8 (16 0) 132 | 0x001a GPSdest_dist 1x2 uint16 (2) 133 | 0x001c Unknown 1x2 uint16 (2) 134 | 0x001f Unknown 1x2 uint16 (7) 135 | 0x0020 Unknown 1x2 uint16 (2) 136 | 0x0021 Unknown 8200x1 bytes (0x44 0x56 0x01 0x02 ) 137 | 0x0022 Unknown 1x2 uint16 (0) 138 | 0x0023 Unknown 1x2 uint16 (0) 139 | 0x0024 Unknown 1x2 uint16 (0) 140 | 0x0025 Unknown 16x1 bytes (0x46 0x35 0x33 0x31 ) 141 | 0x0026 Unknown 4x1 bytes (0x30 0x33 0x30 0x30) 142 | 0x0027 Unknown 1x2 uint16 (0) 143 | 0x0028 Unknown 1x2 uint16 (1) 144 | 0x0029 Unknown 1x4 uint32 (3713) 145 | 0x002a Unknown 1x2 uint16 (0) 146 | 0x002b Unknown 1x4 uint32 (0) 147 | 0x002c Unknown 1x2 uint16 (0) 148 | 0x002d Unknown 1x2 uint16 (0) 149 | 0x002e Unknown 1x2 uint16 (1) 150 | 0x002f Unknown 1x2 uint16 (1) 151 | 0x0030 Unknown 1x2 uint16 (1) 152 | 0x0031 Unknown 1x2 uint16 (4) 153 | 0x0032 Unknown 1x2 uint16 (0) 154 | 0x0033 Unknown 20x1 string ("9999:99:99 00:00:00") 155 | 0x0034 Unknown 1x2 uint16 (1) 156 | 0x0035 Unknown 1x2 uint16 (1) 157 | 0x0036 Unknown 1x2 uint16 (65535) 158 | 0x0037 Unknown 1x2 uint16 (257) 159 | 0x0038 Unknown 1x2 uint16 (2) 160 | 0x0039 Unknown 1x2 uint16 (0) 161 | 0x003a Unknown 1x2 uint16 (1) 162 | 0x003b Unknown 1x2 uint16 (1) 163 | 0x003c Unknown 1x2 uint16 (65535) 164 | 0x003d Unknown 1x2 uint16 (1) 165 | 0x003e Unknown 1x2 uint16 (1) 166 | 0x003f Unknown 1x2 uint16 (0) 167 | 0x0040 Unknown 1x2 uint16 (0) 168 | 0x0041 Unknown 1x2 uint16 (0) 169 | 0x0049 Unknown 1x2 uint16 (0) 170 | 0x004d Unknown 2x8 urational (0.500000 0.500000) 171 | 0x004e Unknown 42x1 bytes (0x00 0x00 0x00 0x00 ) 172 | 0x004f Unknown 1x2 uint16 (0) 173 | 0x0050 Unknown 1x2 uint16 (0) 174 | 0x0057 Unknown 1x2 uint16 (0) 175 | 0x0059 Unknown 4x1 bytes (0x00 0x00 0x00 0x00) 176 | 0x005d Unknown 1x2 uint16 (0) 177 | 0x005e Unknown 4x1 bytes (0x00 0x00 0x00 0x06) 178 | 0x0061 Unknown 148x1 bytes (0x00 0x00 0x00 0x00 ) 179 | 0x0062 Unknown 1x2 uint16 (0) 180 | 0x0063 Unknown 4x1 bytes (0x00 0x00 0x00 0x00) 181 | 0x0065 Unknown 64x1 bytes (0x00 0x00 0x00 0x00 ) 182 | 0x0066 Unknown 64x1 bytes (0x00 0x00 0x00 0x00 ) 183 | 0x0067 Unknown 64x1 bytes (0x00 0x00 0x00 0x00 ) 184 | 0x0068 Unknown 1x1 uint8 (1) 185 | 0x0069 Unknown 72x1 bytes (0x45 0x4e 0x47 0x4c ) 186 | 0x006a Unknown 1x1 uint8 (1) 187 | 0x006b Unknown 72x1 bytes (0x4c 0x4f 0x4e 0x44 ) 188 | 0x006c Unknown 1x1 uint8 (1) 189 | 0x006d Unknown 72x1 bytes (0x4c 0x4f 0x4e 0x44 ) 190 | 0x006e Unknown 1x1 uint8 (1) 191 | 0x006f Unknown 128x1 bytes (0x52 0x4f 0x53 0x53 ) 192 | 0x0070 Unknown 1x1 uint8 (0) 193 | 0x0071 Unknown 128x1 bytes (0x00 0x00 0x00 0x00 ) 194 | 0x8000 Unknown 4x1 bytes (0x30 0x31 0x33 0x33) 195 | 0x8001 Unknown 1x2 uint16 (0) 196 | 0x8002 Unknown 1x2 uint16 (1) 197 | 0x8003 Unknown 1x2 uint16 (1) 198 | 0x8004 Unknown 1x2 uint16 (1691) 199 | 0x8005 Unknown 1x2 uint16 (1054) 200 | 0x8006 Unknown 1x2 uint16 (1984) 201 | 0x8007 Unknown 1x2 uint16 (1) 202 | 0x8008 Unknown 1x2 uint16 (1) 203 | 0x8009 Unknown 1x2 uint16 (1) 204 | 0x8010 Unknown 20x1 string ("9999:99:99 00:00:00") 205 | 0x8012 Unknown 4x1 bytes (0x00 0x00 0x00 0x00) 206 | 207 | ) 208 | 0xa000 Unknown 4x1 bytes (0x30 0x31 0x30 0x30) 209 | 0xa001 Unknown 1x2 uint16 (1) 210 | 0xa002 Unknown 1x4 uint32 (3264) 211 | 0xa003 Unknown 1x4 uint32 (2448) 212 | 0xa005 Interoperability 1x4 uint32 (IFD 11516) 213 | 0xa217 Unknown 1x2 uint16 (2) 214 | 0xa300 Unknown 1x1 bytes (0x03) 215 | 0xa301 Unknown 1x1 bytes (0x01) 216 | 0xa401 Unknown 1x2 uint16 (0) 217 | 0xa402 Unknown 1x2 uint16 (0) 218 | 0xa403 Unknown 1x2 uint16 (0) 219 | 0xa404 Unknown 1x8 urational (0.000000) 220 | 0xa405 Unknown 1x2 uint16 (96) 221 | 0xa406 Unknown 1x2 uint16 (0) 222 | 0xa407 Unknown 1x2 uint16 (0) 223 | 0xa408 Unknown 1x2 uint16 (0) 224 | 0xa409 Unknown 1x2 uint16 (0) 225 | 0xa40a Unknown 1x2 uint16 (0) 226 | 227 | IFDSize: 13 228 | 229 | DIDIFD 230 | Loaded Image data 231 | tiff_ifd.cpp:77 Listing 13 tags 232 | 0x0000 GPSversion 4x1 uint8 (2 2 0 0) 233 | 0x0001 GPSlat_ref 2x1 string ("N") 234 | 0x0002 GPSlat 3x8 urational (51.000000 33.000000 19.350000) 235 | 0x0003 GPSlong_ref 2x1 string ("W") 236 | 0x0004 GPSlong 3x8 urational (0.000000 10.000000 23.980000) 237 | 0x0007 GPStime_stamp 3x8 urational (21.000000 9.000000 27.000000) 238 | 0x0008 GPSsatellites 2x1 string ("5") 239 | 0x0009 GPSstatus 2x1 string ("A") 240 | 0x000a GPSmeasure_mode 2x1 string ("2") 241 | 0x000b GPSdop 1x8 urational (2.200000) 242 | 0x0012 GPSmap_datum 10x1 string ("WGS-84 ") 243 | 0x001b Unknown 3x1 bytes (0x47 0x50 0x53) 244 | 0x001d Unknown 11x1 string ("2011:03:10") 245 | 246 | tiff_ifd.cpp:77 Listing 14 tags 247 | 0x010f Make 10x1 string ("Panasonic") 248 | 0x0110 Model 8x1 string ("DMC-ZS7") 249 | 0x0112 Orientation 1x2 uint16 (1) 250 | 0x011a XResolution 1x8 urational (180.000000) 251 | 0x011b YResolution 1x8 urational (180.000000) 252 | 0x0128 ResolutionUnit 1x2 uint16 (2) 253 | 0x0131 Software 10x1 string ("Ver.1.0 ") 254 | 0x0132 DateChange 20x1 string ("2011:03:12 14:45:33") 255 | 0x0213 YCbCrPositioning 1x2 uint16 (2) 256 | 0x8769 ExifIFDPointer 1x4 uint32 (IFD 654) 257 | 0x8825 GpsInfoIFDPointer 1x4 uint32 (IFD 11252) 258 | 0xc4a5 Unknown 208x1 bytes (0x50 0x72 0x69 0x6e ) 259 | 0xc6d2 Unknown 64x1 bytes (0x00 0x00 0x00 0x00 ) 260 | 0xc6d3 Unknown 128x1 bytes (0x00 0x00 0x00 0x00 ) 261 | 262 | 14 tags in IFD 263 | -------------------------------------------------------------------------------- /test/testdata/simple.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asenior/Jpeg-Redaction-Library/0286e45e6f761e938b50e9924abf302b72bd122a/test/testdata/simple.jpg -------------------------------------------------------------------------------- /test/testdata/simple_output_golden.log: -------------------------------------------------------------------------------- 1 | Testing with loadall=false 2 | Got marker 0xffe0 APPn at 2 3 | Got marker 0xffdb DQT at 20 4 | sz 67 nextloc 89 5 | Got marker 0xffdb DQT at 89 6 | sz 67 nextloc 158 7 | Got marker 0xffc0 SOF0 at 158 8 | Component 1 H 2 V 2, Table 0 9 | Component 2 H 1 V 1, Table 1 10 | Component 3 H 1 V 1, Table 1 11 | JPEG Image is 648 x 486 12 | Got marker 0xffc4 DHT at 177 13 | sz 31 nextloc 210 14 | Got marker 0xffc4 DHT at 210 15 | sz 181 nextloc 393 16 | Got marker 0xffc4 DHT at 393 17 | sz 31 nextloc 426 18 | Got marker 0xffc4 DHT at 426 19 | sz 181 nextloc 609 20 | Got marker 0xffda SOS at 609 21 | SOS slice 12 22 | In scan found marker 0xffd9 23 | EOI at 65719 (len 65106) 24 | Testing with loadall=true 25 | Got marker 0xffe0 APPn at 2 26 | Generic APPn ffe0 loaded. Marker string: JFIF 27 | Got marker 0xffdb DQT at 20 28 | sz 67 nextloc 89 29 | Got marker 0xffdb DQT at 89 30 | sz 67 nextloc 158 31 | Got marker 0xffc0 SOF0 at 158 32 | Component 1 H 2 V 2, Table 0 33 | Component 2 H 1 V 1, Table 1 34 | Component 3 H 1 V 1, Table 1 35 | JPEG Image is 648 x 486 36 | Got marker 0xffc4 DHT at 177 37 | sz 31 nextloc 210 38 | Building lookup table of 9 bits. 39 | Didn't find 1 40 | DHT 0 00. Bytes=29 total = 29 length = 29 41 | Got marker 0xffc4 DHT at 210 42 | sz 181 nextloc 393 43 | Building lookup table of 10 bits. 44 | Didn't find 5 45 | DHT 0 10. Bytes=179 total = 179 length = 179 46 | Got marker 0xffc4 DHT at 393 47 | sz 31 nextloc 426 48 | Building lookup table of 10 bits. 49 | Didn't find 1 50 | DHT 0 01. Bytes=29 total = 29 length = 29 51 | Got marker 0xffc4 DHT at 426 52 | sz 181 nextloc 609 53 | Building lookup table of 10 bits. 54 | Didn't find 5 55 | DHT 0 11. Bytes=179 total = 179 length = 179 56 | Got marker 0xffda SOS at 609 57 | SOS slice 12 58 | In scan found marker 0xffd9 59 | EOI at 65719 (len 65106) 60 | Removed 934 stuff_bytes in 65104 now 64170 61 | Comp 0 0DC DHT: 0 62 | Comp 0 1AC DHT: 1 63 | Comp 1 0DC DHT: 2 64 | Comp 1 1AC DHT: 3 65 | Comp 2 0DC DHT: 2 66 | Comp 2 1AC DHT: 3 67 | Expect 1271 MCUS. 82x62 blocks h:2 v:2 68 | 69 | 70 | Decoding 64170 71 | MCU 0 dc_value -72 y_value_ -147. Doubling gain to 1 72 | MCU 5 dc_value 221 y_value_ 322. Doubling gain to 2 73 | Got to 1271 mcus. 4 bits left. 74 | Reordering Image 82, 62 = 5084 pixels. 5084 bytes. h,v 2,2 75 | Saving Image 82, 62 = 5084 pixels. 5084 bytes 76 | DecodeImage H 648 W 486 77 | Saving: 9 markers 78 | Saved marker ffe0 length 16 at 2 79 | Saved marker ffdb length 67 at 20 80 | Saved marker ffdb length 67 at 89 81 | Saved marker ffc0 length 17 at 158 82 | Saved marker ffc4 length 31 at 177 83 | Saved marker ffc4 length 181 at 210 84 | Saved marker ffc4 length 31 at 393 85 | Saved marker ffc4 length 181 at 426 86 | Inserted 934 stuff_bytes in 64170 now 65104 87 | Saved marker ffda length 64172 at 609 88 | Testing redaction 89 | Got marker 0xffe0 APPn at 2 90 | Generic APPn ffe0 loaded. Marker string: JFIF 91 | Got marker 0xffdb DQT at 20 92 | sz 67 nextloc 89 93 | Got marker 0xffdb DQT at 89 94 | sz 67 nextloc 158 95 | Got marker 0xffc0 SOF0 at 158 96 | Component 1 H 2 V 2, Table 0 97 | Component 2 H 1 V 1, Table 1 98 | Component 3 H 1 V 1, Table 1 99 | JPEG Image is 648 x 486 100 | Got marker 0xffc4 DHT at 177 101 | sz 31 nextloc 210 102 | Building lookup table of 9 bits. 103 | Didn't find 1 104 | DHT 0 00. Bytes=29 total = 29 length = 29 105 | Got marker 0xffc4 DHT at 210 106 | sz 181 nextloc 393 107 | Building lookup table of 10 bits. 108 | Didn't find 5 109 | DHT 0 10. Bytes=179 total = 179 length = 179 110 | Got marker 0xffc4 DHT at 393 111 | sz 31 nextloc 426 112 | Building lookup table of 10 bits. 113 | Didn't find 1 114 | DHT 0 01. Bytes=29 total = 29 length = 29 115 | Got marker 0xffc4 DHT at 426 116 | sz 181 nextloc 609 117 | Building lookup table of 10 bits. 118 | Didn't find 5 119 | DHT 0 11. Bytes=179 total = 179 length = 179 120 | Got marker 0xffda SOS at 609 121 | SOS slice 12 122 | In scan found marker 0xffd9 123 | EOI at 65719 (len 65106) 124 | Removed 934 stuff_bytes in 65104 now 64170 125 | Comp 0 0DC DHT: 0 126 | Comp 0 1AC DHT: 1 127 | Comp 1 0DC DHT: 2 128 | Comp 1 1AC DHT: 3 129 | Comp 2 0DC DHT: 2 130 | Comp 2 1AC DHT: 3 131 | Expect 1271 MCUS. 82x62 blocks h:2 v:2 132 | 133 | 134 | Decoding 64170 135 | MCU 0 dc_value -72 y_value_ -147. Doubling gain to 1 136 | MCU 5 dc_value 221 y_value_ 322. Doubling gain to 2 137 | Got to 1271 mcus. 4 bits left. 138 | Reordering Image 82, 62 = 5084 pixels. 5084 bytes. h,v 2,2 139 | padding 4 bits mask 0f 140 | Redacted data length 54520 bytes 436156 bits 141 | sos block now 54530 bytes 142 | DecodeImage H 648 W 486 143 | Redacting thumbnail 144 | Redaction::10 strips 145 | Strip (48,48: 143 MCUs) has 8020 bits (rep 1074), src 40095 dest 40095 diff 0 offset 0. 146 | Strip (48,64: 184 MCUs) has 7977 bits (rep 1032), src 58844 dest 51898 diff 6946 offset 6946. 147 | Strip (48,80: 225 MCUs) has 8693 bits (rep 1094), src 75715 dest 61824 diff 13891 offset 13891. 148 | Strip (48,96: 266 MCUs) has 8296 bits (rep 1021), src 95764 dest 74274 diff 21490 offset 21490. 149 | Strip (48,112: 307 MCUs) has 7224 bits (rep 984), src 115403 dest 86638 diff 28765 offset 28765. 150 | Strip (48,128: 348 MCUs) has 7275 bits (rep 954), src 132502 dest 97497 diff 35005 offset 35005. 151 | Strip (48,144: 389 MCUs) has 8228 bits (rep 1009), src 149126 dest 107800 diff 41326 offset 41326. 152 | Strip (48,160: 430 MCUs) has 11197 bits (rep 986), src 166433 dest 117888 diff 48545 offset 48545. 153 | Strip (48,176: 471 MCUs) has 9666 bits (rep 962), src 187637 dest 128881 diff 58756 offset 58756. 154 | Strip (48,192: 512 MCUs) has 10832 bits (rep 1172), src 209291 dest 141831 diff 67460 offset 67460. 155 | Saving: 9 markers 156 | Saved marker ffe0 length 16 at 2 157 | Saved marker ffdb length 67 at 20 158 | Saved marker ffdb length 67 at 89 159 | Saved marker ffc0 length 17 at 158 160 | Saved marker ffc4 length 31 at 177 161 | Saved marker ffc4 length 181 at 210 162 | Saved marker ffc4 length 31 at 393 163 | Saved marker ffc4 length 181 at 426 164 | Inserted 808 stuff_bytes in 54530 now 55338 165 | Saved marker ffda length 64172 at 609 166 | Got marker 0xffe0 APPn at 2 167 | Generic APPn ffe0 loaded. Marker string: JFIF 168 | Got marker 0xffdb DQT at 20 169 | sz 67 nextloc 89 170 | Got marker 0xffdb DQT at 89 171 | sz 67 nextloc 158 172 | Got marker 0xffc0 SOF0 at 158 173 | Component 1 H 2 V 2, Table 0 174 | Component 2 H 1 V 1, Table 1 175 | Component 3 H 1 V 1, Table 1 176 | JPEG Image is 648 x 486 177 | Got marker 0xffc4 DHT at 177 178 | sz 31 nextloc 210 179 | Building lookup table of 9 bits. 180 | Didn't find 1 181 | DHT 0 00. Bytes=29 total = 29 length = 29 182 | Got marker 0xffc4 DHT at 210 183 | sz 181 nextloc 393 184 | Building lookup table of 10 bits. 185 | Didn't find 5 186 | DHT 0 10. Bytes=179 total = 179 length = 179 187 | Got marker 0xffc4 DHT at 393 188 | sz 31 nextloc 426 189 | Building lookup table of 10 bits. 190 | Didn't find 1 191 | DHT 0 01. Bytes=29 total = 29 length = 29 192 | Got marker 0xffc4 DHT at 426 193 | sz 181 nextloc 609 194 | Building lookup table of 10 bits. 195 | Didn't find 5 196 | DHT 0 11. Bytes=179 total = 179 length = 179 197 | Got marker 0xffda SOS at 609 198 | SOS slice 12 199 | In scan found marker 0xffd9 200 | EOI at 65719 (len 65106) 201 | Removed 934 stuff_bytes in 65104 now 64170 202 | Comp 0 0DC DHT: 0 203 | Comp 0 1AC DHT: 1 204 | Comp 1 0DC DHT: 2 205 | Comp 1 1AC DHT: 3 206 | Comp 2 0DC DHT: 2 207 | Comp 2 1AC DHT: 3 208 | Expect 1271 MCUS. 82x62 blocks h:2 v:2 209 | 210 | 211 | Decoding 64170 212 | MCU 0 dc_value -72 y_value_ -147. Doubling gain to 1 213 | MCU 5 dc_value 221 y_value_ 322. Doubling gain to 2 214 | Got to 1271 mcus. 4 bits left. 215 | Reordering Image 82, 62 = 5084 pixels. 5084 bytes. h,v 2,2 216 | padding 5 bits mask 1f 217 | Redacted data length 63292 bytes 506331 bits 218 | sos block now 63302 bytes 219 | DecodeImage H 648 W 486 220 | Redacting thumbnail 221 | Redaction::1 strips 222 | Strip (48,64: 184 MCUs) has 7977 bits (rep 1032), src 58844 dest 58844 diff 0 offset 0. 223 | Before patching size 63302 bytes 506411 bits. 224 | Patching in strip 0 225 | Patch at 58924 (80) 1032->7977 226 | Shifting data (506411 bits in 63302 bytes) up by 6945 bits starting at b58924 B7365. New length 513356 227 | fb 8233 flm 7 fhm 248 so 3 228 | Ended with src_byte 7364 229 | i 8362 shift_final 5 mask hf8 l7 230 | padding 4 bits mask 0f 231 | Saving: 9 markers 232 | Saved marker ffe0 length 16 at 2 233 | Saved marker ffdb length 67 at 20 234 | Saved marker ffdb length 67 at 89 235 | Saved marker ffc0 length 17 at 158 236 | Saved marker ffc4 length 31 at 177 237 | Saved marker ffc4 length 181 at 210 238 | Saved marker ffc4 length 31 at 393 239 | Saved marker ffc4 length 181 at 426 240 | Inserted 934 stuff_bytes in 64170 now 65104 241 | Saved marker ffda length 64172 at 609 242 | Got marker 0xffe0 APPn at 2 243 | Generic APPn ffe0 loaded. Marker string: JFIF 244 | Got marker 0xffdb DQT at 20 245 | sz 67 nextloc 89 246 | Got marker 0xffdb DQT at 89 247 | sz 67 nextloc 158 248 | Got marker 0xffc0 SOF0 at 158 249 | Component 1 H 2 V 2, Table 0 250 | Component 2 H 1 V 1, Table 1 251 | Component 3 H 1 V 1, Table 1 252 | JPEG Image is 648 x 486 253 | Got marker 0xffc4 DHT at 177 254 | sz 31 nextloc 210 255 | Building lookup table of 9 bits. 256 | Didn't find 1 257 | DHT 0 00. Bytes=29 total = 29 length = 29 258 | Got marker 0xffc4 DHT at 210 259 | sz 181 nextloc 393 260 | Building lookup table of 10 bits. 261 | Didn't find 5 262 | DHT 0 10. Bytes=179 total = 179 length = 179 263 | Got marker 0xffc4 DHT at 393 264 | sz 31 nextloc 426 265 | Building lookup table of 10 bits. 266 | Didn't find 1 267 | DHT 0 01. Bytes=29 total = 29 length = 29 268 | Got marker 0xffc4 DHT at 426 269 | sz 181 nextloc 609 270 | Building lookup table of 10 bits. 271 | Didn't find 5 272 | DHT 0 11. Bytes=179 total = 179 length = 179 273 | Got marker 0xffda SOS at 609 274 | SOS slice 12 275 | In scan found marker 0xffd9 276 | EOI at 65719 (len 65106) 277 | Removed 934 stuff_bytes in 65104 now 64170 278 | Comp 0 0DC DHT: 0 279 | Comp 0 1AC DHT: 1 280 | Comp 1 0DC DHT: 2 281 | Comp 1 1AC DHT: 3 282 | Comp 2 0DC DHT: 2 283 | Comp 2 1AC DHT: 3 284 | Expect 1271 MCUS. 82x62 blocks h:2 v:2 285 | 286 | 287 | Decoding 64170 288 | MCU 0 dc_value -72 y_value_ -147. Doubling gain to 1 289 | MCU 5 dc_value 221 y_value_ 322. Doubling gain to 2 290 | Got to 1271 mcus. 4 bits left. 291 | Reordering Image 82, 62 = 5084 pixels. 5084 bytes. h,v 2,2 292 | padding 7 bits mask 7f 293 | Redacted data length 48619 bytes 388945 bits 294 | sos block now 48629 bytes 295 | DecodeImage H 648 W 486 296 | Redacting thumbnail 297 | Redaction::11 strips 298 | Strip (48,80: 238 MCUs) has 13672 bits (rep 2012), src 75715 dest 75715 diff 0 offset 0. 299 | Strip (48,96: 279 MCUs) has 13048 bits (rep 2077), src 95764 dest 84104 diff 11660 offset 11660. 300 | Strip (48,112: 320 MCUs) has 11200 bits (rep 1463), src 115403 dest 92772 diff 22631 offset 22631. 301 | Strip (48,128: 361 MCUs) has 10962 bits (rep 1415), src 132502 dest 100134 diff 32368 offset 32368. 302 | Strip (48,144: 402 MCUs) has 12017 bits (rep 1407), src 149126 dest 107211 diff 41915 offset 41915. 303 | Strip (48,160: 443 MCUs) has 15378 bits (rep 1479), src 166433 dest 113908 diff 52525 offset 52525. 304 | Strip (48,176: 484 MCUs) has 14171 bits (rep 1819), src 187637 dest 121213 diff 66424 offset 66424. 305 | Strip (48,192: 525 MCUs) has 16902 bits (rep 1732), src 209291 dest 130515 diff 78776 offset 78776. 306 | Strip (48,208: 566 MCUs) has 13576 bits (rep 1538), src 233161 dest 139215 diff 93946 offset 93946. 307 | Strip (48,224: 607 MCUs) has 13449 bits (rep 1513), src 253518 dest 147534 diff 105984 offset 105984. 308 | Strip (48,240: 648 MCUs) has 7856 bits (rep 1445), src 273553 dest 155633 diff 117920 offset 117920. 309 | Before patching size 48629 bytes 389025 bits. 310 | Patching in strip 0 311 | Patch at 75795 (80) 2012->13672 312 | Shifting data (389025 bits in 48629 bytes) up by 11660 bits starting at b75795 B9474. New length 400685 313 | fb 10931 flm 1 fhm 254 so 1 314 | Ended with src_byte 9473 315 | i 11183 shift_final 3 mask he0 l1f 316 | padding 3 bits mask 07 317 | Patching in strip 1 318 | Patch at 95844 (80) 2077->13048 319 | Shifting data (400685 bits in 50086 bytes) up by 10971 bits starting at b95844 B11980. New length 411656 320 | fb 13351 flm 1 fhm 254 so 1 321 | Ended with src_byte 11979 322 | i 13611 shift_final 4 mask hf0 lf 323 | Patching in strip 2 324 | Patch at 115483 (80) 1463->11200 325 | Shifting data (411656 bits in 51457 bytes) up by 9737 bits starting at b115483 B14435. New length 421393 326 | fb 15652 flm 15 fhm 240 so 4 327 | Ended with src_byte 14434 328 | i 15835 shift_final 3 mask he0 l1f 329 | padding 7 bits mask 7f 330 | Patching in strip 3 331 | Patch at 132582 (80) 1415->10962 332 | Shifting data (421393 bits in 52675 bytes) up by 9547 bits starting at b132582 B16572. New length 430940 333 | fb 17766 flm 127 fhm 128 so 7 334 | Ended with src_byte 16572 335 | i 17942 shift_final 8 mask hff l0 336 | padding 4 bits mask 0f 337 | Patching in strip 4 338 | Patch at 149206 (80) 1407->12017 339 | Shifting data (430940 bits in 53868 bytes) up by 10610 bits starting at b149206 B18650. New length 441550 340 | fb 19977 flm 255 fhm 0 so 8 341 | Ended with src_byte 18650 342 | i 20152 shift_final 7 mask hfe l1 343 | padding 2 bits mask 03 344 | Patching in strip 5 345 | Patch at 166513 (80) 1479->15378 346 | Shifting data (441550 bits in 55194 bytes) up by 13899 bits starting at b166513 B20814. New length 455449 347 | fb 22551 flm 15 fhm 240 so 4 348 | Ended with src_byte 20813 349 | i 22736 shift_final 3 mask he0 l1f 350 | padding 7 bits mask 7f 351 | Patching in strip 6 352 | Patch at 187717 (80) 1819->14171 353 | Shifting data (455449 bits in 56932 bytes) up by 12352 bits starting at b187717 B23464. New length 467801 354 | fb 25008 flm 7 fhm 248 so 3 355 | Ended with src_byte 23463 356 | i 25235 shift_final 8 mask hff l0 357 | padding 7 bits mask 7f 358 | Patching in strip 7 359 | Patch at 209371 (80) 1732->16902 360 | Shifting data (467801 bits in 58476 bytes) up by 15170 bits starting at b209371 B26171. New length 482971 361 | fb 28067 flm 7 fhm 248 so 3 362 | Ended with src_byte 26170 363 | i 28284 shift_final 1 mask h80 l7f 364 | padding 5 bits mask 1f 365 | Patching in strip 8 366 | Patch at 233241 (80) 1538->13576 367 | Shifting data (482971 bits in 60372 bytes) up by 12038 bits starting at b233241 B29155. New length 495009 368 | fb 30659 flm 1 fhm 254 so 1 369 | Ended with src_byte 29154 370 | i 30852 shift_final 1 mask h80 l7f 371 | padding 7 bits mask 7f 372 | Patching in strip 9 373 | Patch at 253598 (80) 1513->13449 374 | Shifting data (495009 bits in 61877 bytes) up by 11936 bits starting at b253598 B31699. New length 506945 375 | fb 33191 flm 3 fhm 252 so 2 376 | Ended with src_byte 31698 377 | i 33380 shift_final 7 mask hfe l1 378 | padding 7 bits mask 7f 379 | Patching in strip 10 380 | Patch at 273633 (80) 1445->7856 381 | Shifting data (506945 bits in 63369 bytes) up by 6411 bits starting at b273633 B34204. New length 513356 382 | fb 35005 flm 15 fhm 240 so 4 383 | Ended with src_byte 34203 384 | i 35186 shift_final 1 mask h80 l7f 385 | padding 4 bits mask 0f 386 | Saving: 9 markers 387 | Saved marker ffe0 length 16 at 2 388 | Saved marker ffdb length 67 at 20 389 | Saved marker ffdb length 67 at 89 390 | Saved marker ffc0 length 17 at 158 391 | Saved marker ffc4 length 31 at 177 392 | Saved marker ffc4 length 181 at 210 393 | Saved marker ffc4 length 31 at 393 394 | Saved marker ffc4 length 181 at 426 395 | Inserted 934 stuff_bytes in 64170 now 65104 396 | Saved marker ffda length 64172 at 609 397 | -------------------------------------------------------------------------------- /test/testdata/simple_rawgrey_golden.pgm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asenior/Jpeg-Redaction-Library/0286e45e6f761e938b50e9924abf302b72bd122a/test/testdata/simple_rawgrey_golden.pgm -------------------------------------------------------------------------------- /test/testdata/test_noexif_golden.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asenior/Jpeg-Redaction-Library/0286e45e6f761e938b50e9924abf302b72bd122a/test/testdata/test_noexif_golden.jpg -------------------------------------------------------------------------------- /test/testdata/test_noexifgps_golden.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asenior/Jpeg-Redaction-Library/0286e45e6f761e938b50e9924abf302b72bd122a/test/testdata/test_noexifgps_golden.jpg -------------------------------------------------------------------------------- /test/testdata/test_nosensitive_golden.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asenior/Jpeg-Redaction-Library/0286e45e6f761e938b50e9924abf302b72bd122a/test/testdata/test_nosensitive_golden.jpg -------------------------------------------------------------------------------- /test/testdata/testexif.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asenior/Jpeg-Redaction-Library/0286e45e6f761e938b50e9924abf302b72bd122a/test/testdata/testexif.jpg -------------------------------------------------------------------------------- /test/testdata/testexif_golden.exiftxt: -------------------------------------------------------------------------------- 1 | ExifTool Version Number : 8.24 2 | Directory : test/testdata 3 | MIME Type : image/jpeg 4 | JFIF Version : 1.01 5 | Exif Byte Order : Big-endian (Motorola, MM) 6 | Image Description : Hello 7 | X Resolution : 1 8 | Y Resolution : 1 9 | Resolution Unit : None 10 | Y Cb Cr Positioning : Centered 11 | Image Width : 648 12 | Image Height : 486 13 | Encoding Process : Baseline DCT, Huffman coding 14 | Bits Per Sample : 8 15 | Color Components : 3 16 | Y Cb Cr Sub Sampling : YCbCr4:2:0 (2 2) 17 | Image Size : 648x486 18 | -------------------------------------------------------------------------------- /test/testdata/windows.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asenior/Jpeg-Redaction-Library/0286e45e6f761e938b50e9924abf302b72bd122a/test/testdata/windows.jpg -------------------------------------------------------------------------------- /test/testdata/windows_out_redacted.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asenior/Jpeg-Redaction-Library/0286e45e6f761e938b50e9924abf302b72bd122a/test/testdata/windows_out_redacted.jpg -------------------------------------------------------------------------------- /test/testdata/windows_rawgrey_golden.pgm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asenior/Jpeg-Redaction-Library/0286e45e6f761e938b50e9924abf302b72bd122a/test/testdata/windows_rawgrey_golden.pgm -------------------------------------------------------------------------------- /test/testreadwrite.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2011 Andrew W. Senior andrew.senior[AT]gmail.com 2 | // Part of the Jpeg-Redaction-Library to read, parse, edit redact and 3 | // write JPEG/EXIF/JFIF images. 4 | // See https://github.com/asenior/Jpeg-Redaction-Library 5 | 6 | // This program is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | 16 | // You should have received a copy of the GNU General Public License 17 | // along with this program. If not, see . 18 | 19 | 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | #include "debug_flag.h" 26 | #include "jpeg.h" 27 | #include "redaction.h" 28 | #include "test_utils.h" 29 | 30 | int main(int argc, char **argv) { 31 | std::string filename("testdata/windows.jpg"); 32 | if (argc > 1) 33 | filename = argv[1]; 34 | jpeg_redaction::debug = 0; 35 | jpeg_redaction::tests::test_readwrite(filename.c_str()); 36 | } 37 | -------------------------------------------------------------------------------- /test/testreadwrite.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (C) 2011 Andrew W. Senior andrew.senior[AT]gmail.com 4 | # Part of the Jpeg-Redaction-Library to read, parse, edit redact and 5 | # write JPEG/EXIF/JFIF images. 6 | # See https://github.com/asenior/Jpeg-Redaction-Library 7 | 8 | # This program is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | 21 | # Program to test that the library preserves information stored in JPEG files 22 | # after reading and writing. 23 | 24 | binary=./testreadwrite 25 | 26 | # Location of exiftool EXIF information dumper. 27 | EXIFTOOL=exiftool 28 | 29 | if [[ ! -x ${binary} ]]; then 30 | echo "binary $binary does not exist" 31 | exit 1; 32 | fi 33 | 34 | # A test suite of images from different devices. 35 | # Currently all pass except 36 | # droid.jpg (thumbnail length is different, but thumbnail 37 | # seems to be correctly preserved.) 38 | # canon-1999 (Can't find the right DHT) 39 | for i in testdata/devices/*.jpg; do 40 | echo "testing $i" 41 | rm -f testout/testplainoutput.jpg 42 | ${binary} $i > testout/testreadwrite.log 43 | exiftool $i > testout/src.exiflog 44 | grep -v "\(Thumbnail Offset\|Exif Byte Order\|^File \|^Directory\|Current IPTC Digest\|JFIF Version\)" testout/src.exiflog | sort > testout/src.exiflogclean 45 | exiftool testout/testplainoutput.jpg > testout/out.exiflog 46 | grep -v "\(Thumbnail Offset\|Exif Byte Order\|^File \|^Directory \|Current IPTC Digest\|JFIF Version\)" testout/out.exiflog | sort | sed -e 's/16495/19359/g' > testout/out.exiflogclean 47 | 48 | # diff testout/src.exiflog testout/out.exiflog > /dev/null || winmerge testout/src.exiflog testout/out.exiflog 49 | if ! diff testout/src.exiflogclean testout/out.exiflogclean > /dev/null; then 50 | echo "sanitized testout/src.exiflog testout/out.exiflog differ for $i" 51 | exit 1; 52 | fi 53 | done 54 | exit 0 55 | --------------------------------------------------------------------------------