2019-10-06 13:23:33 +02:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2012 The Android Open Source Project
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package it.sephiroth.android.library.exif2
|
|
|
|
|
|
|
|
import android.util.Log
|
2019-10-07 20:46:19 +02:00
|
|
|
import it.sephiroth.android.library.exif2.utils.OrderedDataOutputStream
|
2019-10-06 13:23:33 +02:00
|
|
|
import java.io.BufferedOutputStream
|
|
|
|
import java.io.IOException
|
|
|
|
import java.io.OutputStream
|
|
|
|
import java.nio.ByteBuffer
|
|
|
|
import java.nio.ByteOrder
|
2019-10-07 20:46:19 +02:00
|
|
|
import java.util.*
|
2019-10-06 13:23:33 +02:00
|
|
|
|
|
|
|
@Suppress("unused")
|
2019-10-07 20:46:19 +02:00
|
|
|
internal class ExifOutputStream(
|
|
|
|
|
|
|
|
private val mInterface : ExifInterface,
|
|
|
|
|
|
|
|
// the Exif header to be written into the JPEG file.
|
|
|
|
private val exifData : ExifData
|
|
|
|
) {
|
|
|
|
|
2019-10-06 13:23:33 +02:00
|
|
|
private val mBuffer = ByteBuffer.allocate(4)
|
|
|
|
|
|
|
|
private fun requestByteToBuffer(
|
|
|
|
requestByteCount : Int, buffer : ByteArray, offset : Int, length : Int
|
|
|
|
) : Int {
|
|
|
|
val byteNeeded = requestByteCount - mBuffer.position()
|
|
|
|
val byteToRead = if(length > byteNeeded) byteNeeded else length
|
|
|
|
mBuffer.put(buffer, offset, byteToRead)
|
|
|
|
return byteToRead
|
|
|
|
}
|
|
|
|
|
|
|
|
@Throws(IOException::class)
|
|
|
|
fun writeExifData(out : OutputStream) {
|
|
|
|
Log.v(TAG, "Writing exif data...")
|
|
|
|
|
2019-10-07 20:46:19 +02:00
|
|
|
val nullTags = stripNullValueTags(exifData)
|
2019-10-06 13:23:33 +02:00
|
|
|
createRequiredIfdAndTag()
|
|
|
|
val exifSize = calculateAllOffset()
|
|
|
|
// Log.i(TAG, "exifSize: " + (exifSize + 8));
|
|
|
|
if(exifSize + 8 > MAX_EXIF_SIZE) {
|
|
|
|
throw IOException("Exif header is too large (>64Kb)")
|
|
|
|
}
|
|
|
|
|
|
|
|
val outputStream = BufferedOutputStream(out, STREAMBUFFER_SIZE)
|
2019-10-07 20:46:19 +02:00
|
|
|
val dataOutputStream =
|
|
|
|
OrderedDataOutputStream(outputStream)
|
2019-10-06 13:23:33 +02:00
|
|
|
|
|
|
|
dataOutputStream.setByteOrder(ByteOrder.BIG_ENDIAN)
|
|
|
|
|
|
|
|
dataOutputStream.write(0xFF)
|
|
|
|
dataOutputStream.write(JpegHeader.TAG_M_EXIF)
|
|
|
|
dataOutputStream.writeShort((exifSize + 8).toShort())
|
|
|
|
dataOutputStream.writeInt(EXIF_HEADER)
|
|
|
|
dataOutputStream.writeShort(0x0000.toShort())
|
2019-10-07 20:46:19 +02:00
|
|
|
if(exifData.byteOrder == ByteOrder.BIG_ENDIAN) {
|
2019-10-06 13:23:33 +02:00
|
|
|
dataOutputStream.writeShort(TIFF_BIG_ENDIAN)
|
|
|
|
} else {
|
|
|
|
dataOutputStream.writeShort(TIFF_LITTLE_ENDIAN)
|
|
|
|
}
|
2019-10-07 20:46:19 +02:00
|
|
|
dataOutputStream.setByteOrder(exifData.byteOrder)
|
2019-10-06 13:23:33 +02:00
|
|
|
dataOutputStream.writeShort(TIFF_HEADER)
|
|
|
|
dataOutputStream.writeInt(8)
|
|
|
|
writeAllTags(dataOutputStream)
|
|
|
|
|
|
|
|
writeThumbnail(dataOutputStream)
|
|
|
|
|
|
|
|
for(t in nullTags) {
|
2019-10-07 20:46:19 +02:00
|
|
|
exifData.addTag(t)
|
2019-10-06 13:23:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
dataOutputStream.flush()
|
|
|
|
}
|
|
|
|
|
2019-10-07 20:46:19 +02:00
|
|
|
// strip tags that has null value
|
|
|
|
// return list of removed tags
|
|
|
|
private fun stripNullValueTags(data : ExifData) =
|
|
|
|
ArrayList<ExifTag>()
|
|
|
|
.apply {
|
|
|
|
for(t in data.allTags) {
|
|
|
|
if(t.getValue() == null && ! ExifInterface.isOffsetTag(t.tagId)) {
|
|
|
|
data.removeTag(t.tagId, t.ifd)
|
|
|
|
add(t)
|
|
|
|
}
|
|
|
|
}
|
2019-10-06 13:23:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Throws(IOException::class)
|
|
|
|
private fun writeThumbnail(dataOutputStream : OrderedDataOutputStream) {
|
2019-10-07 20:46:19 +02:00
|
|
|
val compressedThumbnail = exifData.compressedThumbnail
|
|
|
|
if(compressedThumbnail != null) {
|
2019-10-06 13:23:33 +02:00
|
|
|
Log.d(TAG, "writing thumbnail..")
|
2019-10-07 20:46:19 +02:00
|
|
|
dataOutputStream.write(compressedThumbnail)
|
|
|
|
} else {
|
|
|
|
val stripList = exifData.stripList
|
|
|
|
if(stripList != null) {
|
|
|
|
Log.d(TAG, "writing uncompressed strip..")
|
|
|
|
stripList.forEach {
|
|
|
|
dataOutputStream.write(it)
|
|
|
|
}
|
2019-10-06 13:23:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Throws(IOException::class)
|
|
|
|
private fun writeAllTags(dataOutputStream : OrderedDataOutputStream) {
|
2019-10-07 20:46:19 +02:00
|
|
|
writeIfd(exifData.getIfdData(IfdData.TYPE_IFD_0), dataOutputStream)
|
|
|
|
writeIfd(exifData.getIfdData(IfdData.TYPE_IFD_EXIF), dataOutputStream)
|
|
|
|
writeIfd(exifData.getIfdData(IfdData.TYPE_IFD_INTEROPERABILITY), dataOutputStream)
|
|
|
|
writeIfd(exifData.getIfdData(IfdData.TYPE_IFD_GPS), dataOutputStream)
|
|
|
|
writeIfd(exifData.getIfdData(IfdData.TYPE_IFD_1), dataOutputStream)
|
2019-10-06 13:23:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Throws(IOException::class)
|
2019-10-07 20:46:19 +02:00
|
|
|
private fun writeIfd(ifd : IfdData?, dataOutputStream : OrderedDataOutputStream) {
|
|
|
|
ifd ?: return
|
|
|
|
val tags = ifd.allTagsCollection
|
2019-10-06 13:23:33 +02:00
|
|
|
dataOutputStream.writeShort(tags.size.toShort())
|
|
|
|
for(tag in tags) {
|
|
|
|
dataOutputStream.writeShort(tag.tagId)
|
|
|
|
dataOutputStream.writeShort(tag.dataType)
|
|
|
|
dataOutputStream.writeInt(tag.componentCount)
|
|
|
|
// Log.v( TAG, "\n" + tag.toString() );
|
|
|
|
if(tag.dataSize > 4) {
|
|
|
|
dataOutputStream.writeInt(tag.offset)
|
|
|
|
} else {
|
|
|
|
writeTagValue(tag, dataOutputStream)
|
|
|
|
var i = 0
|
|
|
|
val n = 4 - tag.dataSize
|
|
|
|
while(i < n) {
|
|
|
|
dataOutputStream.write(0)
|
|
|
|
i ++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dataOutputStream.writeInt(ifd.offsetToNextIfd)
|
|
|
|
for(tag in tags) {
|
|
|
|
if(tag.dataSize > 4) {
|
|
|
|
writeTagValue(tag, dataOutputStream)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun calculateOffsetOfIfd(ifd : IfdData, offsetArg : Int) : Int {
|
|
|
|
var offset = offsetArg
|
|
|
|
offset += 2 + ifd.tagCount * TAG_SIZE + 4
|
2019-10-07 20:46:19 +02:00
|
|
|
|
|
|
|
for(tag in ifd.allTagsCollection) {
|
2019-10-06 13:23:33 +02:00
|
|
|
if(tag.dataSize > 4) {
|
|
|
|
tag.offset = offset
|
|
|
|
offset += tag.dataSize
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return offset
|
|
|
|
}
|
|
|
|
|
|
|
|
@Throws(IOException::class)
|
|
|
|
private fun createRequiredIfdAndTag() {
|
2019-10-07 20:46:19 +02:00
|
|
|
|
2019-10-06 13:23:33 +02:00
|
|
|
// IFD0 is required for all file
|
2019-10-07 20:46:19 +02:00
|
|
|
var ifd0 = exifData.getIfdData(IfdData.TYPE_IFD_0)
|
2019-10-06 13:23:33 +02:00
|
|
|
if(ifd0 == null) {
|
2019-10-07 20:46:19 +02:00
|
|
|
ifd0 = IfdData(IfdData.TYPE_IFD_0)
|
|
|
|
exifData.addIfdData(ifd0)
|
2019-10-06 13:23:33 +02:00
|
|
|
}
|
2019-10-07 20:46:19 +02:00
|
|
|
|
2019-10-06 13:23:33 +02:00
|
|
|
val exifOffsetTag = mInterface.buildUninitializedTag(ExifInterface.TAG_EXIF_IFD)
|
|
|
|
?: throw IOException("No definition for crucial exif tag: " + ExifInterface.TAG_EXIF_IFD)
|
|
|
|
ifd0.setTag(exifOffsetTag)
|
|
|
|
|
|
|
|
// Exif IFD is required for all files.
|
2019-10-07 20:46:19 +02:00
|
|
|
var exifIfd = exifData.getIfdData(IfdData.TYPE_IFD_EXIF)
|
2019-10-06 13:23:33 +02:00
|
|
|
if(exifIfd == null) {
|
2019-10-07 20:46:19 +02:00
|
|
|
exifIfd = IfdData(IfdData.TYPE_IFD_EXIF)
|
|
|
|
exifData.addIfdData(exifIfd)
|
2019-10-06 13:23:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// GPS IFD
|
2019-10-07 20:46:19 +02:00
|
|
|
val gpsIfd = exifData.getIfdData(IfdData.TYPE_IFD_GPS)
|
2019-10-06 13:23:33 +02:00
|
|
|
if(gpsIfd != null) {
|
|
|
|
val gpsOffsetTag = mInterface.buildUninitializedTag(ExifInterface.TAG_GPS_IFD)
|
2019-10-07 20:46:19 +02:00
|
|
|
?: throw IOException("No definition for crucial exif tag: ${ExifInterface.TAG_GPS_IFD}")
|
2019-10-06 13:23:33 +02:00
|
|
|
ifd0.setTag(gpsOffsetTag)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Interoperability IFD
|
2019-10-07 20:46:19 +02:00
|
|
|
val interIfd = exifData.getIfdData(IfdData.TYPE_IFD_INTEROPERABILITY)
|
2019-10-06 13:23:33 +02:00
|
|
|
if(interIfd != null) {
|
|
|
|
val interOffsetTag =
|
|
|
|
mInterface.buildUninitializedTag(ExifInterface.TAG_INTEROPERABILITY_IFD)
|
2019-10-07 20:46:19 +02:00
|
|
|
?: throw IOException("No definition for crucial exif tag: ${ExifInterface.TAG_INTEROPERABILITY_IFD}")
|
2019-10-06 13:23:33 +02:00
|
|
|
exifIfd.setTag(interOffsetTag)
|
|
|
|
}
|
|
|
|
|
2019-10-07 20:46:19 +02:00
|
|
|
var ifd1 = exifData.getIfdData(IfdData.TYPE_IFD_1)
|
2019-10-06 13:23:33 +02:00
|
|
|
|
|
|
|
// thumbnail
|
2019-10-07 20:46:19 +02:00
|
|
|
val compressedThumbnail = exifData.compressedThumbnail
|
|
|
|
val stripList = exifData.stripList
|
2019-10-06 13:23:33 +02:00
|
|
|
when {
|
2019-10-07 20:46:19 +02:00
|
|
|
|
|
|
|
compressedThumbnail != null -> {
|
2019-10-06 13:23:33 +02:00
|
|
|
if(ifd1 == null) {
|
2019-10-07 20:46:19 +02:00
|
|
|
ifd1 = IfdData(IfdData.TYPE_IFD_1)
|
|
|
|
exifData.addIfdData(ifd1)
|
2019-10-06 13:23:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
val offsetTag =
|
|
|
|
mInterface.buildUninitializedTag(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT)
|
|
|
|
?: throw IOException("No definition for crucial exif tag: ${ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT}")
|
|
|
|
|
|
|
|
ifd1.setTag(offsetTag)
|
|
|
|
val lengthTag =
|
|
|
|
mInterface.buildUninitializedTag(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH)
|
|
|
|
?: throw IOException("No definition for crucial exif tag: ${ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH}")
|
|
|
|
|
2019-10-07 20:46:19 +02:00
|
|
|
lengthTag.setValue(compressedThumbnail.size)
|
2019-10-06 13:23:33 +02:00
|
|
|
ifd1.setTag(lengthTag)
|
|
|
|
|
|
|
|
// Get rid of tags for uncompressed if they exist.
|
|
|
|
ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS))
|
|
|
|
ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_BYTE_COUNTS))
|
|
|
|
}
|
2019-10-07 20:46:19 +02:00
|
|
|
|
|
|
|
stripList != null -> {
|
2019-10-06 13:23:33 +02:00
|
|
|
if(ifd1 == null) {
|
2019-10-07 20:46:19 +02:00
|
|
|
ifd1 = IfdData(IfdData.TYPE_IFD_1)
|
|
|
|
exifData.addIfdData(ifd1)
|
2019-10-06 13:23:33 +02:00
|
|
|
}
|
|
|
|
val offsetTag = mInterface.buildUninitializedTag(ExifInterface.TAG_STRIP_OFFSETS)
|
|
|
|
?: throw IOException("No definition for crucial exif tag: ${ExifInterface.TAG_STRIP_OFFSETS}")
|
2019-10-07 20:46:19 +02:00
|
|
|
val lengthTag =
|
|
|
|
mInterface.buildUninitializedTag(ExifInterface.TAG_STRIP_BYTE_COUNTS)
|
|
|
|
?: throw IOException("No definition for crucial exif tag: ${ExifInterface.TAG_STRIP_BYTE_COUNTS}")
|
|
|
|
|
|
|
|
val bytesList = LongArray(stripList.size)
|
|
|
|
stripList.forEachIndexed { index, bytes -> bytesList[index] = bytes.size.toLong() }
|
|
|
|
lengthTag.setValue(bytesList)
|
2019-10-06 13:23:33 +02:00
|
|
|
ifd1.setTag(offsetTag)
|
|
|
|
ifd1.setTag(lengthTag)
|
|
|
|
// Get rid of tags for compressed if they exist.
|
|
|
|
ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT))
|
|
|
|
ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH))
|
|
|
|
}
|
2019-10-07 20:46:19 +02:00
|
|
|
|
2019-10-06 13:23:33 +02:00
|
|
|
ifd1 != null -> {
|
|
|
|
// Get rid of offset and length tags if there is no thumbnail.
|
|
|
|
ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS))
|
|
|
|
ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_BYTE_COUNTS))
|
|
|
|
ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT))
|
|
|
|
ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun calculateAllOffset() : Int {
|
|
|
|
var offset = TIFF_HEADER_SIZE.toInt()
|
|
|
|
|
2019-10-07 20:46:19 +02:00
|
|
|
val ifd0 = exifData.getIfdData(IfdData.TYPE_IFD_0)?.also {
|
|
|
|
offset = calculateOffsetOfIfd(it, offset)
|
|
|
|
}
|
2019-10-06 13:23:33 +02:00
|
|
|
|
2019-10-07 20:46:19 +02:00
|
|
|
val exifIfd = exifData.getIfdData(IfdData.TYPE_IFD_EXIF)?.also { it ->
|
|
|
|
ifd0?.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_EXIF_IFD))
|
2019-10-06 13:23:33 +02:00
|
|
|
?.setValue(offset)
|
2019-10-07 20:46:19 +02:00
|
|
|
offset = calculateOffsetOfIfd(it, offset)
|
2019-10-06 13:23:33 +02:00
|
|
|
}
|
|
|
|
|
2019-10-07 20:46:19 +02:00
|
|
|
exifData.getIfdData(IfdData.TYPE_IFD_INTEROPERABILITY)?.also { it ->
|
|
|
|
exifIfd?.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_INTEROPERABILITY_IFD))
|
2019-10-06 13:23:33 +02:00
|
|
|
?.setValue(offset)
|
2019-10-07 20:46:19 +02:00
|
|
|
offset = calculateOffsetOfIfd(it, offset)
|
2019-10-06 13:23:33 +02:00
|
|
|
}
|
|
|
|
|
2019-10-07 20:46:19 +02:00
|
|
|
exifData.getIfdData(IfdData.TYPE_IFD_GPS)?.also {
|
|
|
|
ifd0?.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_GPS_IFD))
|
|
|
|
?.setValue(offset)
|
|
|
|
offset = calculateOffsetOfIfd(it, offset)
|
2019-10-06 13:23:33 +02:00
|
|
|
}
|
|
|
|
|
2019-10-07 20:46:19 +02:00
|
|
|
val ifd1 = exifData.getIfdData(IfdData.TYPE_IFD_1)?.also {
|
|
|
|
ifd0?.offsetToNextIfd = offset
|
|
|
|
offset = calculateOffsetOfIfd(it, offset)
|
|
|
|
}
|
|
|
|
|
|
|
|
val compressedThumbnail = exifData.compressedThumbnail
|
|
|
|
if(compressedThumbnail != null) {
|
|
|
|
ifd1
|
|
|
|
?.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT))
|
2019-10-06 13:23:33 +02:00
|
|
|
?.setValue(offset)
|
2019-10-07 20:46:19 +02:00
|
|
|
offset += compressedThumbnail.size
|
|
|
|
} else {
|
|
|
|
// uncompressed thumbnail
|
|
|
|
val stripList = exifData.stripList
|
|
|
|
if(stripList != null) {
|
|
|
|
val offsets = LongArray(stripList.size)
|
|
|
|
stripList.forEachIndexed { index, bytes ->
|
|
|
|
offsets[index] = offset.toLong()
|
|
|
|
offset += bytes.size
|
|
|
|
}
|
|
|
|
ifd1
|
|
|
|
?.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS))
|
|
|
|
?.setValue(offsets)
|
2019-10-06 13:23:33 +02:00
|
|
|
}
|
|
|
|
}
|
2019-10-07 20:46:19 +02:00
|
|
|
|
2019-10-06 13:23:33 +02:00
|
|
|
return offset
|
|
|
|
}
|
|
|
|
|
|
|
|
companion object {
|
|
|
|
private const val TAG = "ExifOutputStream"
|
|
|
|
private const val STREAMBUFFER_SIZE = 0x00010000 // 64Kb
|
|
|
|
|
|
|
|
private const val STATE_SOI = 0
|
|
|
|
private const val EXIF_HEADER = 0x45786966
|
|
|
|
private const val TIFF_HEADER : Short = 0x002A
|
|
|
|
private const val TIFF_BIG_ENDIAN : Short = 0x4d4d
|
|
|
|
private const val TIFF_LITTLE_ENDIAN : Short = 0x4949
|
|
|
|
private const val TAG_SIZE : Short = 12
|
|
|
|
private const val TIFF_HEADER_SIZE : Short = 8
|
|
|
|
private const val MAX_EXIF_SIZE = 65535
|
|
|
|
|
|
|
|
@Throws(IOException::class)
|
|
|
|
fun writeTagValue(tag : ExifTag, dataOutputStream : OrderedDataOutputStream) {
|
|
|
|
when(tag.dataType) {
|
|
|
|
ExifTag.TYPE_ASCII -> {
|
|
|
|
val buf = tag.stringByte !!
|
|
|
|
if(buf.size == tag.componentCount) {
|
|
|
|
buf[buf.size - 1] = 0
|
|
|
|
dataOutputStream.write(buf)
|
|
|
|
} else {
|
|
|
|
dataOutputStream.write(buf)
|
|
|
|
dataOutputStream.write(0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ExifTag.TYPE_LONG, ExifTag.TYPE_UNSIGNED_LONG -> run {
|
|
|
|
for(i in 0 until tag.componentCount) {
|
|
|
|
dataOutputStream.writeInt(tag.getValueAt(i).toInt())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ExifTag.TYPE_RATIONAL, ExifTag.TYPE_UNSIGNED_RATIONAL -> run {
|
|
|
|
for(i in 0 until tag.componentCount) {
|
|
|
|
dataOutputStream.writeRational(tag.getRational(i) !!)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ExifTag.TYPE_UNDEFINED, ExifTag.TYPE_UNSIGNED_BYTE -> {
|
|
|
|
val buf = ByteArray(tag.componentCount)
|
|
|
|
tag.getBytes(buf)
|
|
|
|
dataOutputStream.write(buf)
|
|
|
|
}
|
|
|
|
|
|
|
|
ExifTag.TYPE_UNSIGNED_SHORT -> {
|
|
|
|
for(i in 0 until tag.componentCount) {
|
|
|
|
dataOutputStream.writeShort(tag.getValueAt(i).toShort())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|