forked from zhengszh/ExifGPSEditor
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathExif.java
203 lines (198 loc) · 5.9 KB
/
Exif.java
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
import java.awt.Image;
import java.awt.image.*;
import javax.imageio.*;
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.text.DecimalFormat;
public class Exif {
//Get big endian format long value
private static int getLongValue(byte[] bFile, int i) {
int value = 0;
int mask = 0xFF;
for (int k = 0; k < 4; ++k) {
value += ((int)(bFile[i + k] & mask) << (8 * k));
}
return value;
}
// Translation degree into (d, m , s) format
private static double[] transDegree(double d) {
double degree = (double)(int)d;
d -= degree;
d *= 60;
double minute = (double)(int)d;
d -= minute;
d *= 60;
DecimalFormat df = new DecimalFormat("#.0000");
double second = Double.parseDouble(df.format(d));
double[] result = {degree, minute, second};
return result;
}
// Translate double into p / q rational format
private static int[] getFraction(double d) {
int[] fract = new int[2];
double[] temp = new double[2];
temp[0] = d;
temp[1] = 1.0;
while (Math.abs((double)(int)temp[0] - temp[0]) > 1E-5) {
temp[0] *= 10;
temp[1] *= 10;
}
fract[0] = (int)temp[0];
fract[1] = (int)temp[1];
return fract;
}
// Translate long into big endian bytes
private static byte[] getLongBytes(int i) {
byte[] result = new byte[4];
for (int k = 0; k < 4; ++k) {
result[k] = (byte)(i >>> (k * 8));
}
return result;
}
private static class GpsLocation {
public double longitude;
public char longRef;
public double latitude;
public char latiRef;
}
// Input check
private static Boolean validLocation(GpsLocation loc) {
if (loc.longitude >= 0.0 && loc.longitude <= 180.0) {
if (loc.latitude >= 0.0 && loc.latitude <= 90.0) {
if (loc.longRef == 'E' || loc.longRef == 'W') {
if (loc.latiRef == 'N' || loc.latiRef == 'S') {
return true;
}
}
}
}
return false;
}
public static void changeLocation(String filename, GpsLocation loc) {
FileInputStream fileInputStream = null;
File file = new File(filename);
byte[] bFile = new byte[(int)file.length()];
// Read as byte array
try {
fileInputStream = new FileInputStream(file);
fileInputStream.read(bFile);
fileInputStream.close();
} catch (Exception e){
e.printStackTrace();
}
Boolean isHead = false;
int ffcount = 0;
for (int i = 0; i < bFile.length; i++) {
if (isHead) {
if (bFile[i] == (byte)0xE1) {
int length = ((short)(bFile[i + 2]) << 8) + (short)bFile[i + 1];
i += 3;
String checkStr = "Exif";
for (int k = 0; k < 4; ++k) {
if ((char)bFile[i + k] != checkStr.charAt(k)) {
break;
}
}
i += 6;
int tiffHeaderStart = i;
i += 8;
int ifdEntryNum = ((short)(bFile[i]) << 8) + (short)bFile[i + 1];
i += 2;
for (int k = 0; k < ifdEntryNum; ++k) {
// Is Exif GPS data
if (bFile[i] == (byte)0x25 && bFile[i + 1] == (byte)0x88) {
int ifdType = ((short)(bFile[i + 3]) << 8) + (short)bFile[i + 2];
//Is Long type
if (ifdType == 4) {
int gpsOffset = ((int)(bFile[i + 11]) << 24) + ((int)(bFile[i + 10]) << 16) + ((int)(bFile[i + 9]) << 8) + ((int)bFile[i + 8] & 0xFF);
int gpsStart = gpsOffset + tiffHeaderStart;
i = gpsStart;
int gpsItemNum = ((short)(bFile[i + 1]) << 8) + (short)bFile[i];
i += 2;
for (int t = 0; t < gpsItemNum; ++t) {
int tagid = ((short)(bFile[i + 1]) << 8) + (short)bFile[i];
int type = ((short)(bFile[i + 3]) << 8) + (short)bFile[i + 2];
int count = ((int)(bFile[i + 7]) << 24) + ((int)(bFile[i + 6]) << 16) + ((int)(bFile[i + 5]) << 8) + ((int)bFile[i + 4] & 0xFF);
if (tagid == 1) {
if (type == 2 && count == 2) {
bFile[i + 8] = (byte)loc.latiRef;
char ref = (char)bFile[i + 8];
}
} else if (tagid == 2) {
if (type == 5 && count == 3) {
int offset = getLongValue(bFile, i + 8);
int degreeStart = tiffHeaderStart + offset;
double[] latitude = transDegree(loc.latitude);
for (int x = 0; x < 3; ++x) {
int[] ration = getFraction(latitude[x]);
for (int j = 0; j < 2; ++j) {
byte[] bytes = getLongBytes(ration[j]);
for (int y = 0; y < 4; ++y) {
bFile[degreeStart + y] = bytes[y];
}
degreeStart += 4;
}
}
}
} else if (tagid == 3) {
if (type == 2 && count == 2) {
bFile[i + 8] = (byte)loc.longRef;
char ref = (char)bFile[i + 8];
}
} else if (tagid == 4) {
if (type == 5 && count == 3) {
int offset = getLongValue(bFile, i + 8);
int degreeStart = tiffHeaderStart + offset;
double[] longitude = transDegree(loc.longitude);
for (int x = 0; x < 3; ++x) {
int[] ration = getFraction(longitude[x]);
for (int j = 0; j < 2; ++j) {
byte[] bytes = getLongBytes(ration[j]);
for (int y = 0; y < 4; ++y) {
bFile[degreeStart + y] = bytes[y];
}
degreeStart += 4;
}
}
}
}
i += 12;
}
}
break;
}
i += 12;
}
}
}
isHead = (bFile[i] == (byte)0xFF);
}
try {
FileOutputStream fos = new FileOutputStream("output.jpg");
fos.write(bFile);
fos.close();
} catch (Exception e){
e.printStackTrace();
}
}
public static void main(String[] args) {
if (args.length != 5) {
return;
}
String file = args[0];
GpsLocation loc = new GpsLocation();
loc.longitude = Double.parseDouble(args[1]);
loc.longRef = args[2].charAt(0);
loc.latitude = Double.parseDouble(args[3]);
loc.latiRef = args[4].charAt(0);
if (validLocation(loc)) {
changeLocation(file, loc);
}
}
}