-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathtiffsnip.c
405 lines (379 loc) · 13.7 KB
/
tiffsnip.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
/*
* Copyright (c) 2019 Joseph Utecht
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <sys/types.h>
#include "tiff.h"
bool DEBUG = false;
#define BUFFER_SIZE 2048
char ZEROS[BUFFER_SIZE] = {0};
struct Header {
uint16 byte_order;
uint16 magic_number;
};
struct BigHeader {
uint16 offset_bytesize;
uint16 zeros;
};
struct IFD {
uint16 tag;
uint16 tag_type;
int32 count;
uint32 value;
};
struct __attribute__((__packed__)) BIGIFD {
uint16 tag;
uint16 tag_type;
uint64_t count;
uint64_t value;
};
int IFD_ROW_SIZE = sizeof(struct IFD);
int OFFSET_SIZE = sizeof(uint32);
int IFD_COUNT_SIZE = sizeof(int16);
bool BIG_TIFF = false;
int ifd_value_size(uint16 tag_type){
switch(tag_type){
case TIFF_NOTYPE:
return 0; /* placeholder */
break;
case TIFF_BYTE:
return sizeof(uint8); /* 8-bit unsigned integer */
break;
case TIFF_ASCII:
return sizeof(uint8); /* 8-bit bytes w/ last byte null */
break;
case TIFF_SHORT:
return sizeof(uint16); /* 16-bit unsigned integer */
break;
case TIFF_LONG:
return sizeof(uint32);
break; /* 32-bit unsigned integer */
case TIFF_RATIONAL:
return sizeof(uint64_t); /* 64-bit unsigned fraction */
break;
case TIFF_SBYTE:
return sizeof(uint8); /* !8-bit signed integer */
break;
case TIFF_UNDEFINED:
return sizeof(uint8);
break; /* !8-bit untyped data */
case TIFF_SSHORT:
return sizeof(uint16); /* !16-bit signed integer */
break;
case TIFF_SLONG:
return sizeof(uint32); /* !32-bit signed integer */
break;
case TIFF_SRATIONAL:
return sizeof(uint64_t); /* !64-bit signed fraction */
break;
case TIFF_FLOAT:
return sizeof(uint32); /* !32-bit IEEE floating point */
break;
case TIFF_DOUBLE:
return sizeof(uint64_t); /* !64-bit IEEE floating point */
break;
case TIFF_IFD:
return sizeof(uint32);
break; /* %32-bit unsigned integer (offset) */
case TIFF_LONG8:
return sizeof(uint64_t); /* BigTIFF 64-bit unsigned integer */
break;
case TIFF_SLONG8:
return sizeof(uint64_t);
break; /* BigTIFF 64-bit signed integer */
case TIFF_IFD8:
return sizeof(uint64_t);
break; /* BigTIFF 64-bit unsigned integer (offset) */
}
return 0;
}
struct IFD* find_tag(struct IFD ifds[], int ifd_count, uint16 tag){
for(int i = 0; i < ifd_count; i++){
if(ifds[i].tag == tag){
return &ifds[i];
}
}
return 0;
}
struct BIGIFD* find_big_tag(struct BIGIFD ifds[], int64_t ifd_count, uint16 tag){
for(int i = 0; i < ifd_count; i++){
if(ifds[i].tag == tag){
return &ifds[i];
}
}
return 0;
}
void tiff_clear(FILE *fp, off_t start, int64_t size){
if(DEBUG) printf("Clearing %lld at 0x%llx\n", size, start);
fseeko(fp, start, SEEK_SET);
int64_t remaining_size = size;
while(remaining_size > 0){
if(remaining_size > BUFFER_SIZE){
fwrite(ZEROS, sizeof(char), BUFFER_SIZE, fp);
} else {
fwrite(ZEROS, sizeof(char), remaining_size, fp);
}
remaining_size -= BUFFER_SIZE;
}
}
void overwrite_ifd_offset(FILE *fp, off_t offset, off_t final_offset){
fseeko(fp, offset, SEEK_SET);
int64_t ifd_count = 0;
fread(&ifd_count, IFD_COUNT_SIZE, 1, fp);
fseek(fp, IFD_ROW_SIZE * ifd_count, SEEK_CUR);
fwrite(&final_offset, OFFSET_SIZE, 1, fp);
}
off_t scan_ifd(FILE *fp, off_t offset, int page_num, bool delete){
fseeko(fp, offset, SEEK_SET);
int ifd_count;
fread(&ifd_count, IFD_COUNT_SIZE, 1, fp);
if(DEBUG) printf("Image #%d\n", page_num);
if(DEBUG) printf("Found %d IFDs\n", ifd_count);
bool tiles_found = false;
bool strips_found = false;
struct IFD ifds[ifd_count];
fread(&ifds, IFD_ROW_SIZE, ifd_count, fp);
for(int i = 0; i < ifd_count; i++){
if(DEBUG) printf("TAG: %d, Type: %d, Count: %d, Value: %d\n",
ifds[i].tag,
ifds[i].tag_type,
ifds[i].count,
ifds[i].value);
if(ifds[i].tag == TIFFTAG_STRIPOFFSETS){
strips_found = true;
}
if(ifds[i].tag == TIFFTAG_TILEOFFSETS){
tiles_found = true;
}
}
off_t next_offset = 0;
fread(&next_offset, OFFSET_SIZE, 1, fp);
if(DEBUG) printf("Next Offset: 0x%llx\n", next_offset);
if(delete){
if(DEBUG) printf("Deleting IFD table\n");
int64_t ifd_size = (IFD_ROW_SIZE * ifd_count) + IFD_COUNT_SIZE + OFFSET_SIZE;
tiff_clear(fp, offset, ifd_size);
if(tiles_found || strips_found){
uint16 size_tag;
uint16 offset_tag;
if(tiles_found){
offset_tag = TIFFTAG_TILEOFFSETS;
size_tag = TIFFTAG_TILEBYTECOUNTS;
} else {
offset_tag = TIFFTAG_STRIPOFFSETS;
size_tag = TIFFTAG_STRIPBYTECOUNTS;
}
struct IFD *size_row;
struct IFD *offset_row;
size_row = find_tag(ifds, ifd_count, size_tag);
if(DEBUG) printf("Found this many tilesizes: %d\n", size_row->count);
offset_row = find_tag(ifds, ifd_count, offset_tag);
if(size_row->count != offset_row->count){
printf("Bad Tile offset/size row found, exiting.\n");
return 1;
}
//then iterate through offsets
fseek(fp, offset_row->value, SEEK_SET);
uint32 tile_addresses[offset_row->count];
fread(&tile_addresses, OFFSET_SIZE, offset_row->count, fp);
fseek(fp, size_row->value, SEEK_SET);
uint32 tile_sizes[size_row->count];
fread(&tile_sizes, OFFSET_SIZE, size_row->count, fp);
// in the special case where a single tile/strip exists
// we need to delete the offset from the value/offset field
if(offset_row->count == 1){
tiff_clear(fp, offset_row->value, size_row->value);
} else {
for(int i = 0; i < offset_row->count; i++){
tiff_clear(fp, tile_addresses[i], tile_sizes[i]);
}
}
}
// delete all off stored information
for(int i = 0; i < ifd_count; i++){
// TODO: change to something with size
if(ifd_value_size(ifds[i].tag_type) * ifds[i].count > sizeof(uint32)){
tiff_clear(fp, ifds[i].value, ifds[i].count * ifd_value_size(ifds[i].tag_type));
}
}
}
return next_offset;
}
off_t scan_big_ifd(FILE *fp, off_t offset, int page_num, bool delete){
fseeko(fp, offset, SEEK_SET);
int64_t ifd_count = 0;
fread(&ifd_count, IFD_COUNT_SIZE, 1, fp);
if(DEBUG) printf("Image #%d\n", page_num);
if(DEBUG) printf("Found %lld IFDs\n", ifd_count);
bool tiles_found = false;
bool strips_found = false;
struct BIGIFD ifds[ifd_count];
fread(&ifds, IFD_ROW_SIZE, ifd_count, fp);
for(int i = 0; i < ifd_count; i++){
if(DEBUG) printf("TAG: %d, Type: %d, Count: %lld, Value: %lld\n",
ifds[i].tag,
ifds[i].tag_type,
ifds[i].count,
ifds[i].value);
if(ifds[i].tag == TIFFTAG_STRIPOFFSETS){
strips_found = true;
}
if(ifds[i].tag == TIFFTAG_TILEOFFSETS){
tiles_found = true;
}
}
off_t next_offset = 0;
fread(&next_offset, OFFSET_SIZE, 1, fp);
if(DEBUG) printf("Next Offset: 0x%llx\n", next_offset);
if(delete){
if(DEBUG) printf("Deleting IFD table\n");
int64_t ifd_size = (IFD_ROW_SIZE * ifd_count) + IFD_COUNT_SIZE + OFFSET_SIZE;
tiff_clear(fp, offset, ifd_size);
if(tiles_found || strips_found){
uint16 size_tag;
uint16 offset_tag;
if(tiles_found){
offset_tag = TIFFTAG_TILEOFFSETS;
size_tag = TIFFTAG_TILEBYTECOUNTS;
} else {
offset_tag = TIFFTAG_STRIPOFFSETS;
size_tag = TIFFTAG_STRIPBYTECOUNTS;
}
struct BIGIFD *size_row;
struct BIGIFD *offset_row;
size_row = find_big_tag(ifds, ifd_count, size_tag);
if(DEBUG) printf("Found this many tilesizes: %lld\n", size_row->count);
offset_row = find_big_tag(ifds, ifd_count, offset_tag);
if(size_row->count != offset_row->count){
printf("Bad Tile offset/size row found, exiting.\n");
return 1;
}
//then iterate through offsets
fseek(fp, offset_row->value, SEEK_SET);
uint64_t tile_addresses[offset_row->count];
fread(&tile_addresses, OFFSET_SIZE, offset_row->count, fp);
fseek(fp, size_row->value, SEEK_SET);
uint64_t tile_sizes[size_row->count];
fread(&tile_sizes, OFFSET_SIZE, size_row->count, fp);
// in the special case where a single tile/strip exists
// we need to delete the offset from the value/offset field
if(offset_row->count == 1){
tiff_clear(fp, offset_row->value, size_row->value);
} else {
for(int i = 0; i < offset_row->count; i++){
tiff_clear(fp, tile_addresses[i], tile_sizes[i]);
}
}
}
// delete all off stored information
for(int i = 0; i < ifd_count; i++){
// TODO: change to something with size
if(ifd_value_size(ifds[i].tag_type) * ifds[i].count > sizeof(uint64_t)){
tiff_clear(fp, ifds[i].value, ifds[i].count * ifd_value_size(ifds[i].tag_type));
}
}
}
return next_offset;
}
int main(int argc, char *argv[]) {
bool help = false;
help = argc != 3;
for(int i = 0; i < argc; i++){
if(strcmp(argv[i], "--help") == 0){
help = true;
}
}
if(help){
printf("tiffsnip, version 1.0\nA utility for zeroing pages from tiff files\n\nUsage: tiffsnip file page_index\n\tfile: the tiff file to be snipped\n\tpage_index: the page number to be snipped (1 indexed)\n");
return 0;
}
FILE *fp;
fp = fopen(argv[1], "r+b");
if(fp == NULL){
printf("Opening file failed\n");
return 1;
}
struct Header header;
fread(&header, sizeof(struct Header), 1, fp);
off_t first_offset = 0;
if (header.byte_order != TIFF_LITTLEENDIAN){
printf("Non-little endian byte order found, exiting.");
return 1;
}
if (header.magic_number == TIFF_VERSION_BIG){
struct BigHeader big_header;
fread(&big_header, sizeof(struct BigHeader), 1, fp);
IFD_ROW_SIZE = 20;
OFFSET_SIZE = sizeof(uint64_t);
IFD_COUNT_SIZE = sizeof(uint64_t);
BIG_TIFF = true;
}
fread(&first_offset, OFFSET_SIZE, 1, fp);
if(DEBUG) printf("BO: %x\nMN: %d\nOffset: 0x%llx\n", header.byte_order,
header.magic_number,
first_offset);
if(DEBUG) printf("Offsetsize %d\n", OFFSET_SIZE);
off_t next_offset = first_offset;
off_t last_offset = 0;
int page_count = 0;
int to_delete = -1;
if(argc == 3){
to_delete = atoi(argv[2]);
}
while (next_offset > 0) {
page_count += 1;
last_offset = next_offset;
if(BIG_TIFF){
next_offset = scan_big_ifd(fp, next_offset, page_count, page_count == to_delete);
} else {
next_offset = scan_ifd(fp, next_offset, page_count, page_count == to_delete);
}
if(page_count == to_delete){
if(DEBUG) printf("Updating last offset to next offset\n");
if(to_delete == 1){
// need to update offset from first header
if(DEBUG) printf("Overwriting Header Offset: 0x%llx\n", next_offset);
fseek(fp, sizeof(struct Header), SEEK_SET);
if(BIG_TIFF){
fseek(fp, sizeof(struct BigHeader), SEEK_CUR);
}
fwrite(&next_offset, OFFSET_SIZE, 1, fp);
fclose(fp);
return 0;
} else {
// need to scan to correct offset
off_t final_offset = next_offset;
next_offset = first_offset;
page_count = 1;
while (page_count < to_delete - 1){
if(BIG_TIFF){
next_offset = scan_big_ifd(fp, next_offset, page_count, false);
} else {
next_offset = scan_ifd(fp, next_offset, page_count, false);
}
page_count += 1;
}
if(DEBUG) printf("Overwriting IFD Offset: 0x%llx -> 0x%llx\n", next_offset, final_offset);
overwrite_ifd_offset(fp, next_offset, final_offset);
fclose(fp);
return 0;
}
return 0;
}
}
return 0;
}