1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.imaging.formats.tiff.taginfos;
18
19 import java.io.UnsupportedEncodingException;
20 import java.nio.ByteOrder;
21 import java.nio.charset.StandardCharsets;
22
23 import org.apache.commons.imaging.ImagingException;
24 import org.apache.commons.imaging.common.Allocator;
25 import org.apache.commons.imaging.common.BinaryFunctions;
26 import org.apache.commons.imaging.formats.tiff.TiffField;
27 import org.apache.commons.imaging.formats.tiff.constants.TiffDirectoryType;
28 import org.apache.commons.imaging.formats.tiff.fieldtypes.AbstractFieldType;
29 import org.apache.commons.imaging.internal.Debug;
30
31
32
33
34
35 public final class TagInfoGpsText extends TagInfo {
36
37 private static final class TextEncoding {
38 final byte[] prefix;
39 public final String encodingName;
40
41 TextEncoding(final byte[] prefix, final String encodingName) {
42 this.prefix = prefix;
43 this.encodingName = encodingName;
44 }
45 }
46
47 private static final TagInfoGpsText.TextEncoding TEXT_ENCODING_ASCII = new TextEncoding(new byte[] { 0x41, 0x53, 0x43, 0x49, 0x49, 0x00, 0x00, 0x00, },
48 StandardCharsets.US_ASCII.name());
49 private static final TagInfoGpsText.TextEncoding TEXT_ENCODING_JIS = new TextEncoding(new byte[] { 0x4A, 0x49, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, },
50 "JIS");
51 private static final TagInfoGpsText.TextEncoding TEXT_ENCODING_UNICODE_LE = new TextEncoding(new byte[] { 0x55, 0x4E, 0x49, 0x43, 0x4F, 0x44, 0x45, 0x00 },
52 StandardCharsets.UTF_16LE.name());
53 private static final TagInfoGpsText.TextEncoding TEXT_ENCODING_UNICODE_BE = new TextEncoding(new byte[] { 0x55, 0x4E, 0x49, 0x43, 0x4F, 0x44, 0x45, 0x00 },
54 StandardCharsets.UTF_16BE.name());
55 private static final TagInfoGpsText.TextEncoding TEXT_ENCODING_UNDEFINED = new TextEncoding(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
56
57 StandardCharsets.ISO_8859_1.name());
58
59 private static final TagInfoGpsText.TextEncoding[] TEXT_ENCODINGS = { TEXT_ENCODING_ASCII,
60 TEXT_ENCODING_JIS,
61 TEXT_ENCODING_UNICODE_LE,
62 TEXT_ENCODING_UNICODE_BE,
63 TEXT_ENCODING_UNDEFINED,
64 };
65
66 public TagInfoGpsText(final String name, final int tag, final TiffDirectoryType exifDirectory) {
67 super(name, tag, AbstractFieldType.UNDEFINED, LENGTH_UNKNOWN, exifDirectory);
68 }
69
70 @Override
71 public byte[] encodeValue(final AbstractFieldType abstractFieldType, final Object value, final ByteOrder byteOrder) throws ImagingException {
72 if (!(value instanceof String)) {
73 throw new ImagingException("GPS text value not String", value);
74 }
75 final String s = (String) value;
76
77 try {
78
79 final byte[] asciiBytes = s.getBytes(TEXT_ENCODING_ASCII.encodingName);
80 final String decodedAscii = new String(asciiBytes, TEXT_ENCODING_ASCII.encodingName);
81 if (decodedAscii.equals(s)) {
82
83 final byte[] result = Allocator.byteArray(asciiBytes.length + TEXT_ENCODING_ASCII.prefix.length);
84 System.arraycopy(TEXT_ENCODING_ASCII.prefix, 0, result, 0, TEXT_ENCODING_ASCII.prefix.length);
85 System.arraycopy(asciiBytes, 0, result, TEXT_ENCODING_ASCII.prefix.length, asciiBytes.length);
86 return result;
87 }
88
89 final TextEncoding encoding;
90 if (byteOrder == ByteOrder.BIG_ENDIAN) {
91 encoding = TEXT_ENCODING_UNICODE_BE;
92 } else {
93 encoding = TEXT_ENCODING_UNICODE_LE;
94 }
95 final byte[] unicodeBytes = s.getBytes(encoding.encodingName);
96 final byte[] result = Allocator.byteArray(unicodeBytes.length + encoding.prefix.length);
97 System.arraycopy(encoding.prefix, 0, result, 0, encoding.prefix.length);
98 System.arraycopy(unicodeBytes, 0, result, encoding.prefix.length, unicodeBytes.length);
99 return result;
100 } catch (final UnsupportedEncodingException e) {
101 throw new ImagingException(e.getMessage(), e);
102 }
103 }
104
105 @Override
106 public String getValue(final TiffField entry) throws ImagingException {
107 if (entry.getFieldType() == AbstractFieldType.ASCII) {
108 final Object object = AbstractFieldType.ASCII.getValue(entry);
109 if (object instanceof String) {
110 return (String) object;
111 }
112 if (object instanceof String[]) {
113
114
115
116
117
118 return ((String[]) object)[0];
119 }
120 throw new ImagingException("Unexpected ASCII type decoded");
121 }
122 if (entry.getFieldType() != AbstractFieldType.UNDEFINED && entry.getFieldType() != AbstractFieldType.BYTE) {
123 Debug.debug("entry.type: " + entry.getFieldType());
124 Debug.debug("entry.directoryType: " + entry.getDirectoryType());
125 Debug.debug("entry.type: " + entry.getDescriptionWithoutValue());
126 Debug.debug("entry.type: " + entry.getFieldType());
127 throw new ImagingException("GPS text field not encoded as bytes.");
128 }
129
130 final byte[] bytes = entry.getByteArrayValue();
131 if (bytes.length < 8) {
132
133 return new String(bytes, StandardCharsets.US_ASCII);
134 }
135
136 for (final TextEncoding encoding : TEXT_ENCODINGS) {
137 if (BinaryFunctions.compareBytes(bytes, 0, encoding.prefix, 0, encoding.prefix.length)) {
138 try {
139 final String decodedString = new String(bytes, encoding.prefix.length, bytes.length - encoding.prefix.length, encoding.encodingName);
140 final byte[] reEncodedBytes = decodedString.getBytes(encoding.encodingName);
141 if (BinaryFunctions.compareBytes(bytes, encoding.prefix.length, reEncodedBytes, 0, reEncodedBytes.length)) {
142 return decodedString;
143 }
144 } catch (final UnsupportedEncodingException e) {
145 throw new ImagingException(e.getMessage(), e);
146 }
147 }
148 }
149
150
151 return new String(bytes, StandardCharsets.US_ASCII);
152 }
153
154 @Override
155 public boolean isText() {
156 return true;
157 }
158 }