├── .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 |
--------------------------------------------------------------------------------