refactor
This commit is contained in:
parent
c4b0c04126
commit
0c3a222fc7
|
@ -51,6 +51,8 @@
|
|||
<w>idempotency</w>
|
||||
<w>ihdr</w>
|
||||
<w>infos</w>
|
||||
<w>iptc</w>
|
||||
<w>jfif</w>
|
||||
<w>kapt</w>
|
||||
<w>kddi</w>
|
||||
<w>kenglxn</w>
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
///*
|
||||
// * 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 java.io.InputStream
|
||||
//import java.nio.ByteBuffer
|
||||
//import kotlin.math.min
|
||||
//
|
||||
//internal class ByteBufferInputStream(private val mBuf : ByteBuffer) : InputStream() {
|
||||
//
|
||||
// override fun read() : Int = when {
|
||||
// ! mBuf.hasRemaining() -> - 1
|
||||
// else -> mBuf.get().toInt() and 0xFF
|
||||
// }
|
||||
//
|
||||
// override fun read(bytes : ByteArray, off : Int, len : Int) : Int {
|
||||
// if(! mBuf.hasRemaining()) return - 1
|
||||
// val willRead = min(len, mBuf.remaining())
|
||||
// mBuf.get(bytes, off, willRead)
|
||||
// return willRead
|
||||
// }
|
||||
//}
|
|
@ -17,6 +17,7 @@
|
|||
package it.sephiroth.android.library.exif2
|
||||
|
||||
import android.util.Log
|
||||
import it.sephiroth.android.library.exif2.utils.notEmpty
|
||||
|
||||
import java.io.UnsupportedEncodingException
|
||||
import java.nio.ByteOrder
|
||||
|
@ -33,94 +34,74 @@ import java.util.Arrays
|
|||
* @see IfdData
|
||||
*/
|
||||
@Suppress("unused")
|
||||
internal open class ExifData( val byteOrder : ByteOrder ) {
|
||||
internal class ExifData(
|
||||
val byteOrder : ByteOrder = ExifInterface.DEFAULT_BYTE_ORDER,
|
||||
val sections : List<ExifParser.Section> = ArrayList(),
|
||||
val mUncompressedDataPosition : Int = 0,
|
||||
val qualityGuess : Int = 0,
|
||||
val jpegProcess : Short = 0
|
||||
) {
|
||||
|
||||
var sections : List<ExifParser.Section>? = null
|
||||
private val mIfdDatas = arrayOfNulls<IfdData>(IfdId.TYPE_IFD_COUNT)
|
||||
/**
|
||||
* Gets the compressed thumbnail. Returns null if there is no compressed
|
||||
* thumbnail.
|
||||
*
|
||||
* @see .hasCompressedThumbnail
|
||||
*/
|
||||
/**
|
||||
* Sets the compressed thumbnail.
|
||||
*/
|
||||
var compressedThumbnail : ByteArray? = null
|
||||
private val mStripBytes = ArrayList<ByteArray?>()
|
||||
var qualityGuess = 0
|
||||
private var imageLength = - 1
|
||||
private var imageWidth = - 1
|
||||
var jpegProcess : Short = 0
|
||||
var mUncompressedDataPosition = 0
|
||||
|
||||
/**
|
||||
* Gets the strip count.
|
||||
*/
|
||||
private val mIfdDatas = arrayOfNulls<IfdData>(IfdData.TYPE_IFD_COUNT)
|
||||
|
||||
// the compressed thumbnail.
|
||||
// null if there is no compressed thumbnail.
|
||||
var compressedThumbnail : ByteArray? = null
|
||||
|
||||
private val mStripBytes = ArrayList<ByteArray?>()
|
||||
val stripCount : Int
|
||||
get() = mStripBytes.size
|
||||
|
||||
/**
|
||||
* Decodes the user comment tag into string as specified in the EXIF
|
||||
* standard. Returns null if decoding failed.
|
||||
*/
|
||||
// Decodes the user comment tag into string as specified in the EXIF standard.
|
||||
// Returns null if decoding failed.
|
||||
val userComment : String?
|
||||
get() {
|
||||
val ifdData = mIfdDatas[IfdId.TYPE_IFD_0] ?: return null
|
||||
|
||||
val ifdData = mIfdDatas[IfdData.TYPE_IFD_0]
|
||||
?: return null
|
||||
|
||||
val tag = ifdData.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_USER_COMMENT))
|
||||
?: return null
|
||||
if(tag.componentCount < 8) {
|
||||
|
||||
if(tag.componentCount < 8)
|
||||
return null
|
||||
}
|
||||
|
||||
val buf = ByteArray(tag.componentCount)
|
||||
tag.getBytes(buf)
|
||||
|
||||
val code = ByteArray(8)
|
||||
System.arraycopy(buf, 0, code, 0, 8)
|
||||
|
||||
return try {
|
||||
when {
|
||||
code.contentEquals(USER_COMMENT_ASCII) -> String(
|
||||
buf,
|
||||
8,
|
||||
buf.size - 8,
|
||||
Charsets.US_ASCII
|
||||
)
|
||||
code.contentEquals(USER_COMMENT_JIS) -> String(
|
||||
buf,
|
||||
8,
|
||||
buf.size - 8,
|
||||
Charset.forName("EUC-JP")
|
||||
)
|
||||
code.contentEquals(USER_COMMENT_UNICODE) -> String(
|
||||
buf,
|
||||
8,
|
||||
buf.size - 8,
|
||||
Charsets.UTF_16
|
||||
)
|
||||
|
||||
val buf = ByteArray(tag.componentCount)
|
||||
tag.getBytes(buf)
|
||||
|
||||
val code = ByteArray(8)
|
||||
System.arraycopy(buf, 0, code, 0, 8)
|
||||
|
||||
val charset = when {
|
||||
code.contentEquals(USER_COMMENT_ASCII) -> Charsets.US_ASCII
|
||||
code.contentEquals(USER_COMMENT_JIS) -> eucJp
|
||||
code.contentEquals(USER_COMMENT_UNICODE) -> Charsets.UTF_16
|
||||
else -> null
|
||||
}
|
||||
if(charset == null) null else String(buf, 8, buf.size - 8, charset)
|
||||
} catch(e : UnsupportedEncodingException) {
|
||||
Log.w(TAG, "Failed to decode the user comment")
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all [ExifTag]s in the ExifData or null if there
|
||||
* are none.
|
||||
*/
|
||||
val allTags : List<ExifTag>?
|
||||
get() {
|
||||
val ret = ArrayList<ExifTag>()
|
||||
mIfdDatas.forEach { it?.allTags?.forEach { tag -> ret.add(tag) } }
|
||||
return if(ret.isEmpty()) null else ret
|
||||
}
|
||||
// list of all [ExifTag]s in the ExifData
|
||||
// or null if there are none.
|
||||
val allTags : List<ExifTag>
|
||||
get() = ArrayList<ExifTag>()
|
||||
.apply { mIfdDatas.forEach { if(it != null) addAll(it.allTagsCollection) } }
|
||||
|
||||
val imageSize : IntArray
|
||||
get() = intArrayOf(imageWidth, imageLength)
|
||||
|
||||
val stripList : List<ByteArray>?
|
||||
get() = mStripBytes.filterNotNull().notEmpty()
|
||||
|
||||
/**
|
||||
* Returns true it this header contains a compressed thumbnail.
|
||||
*/
|
||||
|
@ -166,41 +147,36 @@ internal open class ExifData( val byteOrder : ByteOrder ) {
|
|||
* Returns the tag with a given TID in the given IFD if the tag exists.
|
||||
* Otherwise returns null.
|
||||
*/
|
||||
fun getTag(tag : Short, ifd : Int) : ExifTag? {
|
||||
val ifdData = mIfdDatas[ifd]
|
||||
return ifdData?.getTag(tag)
|
||||
}
|
||||
fun getTag(tag : Short, ifd : Int) : ExifTag? =
|
||||
mIfdDatas[ifd]?.getTag(tag)
|
||||
|
||||
/**
|
||||
* Adds the given ExifTag to its default IFD and returns an existing ExifTag
|
||||
* with the same TID or null if none exist.
|
||||
*/
|
||||
fun addTag(tag : ExifTag?) : ExifTag? {
|
||||
if(tag != null) {
|
||||
val ifd = tag.ifd
|
||||
return addTag(tag, ifd)
|
||||
fun addTag(tag : ExifTag?) : ExifTag? =
|
||||
when(tag) {
|
||||
null -> null
|
||||
else -> addTag(tag, tag.ifd)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given ExifTag to the given IFD and returns an existing ExifTag
|
||||
* with the same TID or null if none exist.
|
||||
*/
|
||||
private fun addTag(tag : ExifTag?, ifdId : Int) : ExifTag? {
|
||||
if(tag != null && ExifTag.isValidIfd(ifdId)) {
|
||||
val ifdData = getOrCreateIfdData(ifdId)
|
||||
return ifdData.setTag(tag)
|
||||
private fun addTag(tag : ExifTag?, ifdId : Int) : ExifTag? =
|
||||
when {
|
||||
tag == null -> null
|
||||
! ExifTag.isValidIfd(ifdId) -> null
|
||||
else -> getOrCreateIfdData(ifdId).setTag(tag)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [IfdData] object corresponding to a given IFD or
|
||||
* generates one if none exist.
|
||||
*/
|
||||
private fun getOrCreateIfdData(ifdId : Int) : IfdData {
|
||||
var ifdData : IfdData? = mIfdDatas[ifdId]
|
||||
var ifdData = mIfdDatas[ifdId]
|
||||
if(ifdData == null) {
|
||||
ifdData = IfdData(ifdId)
|
||||
mIfdDatas[ifdId] = ifdData
|
||||
|
@ -211,9 +187,9 @@ internal open class ExifData( val byteOrder : ByteOrder ) {
|
|||
/**
|
||||
* Removes the thumbnail and its related tags. IFD1 will be removed.
|
||||
*/
|
||||
protected fun removeThumbnailData() {
|
||||
fun removeThumbnailData() {
|
||||
clearThumbnailAndStrips()
|
||||
mIfdDatas[IfdId.TYPE_IFD_1] = null
|
||||
mIfdDatas[IfdData.TYPE_IFD_1] = null
|
||||
}
|
||||
|
||||
fun clearThumbnailAndStrips() {
|
||||
|
@ -233,17 +209,8 @@ internal open class ExifData( val byteOrder : ByteOrder ) {
|
|||
* Returns a list of all [ExifTag]s in a given IFD or null if there
|
||||
* are none.
|
||||
*/
|
||||
fun getAllTagsForIfd(ifd : Int) : List<ExifTag>? {
|
||||
val d = mIfdDatas[ifd] ?: return null
|
||||
val tags = d.allTags
|
||||
val ret = ArrayList<ExifTag>(tags.size)
|
||||
for(t in tags) {
|
||||
ret.add(t)
|
||||
}
|
||||
return if(ret.size == 0) {
|
||||
null
|
||||
} else ret
|
||||
}
|
||||
fun getAllTagsForIfd(ifd : Int) : List<ExifTag>? =
|
||||
mIfdDatas[ifd]?.allTagsCollection?.notEmpty()?.toList()
|
||||
|
||||
// Returns a list of all [ExifTag]s with a given TID
|
||||
// or null if there are none.
|
||||
|
@ -280,7 +247,7 @@ internal open class ExifData( val byteOrder : ByteOrder ) {
|
|||
}
|
||||
}
|
||||
|
||||
for(i in 0 until IfdId.TYPE_IFD_COUNT) {
|
||||
for(i in 0 until IfdData.TYPE_IFD_COUNT) {
|
||||
val ifd1 = other.getIfdData(i)
|
||||
val ifd2 = getIfdData(i)
|
||||
if(ifd1 != ifd2) return false
|
||||
|
@ -294,10 +261,9 @@ internal open class ExifData( val byteOrder : ByteOrder ) {
|
|||
* Returns the [IfdData] object corresponding to a given IFD if it
|
||||
* exists or null.
|
||||
*/
|
||||
fun getIfdData(ifdId : Int) : IfdData? {
|
||||
return if(ExifTag.isValidIfd(ifdId)) {
|
||||
mIfdDatas[ifdId]
|
||||
} else null
|
||||
fun getIfdData(ifdId : Int) = when {
|
||||
! ExifTag.isValidIfd(ifdId) -> null
|
||||
else -> mIfdDatas[ifdId]
|
||||
}
|
||||
|
||||
fun setImageSize(imageWidth : Int, imageLength : Int) {
|
||||
|
@ -307,7 +273,7 @@ internal open class ExifData( val byteOrder : ByteOrder ) {
|
|||
|
||||
override fun hashCode() : Int {
|
||||
var result = byteOrder.hashCode()
|
||||
result = 31 * result + (sections?.hashCode() ?: 0)
|
||||
result = 31 * result + (sections.hashCode())
|
||||
result = 31 * result + mIfdDatas.contentHashCode()
|
||||
result = 31 * result + (compressedThumbnail?.contentHashCode() ?: 0)
|
||||
result = 31 * result + mStripBytes.hashCode()
|
||||
|
@ -330,5 +296,8 @@ internal open class ExifData( val byteOrder : ByteOrder ) {
|
|||
|
||||
private val USER_COMMENT_UNICODE =
|
||||
byteArrayOf(0x55, 0x4E, 0x49, 0x43, 0x4F, 0x44, 0x45, 0x00)
|
||||
|
||||
private val eucJp = Charset.forName("EUC-JP")
|
||||
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -17,25 +17,23 @@
|
|||
package it.sephiroth.android.library.exif2
|
||||
|
||||
import android.util.Log
|
||||
|
||||
import it.sephiroth.android.library.exif2.utils.OrderedDataOutputStream
|
||||
import java.io.BufferedOutputStream
|
||||
import java.io.IOException
|
||||
import java.io.OutputStream
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder
|
||||
import java.util.ArrayList
|
||||
import java.util.*
|
||||
|
||||
@Suppress("unused")
|
||||
internal class ExifOutputStream(private val mInterface : ExifInterface) {
|
||||
/**
|
||||
* Gets the Exif header to be written into the JPEF file.
|
||||
*/
|
||||
/**
|
||||
* Sets the ExifData to be written into the JPEG file. Should be called
|
||||
* before writing image data.
|
||||
*/
|
||||
var exifData : ExifData? = null
|
||||
|
||||
internal class ExifOutputStream(
|
||||
|
||||
private val mInterface : ExifInterface,
|
||||
|
||||
// the Exif header to be written into the JPEG file.
|
||||
private val exifData : ExifData
|
||||
) {
|
||||
|
||||
private val mBuffer = ByteBuffer.allocate(4)
|
||||
|
||||
private fun requestByteToBuffer(
|
||||
|
@ -49,13 +47,9 @@ internal class ExifOutputStream(private val mInterface : ExifInterface) {
|
|||
|
||||
@Throws(IOException::class)
|
||||
fun writeExifData(out : OutputStream) {
|
||||
if(exifData == null) {
|
||||
return
|
||||
}
|
||||
|
||||
Log.v(TAG, "Writing exif data...")
|
||||
|
||||
val nullTags = stripNullValueTags(exifData !!)
|
||||
val nullTags = stripNullValueTags(exifData)
|
||||
createRequiredIfdAndTag()
|
||||
val exifSize = calculateAllOffset()
|
||||
// Log.i(TAG, "exifSize: " + (exifSize + 8));
|
||||
|
@ -64,7 +58,8 @@ internal class ExifOutputStream(private val mInterface : ExifInterface) {
|
|||
}
|
||||
|
||||
val outputStream = BufferedOutputStream(out, STREAMBUFFER_SIZE)
|
||||
val dataOutputStream = OrderedDataOutputStream(outputStream)
|
||||
val dataOutputStream =
|
||||
OrderedDataOutputStream(outputStream)
|
||||
|
||||
dataOutputStream.setByteOrder(ByteOrder.BIG_ENDIAN)
|
||||
|
||||
|
@ -73,12 +68,12 @@ internal class ExifOutputStream(private val mInterface : ExifInterface) {
|
|||
dataOutputStream.writeShort((exifSize + 8).toShort())
|
||||
dataOutputStream.writeInt(EXIF_HEADER)
|
||||
dataOutputStream.writeShort(0x0000.toShort())
|
||||
if(exifData !!.byteOrder == ByteOrder.BIG_ENDIAN) {
|
||||
if(exifData.byteOrder == ByteOrder.BIG_ENDIAN) {
|
||||
dataOutputStream.writeShort(TIFF_BIG_ENDIAN)
|
||||
} else {
|
||||
dataOutputStream.writeShort(TIFF_LITTLE_ENDIAN)
|
||||
}
|
||||
dataOutputStream.setByteOrder(exifData !!.byteOrder)
|
||||
dataOutputStream.setByteOrder(exifData.byteOrder)
|
||||
dataOutputStream.writeShort(TIFF_HEADER)
|
||||
dataOutputStream.writeInt(8)
|
||||
writeAllTags(dataOutputStream)
|
||||
|
@ -86,57 +81,55 @@ internal class ExifOutputStream(private val mInterface : ExifInterface) {
|
|||
writeThumbnail(dataOutputStream)
|
||||
|
||||
for(t in nullTags) {
|
||||
exifData !!.addTag(t)
|
||||
exifData.addTag(t)
|
||||
}
|
||||
|
||||
dataOutputStream.flush()
|
||||
}
|
||||
|
||||
private fun stripNullValueTags(data : ExifData) : ArrayList<ExifTag> {
|
||||
val nullTags = ArrayList<ExifTag>()
|
||||
for(t in data.allTags !!) {
|
||||
if(t.getValue() == null && ! ExifInterface.isOffsetTag(t.tagId)) {
|
||||
data.removeTag(t.tagId, t.ifd)
|
||||
nullTags.add(t)
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullTags
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun writeThumbnail(dataOutputStream : OrderedDataOutputStream) {
|
||||
if(exifData !!.hasCompressedThumbnail()) {
|
||||
val compressedThumbnail = exifData.compressedThumbnail
|
||||
if(compressedThumbnail != null) {
|
||||
Log.d(TAG, "writing thumbnail..")
|
||||
dataOutputStream.write(exifData !!.compressedThumbnail !!)
|
||||
} else if(exifData !!.hasUncompressedStrip()) {
|
||||
Log.d(TAG, "writing uncompressed strip..")
|
||||
for(i in 0 until exifData !!.stripCount) {
|
||||
dataOutputStream.write(exifData !!.getStrip(i) !!)
|
||||
dataOutputStream.write(compressedThumbnail)
|
||||
} else {
|
||||
val stripList = exifData.stripList
|
||||
if(stripList != null) {
|
||||
Log.d(TAG, "writing uncompressed strip..")
|
||||
stripList.forEach {
|
||||
dataOutputStream.write(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun writeAllTags(dataOutputStream : OrderedDataOutputStream) {
|
||||
writeIfd(exifData !!.getIfdData(IfdId.TYPE_IFD_0) !!, dataOutputStream)
|
||||
writeIfd(exifData !!.getIfdData(IfdId.TYPE_IFD_EXIF) !!, dataOutputStream)
|
||||
val interoperabilityIfd = exifData !!.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY)
|
||||
if(interoperabilityIfd != null) {
|
||||
writeIfd(interoperabilityIfd, dataOutputStream)
|
||||
}
|
||||
val gpsIfd = exifData !!.getIfdData(IfdId.TYPE_IFD_GPS)
|
||||
if(gpsIfd != null) {
|
||||
writeIfd(gpsIfd, dataOutputStream)
|
||||
}
|
||||
val ifd1 = exifData !!.getIfdData(IfdId.TYPE_IFD_1)
|
||||
if(ifd1 != null) {
|
||||
writeIfd(exifData !!.getIfdData(IfdId.TYPE_IFD_1) !!, dataOutputStream)
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun writeIfd(ifd : IfdData, dataOutputStream : OrderedDataOutputStream) {
|
||||
val tags = ifd.allTags
|
||||
private fun writeIfd(ifd : IfdData?, dataOutputStream : OrderedDataOutputStream) {
|
||||
ifd ?: return
|
||||
val tags = ifd.allTagsCollection
|
||||
dataOutputStream.writeShort(tags.size.toShort())
|
||||
for(tag in tags) {
|
||||
dataOutputStream.writeShort(tag.tagId)
|
||||
|
@ -166,8 +159,8 @@ internal class ExifOutputStream(private val mInterface : ExifInterface) {
|
|||
private fun calculateOffsetOfIfd(ifd : IfdData, offsetArg : Int) : Int {
|
||||
var offset = offsetArg
|
||||
offset += 2 + ifd.tagCount * TAG_SIZE + 4
|
||||
val tags = ifd.allTags
|
||||
for(tag in tags) {
|
||||
|
||||
for(tag in ifd.allTagsCollection) {
|
||||
if(tag.dataSize > 4) {
|
||||
tag.offset = offset
|
||||
offset += tag.dataSize
|
||||
|
@ -178,49 +171,53 @@ internal class ExifOutputStream(private val mInterface : ExifInterface) {
|
|||
|
||||
@Throws(IOException::class)
|
||||
private fun createRequiredIfdAndTag() {
|
||||
|
||||
// IFD0 is required for all file
|
||||
var ifd0 = exifData !!.getIfdData(IfdId.TYPE_IFD_0)
|
||||
var ifd0 = exifData.getIfdData(IfdData.TYPE_IFD_0)
|
||||
if(ifd0 == null) {
|
||||
ifd0 = IfdData(IfdId.TYPE_IFD_0)
|
||||
exifData !!.addIfdData(ifd0)
|
||||
ifd0 = IfdData(IfdData.TYPE_IFD_0)
|
||||
exifData.addIfdData(ifd0)
|
||||
}
|
||||
|
||||
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.
|
||||
var exifIfd = exifData !!.getIfdData(IfdId.TYPE_IFD_EXIF)
|
||||
var exifIfd = exifData.getIfdData(IfdData.TYPE_IFD_EXIF)
|
||||
if(exifIfd == null) {
|
||||
exifIfd = IfdData(IfdId.TYPE_IFD_EXIF)
|
||||
exifData !!.addIfdData(exifIfd)
|
||||
exifIfd = IfdData(IfdData.TYPE_IFD_EXIF)
|
||||
exifData.addIfdData(exifIfd)
|
||||
}
|
||||
|
||||
// GPS IFD
|
||||
val gpsIfd = exifData !!.getIfdData(IfdId.TYPE_IFD_GPS)
|
||||
val gpsIfd = exifData.getIfdData(IfdData.TYPE_IFD_GPS)
|
||||
if(gpsIfd != null) {
|
||||
val gpsOffsetTag = mInterface.buildUninitializedTag(ExifInterface.TAG_GPS_IFD)
|
||||
?: throw IOException("No definition for crucial exif tag: " + ExifInterface.TAG_GPS_IFD)
|
||||
?: throw IOException("No definition for crucial exif tag: ${ExifInterface.TAG_GPS_IFD}")
|
||||
ifd0.setTag(gpsOffsetTag)
|
||||
}
|
||||
|
||||
// Interoperability IFD
|
||||
val interIfd = exifData !!.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY)
|
||||
val interIfd = exifData.getIfdData(IfdData.TYPE_IFD_INTEROPERABILITY)
|
||||
if(interIfd != null) {
|
||||
val interOffsetTag =
|
||||
mInterface.buildUninitializedTag(ExifInterface.TAG_INTEROPERABILITY_IFD)
|
||||
?: throw IOException("No definition for crucial exif tag: " + ExifInterface.TAG_INTEROPERABILITY_IFD)
|
||||
?: throw IOException("No definition for crucial exif tag: ${ExifInterface.TAG_INTEROPERABILITY_IFD}")
|
||||
exifIfd.setTag(interOffsetTag)
|
||||
}
|
||||
|
||||
var ifd1 = exifData !!.getIfdData(IfdId.TYPE_IFD_1)
|
||||
var ifd1 = exifData.getIfdData(IfdData.TYPE_IFD_1)
|
||||
|
||||
// thumbnail
|
||||
val compressedThumbnail = exifData.compressedThumbnail
|
||||
val stripList = exifData.stripList
|
||||
when {
|
||||
exifData !!.hasCompressedThumbnail() -> {
|
||||
|
||||
|
||||
compressedThumbnail != null -> {
|
||||
if(ifd1 == null) {
|
||||
ifd1 = IfdData(IfdId.TYPE_IFD_1)
|
||||
exifData !!.addIfdData(ifd1)
|
||||
ifd1 = IfdData(IfdData.TYPE_IFD_1)
|
||||
exifData.addIfdData(ifd1)
|
||||
}
|
||||
|
||||
val offsetTag =
|
||||
|
@ -232,34 +229,35 @@ internal class ExifOutputStream(private val mInterface : ExifInterface) {
|
|||
mInterface.buildUninitializedTag(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH)
|
||||
?: throw IOException("No definition for crucial exif tag: ${ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH}")
|
||||
|
||||
lengthTag.setValue(exifData !!.compressedThumbnail !!.size)
|
||||
lengthTag.setValue(compressedThumbnail.size)
|
||||
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))
|
||||
}
|
||||
exifData !!.hasUncompressedStrip() -> {
|
||||
|
||||
stripList != null -> {
|
||||
if(ifd1 == null) {
|
||||
ifd1 = IfdData(IfdId.TYPE_IFD_1)
|
||||
exifData !!.addIfdData(ifd1)
|
||||
ifd1 = IfdData(IfdData.TYPE_IFD_1)
|
||||
exifData.addIfdData(ifd1)
|
||||
}
|
||||
val stripCount = exifData !!.stripCount
|
||||
val offsetTag = mInterface.buildUninitializedTag(ExifInterface.TAG_STRIP_OFFSETS)
|
||||
?: throw IOException("No definition for crucial exif tag: ${ExifInterface.TAG_STRIP_OFFSETS}")
|
||||
val lengthTag = mInterface.buildUninitializedTag(ExifInterface.TAG_STRIP_BYTE_COUNTS)
|
||||
?: throw IOException("No definition for crucial exif tag: ${ExifInterface.TAG_STRIP_BYTE_COUNTS}")
|
||||
val lengths = LongArray(stripCount)
|
||||
for(i in 0 until exifData !!.stripCount) {
|
||||
lengths[i] = exifData !!.getStrip(i) !!.size.toLong()
|
||||
}
|
||||
lengthTag.setValue(lengths)
|
||||
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)
|
||||
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))
|
||||
}
|
||||
|
||||
ifd1 != null -> {
|
||||
// Get rid of offset and length tags if there is no thumbnail.
|
||||
ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS))
|
||||
|
@ -272,50 +270,55 @@ internal class ExifOutputStream(private val mInterface : ExifInterface) {
|
|||
|
||||
private fun calculateAllOffset() : Int {
|
||||
var offset = TIFF_HEADER_SIZE.toInt()
|
||||
val exifData = this.exifData !!
|
||||
val ifd0 = exifData.getIfdData(IfdId.TYPE_IFD_0)
|
||||
offset = calculateOffsetOfIfd(ifd0 !!, offset)
|
||||
ifd0.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_EXIF_IFD))
|
||||
?.setValue(offset)
|
||||
|
||||
val exifIfd = exifData.getIfdData(IfdId.TYPE_IFD_EXIF)
|
||||
offset = calculateOffsetOfIfd(exifIfd !!, offset)
|
||||
|
||||
val interIfd = exifData.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY)
|
||||
if(interIfd != null) {
|
||||
exifIfd.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_INTEROPERABILITY_IFD))
|
||||
?.setValue(offset)
|
||||
offset = calculateOffsetOfIfd(interIfd, offset)
|
||||
val ifd0 = exifData.getIfdData(IfdData.TYPE_IFD_0)?.also {
|
||||
offset = calculateOffsetOfIfd(it, offset)
|
||||
}
|
||||
|
||||
val gpsIfd = exifData.getIfdData(IfdId.TYPE_IFD_GPS)
|
||||
if(gpsIfd != null) {
|
||||
ifd0.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_GPS_IFD))
|
||||
val exifIfd = exifData.getIfdData(IfdData.TYPE_IFD_EXIF)?.also { it ->
|
||||
ifd0?.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_EXIF_IFD))
|
||||
?.setValue(offset)
|
||||
offset = calculateOffsetOfIfd(gpsIfd, offset)
|
||||
offset = calculateOffsetOfIfd(it, offset)
|
||||
}
|
||||
|
||||
val ifd1 = exifData.getIfdData(IfdId.TYPE_IFD_1)
|
||||
if(ifd1 != null) {
|
||||
ifd0.offsetToNextIfd = offset
|
||||
offset = calculateOffsetOfIfd(ifd1, offset)
|
||||
exifData.getIfdData(IfdData.TYPE_IFD_INTEROPERABILITY)?.also { it ->
|
||||
exifIfd?.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_INTEROPERABILITY_IFD))
|
||||
?.setValue(offset)
|
||||
offset = calculateOffsetOfIfd(it, offset)
|
||||
}
|
||||
|
||||
// thumbnail
|
||||
if(exifData .hasCompressedThumbnail()) {
|
||||
ifd1 !!.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT))
|
||||
exifData.getIfdData(IfdData.TYPE_IFD_GPS)?.also {
|
||||
ifd0?.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_GPS_IFD))
|
||||
?.setValue(offset)
|
||||
offset += exifData .compressedThumbnail !!.size
|
||||
} else if(exifData .hasUncompressedStrip()) {
|
||||
val stripCount = exifData .stripCount
|
||||
val offsets = LongArray(stripCount)
|
||||
for(i in 0 until exifData .stripCount) {
|
||||
offsets[i] = offset.toLong()
|
||||
offset += exifData .getStrip(i) !!.size
|
||||
offset = calculateOffsetOfIfd(it, offset)
|
||||
}
|
||||
|
||||
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))
|
||||
?.setValue(offset)
|
||||
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)
|
||||
}
|
||||
ifd1 !!.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS))
|
||||
?.setValue(offsets)
|
||||
}
|
||||
|
||||
return offset
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package it.sephiroth.android.library.exif2
|
||||
|
||||
import android.util.Log
|
||||
import it.sephiroth.android.library.exif2.utils.CountedDataInputStream
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
|
@ -40,15 +41,15 @@ private constructor(
|
|||
/**
|
||||
* the ID of current IFD.
|
||||
*
|
||||
* @see IfdId.TYPE_IFD_0
|
||||
* @see IfdId.TYPE_IFD_1
|
||||
* @see IfdId.TYPE_IFD_GPS
|
||||
* @see IfdId.TYPE_IFD_INTEROPERABILITY
|
||||
* @see IfdId.TYPE_IFD_EXIF
|
||||
* @see IfdData.TYPE_IFD_0
|
||||
* @see IfdData.TYPE_IFD_1
|
||||
* @see IfdData.TYPE_IFD_GPS
|
||||
* @see IfdData.TYPE_IFD_INTEROPERABILITY
|
||||
* @see IfdData.TYPE_IFD_EXIF
|
||||
*/
|
||||
var currentIfd : Int = 0
|
||||
private set
|
||||
|
||||
|
||||
/**
|
||||
* If [.next] return [.EVENT_NEW_TAG] or
|
||||
* [.EVENT_VALUE_OF_REGISTERED_TAG], call this function to get the
|
||||
|
@ -154,10 +155,10 @@ private constructor(
|
|||
throw ExifInvalidFormatException("Invalid offset $offset")
|
||||
}
|
||||
mIfd0Position = offset.toInt()
|
||||
currentIfd = IfdId.TYPE_IFD_0
|
||||
currentIfd = IfdData.TYPE_IFD_0
|
||||
|
||||
if(isIfdRequested(IfdId.TYPE_IFD_0) || needToParseOffsetsInCurrentIfd()) {
|
||||
registerIfd(IfdId.TYPE_IFD_0, offset)
|
||||
if(isIfdRequested(IfdData.TYPE_IFD_0) || needToParseOffsetsInCurrentIfd()) {
|
||||
registerIfd(IfdData.TYPE_IFD_0, offset)
|
||||
if(offset != DEFAULT_IFD0_OFFSET.toLong()) {
|
||||
val ba = ByteArray(offset.toInt() - DEFAULT_IFD0_OFFSET)
|
||||
mDataAboveIfd0 = ba
|
||||
|
@ -182,15 +183,16 @@ private constructor(
|
|||
|
||||
@Throws(IOException::class, ExifInvalidFormatException::class)
|
||||
private fun seekTiffData(inputStream : InputStream) : CountedDataInputStream {
|
||||
val dataStream = CountedDataInputStream(inputStream)
|
||||
val dataStream =
|
||||
CountedDataInputStream(inputStream)
|
||||
var tiffStream : CountedDataInputStream? = null
|
||||
|
||||
var a = dataStream.readUnsignedByte()
|
||||
val b = dataStream.readUnsignedByte()
|
||||
|
||||
|
||||
|
||||
if(a == 137 && b == 80) error("maybe PNG image")
|
||||
|
||||
|
||||
if(a != 0xFF || b != JpegHeader.TAG_SOI) error("invalid jpeg header")
|
||||
|
||||
while(true) {
|
||||
|
@ -213,9 +215,6 @@ private constructor(
|
|||
Log.w(TAG, "Extraneous ${a - 1} padding bytes before section $marker")
|
||||
}
|
||||
|
||||
val section = Section()
|
||||
section.type = marker
|
||||
|
||||
// Read the length of the section.
|
||||
val lh = dataStream.readByte().toInt()
|
||||
val ll = dataStream.readByte().toInt()
|
||||
|
@ -225,8 +224,6 @@ private constructor(
|
|||
throw ExifInvalidFormatException("Invalid marker")
|
||||
}
|
||||
|
||||
section.size = itemlen
|
||||
|
||||
data = ByteArray(itemlen)
|
||||
data[0] = lh.toByte()
|
||||
data[1] = ll.toByte()
|
||||
|
@ -240,7 +237,7 @@ private constructor(
|
|||
throw ExifInvalidFormatException("Premature end of file? Expecting " + (itemlen - 2) + ", received " + got)
|
||||
}
|
||||
|
||||
section.data = data
|
||||
val section = Section(type = marker, size = itemlen, data = data)
|
||||
|
||||
var ignore = false
|
||||
|
||||
|
@ -286,7 +283,9 @@ private constructor(
|
|||
// header = Exif, headerTail=\0\0
|
||||
if(header == EXIF_HEADER && headerTail == EXIF_HEADER_TAIL) {
|
||||
tiffStream =
|
||||
CountedDataInputStream(ByteArrayInputStream(data, 8, itemlen - 8))
|
||||
CountedDataInputStream(
|
||||
ByteArrayInputStream(data, 8, itemlen - 8)
|
||||
)
|
||||
tiffStream.end = itemlen - 6
|
||||
ignore = false
|
||||
} else {
|
||||
|
@ -428,11 +427,11 @@ private constructor(
|
|||
|
||||
private fun isIfdRequested(ifdType : Int) : Boolean {
|
||||
when(ifdType) {
|
||||
IfdId.TYPE_IFD_0 -> return mOptions and ExifInterface.Options.OPTION_IFD_0 != 0
|
||||
IfdId.TYPE_IFD_1 -> return mOptions and ExifInterface.Options.OPTION_IFD_1 != 0
|
||||
IfdId.TYPE_IFD_EXIF -> return mOptions and ExifInterface.Options.OPTION_IFD_EXIF != 0
|
||||
IfdId.TYPE_IFD_GPS -> return mOptions and ExifInterface.Options.OPTION_IFD_GPS != 0
|
||||
IfdId.TYPE_IFD_INTEROPERABILITY -> return mOptions and ExifInterface.Options.OPTION_IFD_INTEROPERABILITY != 0
|
||||
IfdData.TYPE_IFD_0 -> return mOptions and ExifInterface.Options.OPTION_IFD_0 != 0
|
||||
IfdData.TYPE_IFD_1 -> return mOptions and ExifInterface.Options.OPTION_IFD_1 != 0
|
||||
IfdData.TYPE_IFD_EXIF -> return mOptions and ExifInterface.Options.OPTION_IFD_EXIF != 0
|
||||
IfdData.TYPE_IFD_GPS -> return mOptions and ExifInterface.Options.OPTION_IFD_GPS != 0
|
||||
IfdData.TYPE_IFD_INTEROPERABILITY -> return mOptions and ExifInterface.Options.OPTION_IFD_INTEROPERABILITY != 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
@ -440,17 +439,17 @@ private constructor(
|
|||
private fun needToParseOffsetsInCurrentIfd() : Boolean {
|
||||
return when(currentIfd) {
|
||||
|
||||
IfdId.TYPE_IFD_0 ->
|
||||
isIfdRequested(IfdId.TYPE_IFD_EXIF) ||
|
||||
isIfdRequested(IfdId.TYPE_IFD_GPS) ||
|
||||
isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY) ||
|
||||
isIfdRequested(IfdId.TYPE_IFD_1)
|
||||
IfdData.TYPE_IFD_0 ->
|
||||
isIfdRequested(IfdData.TYPE_IFD_EXIF) ||
|
||||
isIfdRequested(IfdData.TYPE_IFD_GPS) ||
|
||||
isIfdRequested(IfdData.TYPE_IFD_INTEROPERABILITY) ||
|
||||
isIfdRequested(IfdData.TYPE_IFD_1)
|
||||
|
||||
IfdId.TYPE_IFD_1 -> isThumbnailRequested
|
||||
IfdData.TYPE_IFD_1 -> isThumbnailRequested
|
||||
|
||||
IfdId.TYPE_IFD_EXIF ->
|
||||
IfdData.TYPE_IFD_EXIF ->
|
||||
// The offset to interoperability IFD is located in Exif IFD
|
||||
isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY)
|
||||
isIfdRequested(IfdData.TYPE_IFD_INTEROPERABILITY)
|
||||
|
||||
else -> false
|
||||
}
|
||||
|
@ -508,11 +507,11 @@ private constructor(
|
|||
return EVENT_NEW_TAG
|
||||
} else if(offset == endOfTags) {
|
||||
// There is a link to ifd1 at the end of ifd0
|
||||
if(currentIfd == IfdId.TYPE_IFD_0) {
|
||||
if(currentIfd == IfdData.TYPE_IFD_0) {
|
||||
val ifdOffset = readUnsignedLong()
|
||||
if(isIfdRequested(IfdId.TYPE_IFD_1) || isThumbnailRequested) {
|
||||
if(isIfdRequested(IfdData.TYPE_IFD_1) || isThumbnailRequested) {
|
||||
if(ifdOffset != 0L) {
|
||||
registerIfd(IfdId.TYPE_IFD_1, ifdOffset)
|
||||
registerIfd(IfdData.TYPE_IFD_1, ifdOffset)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -610,9 +609,9 @@ private constructor(
|
|||
}
|
||||
val ifdOffset = readUnsignedLong()
|
||||
// For ifd0, there is a link to ifd1 in the end of all tags
|
||||
if(currentIfd == IfdId.TYPE_IFD_0 && (isIfdRequested(IfdId.TYPE_IFD_1) || isThumbnailRequested)) {
|
||||
if(currentIfd == IfdData.TYPE_IFD_0 && (isIfdRequested(IfdData.TYPE_IFD_1) || isThumbnailRequested)) {
|
||||
if(ifdOffset > 0) {
|
||||
registerIfd(IfdId.TYPE_IFD_1, ifdOffset)
|
||||
registerIfd(IfdData.TYPE_IFD_1, ifdOffset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -720,19 +719,19 @@ private constructor(
|
|||
val tid = tag.tagId
|
||||
val ifd = tag.ifd
|
||||
if(tid == TAG_EXIF_IFD && checkAllowed(ifd, ExifInterface.TAG_EXIF_IFD)) {
|
||||
if(isIfdRequested(IfdId.TYPE_IFD_EXIF) || isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY)) {
|
||||
registerIfd(IfdId.TYPE_IFD_EXIF, tag.getValueAt(0))
|
||||
if(isIfdRequested(IfdData.TYPE_IFD_EXIF) || isIfdRequested(IfdData.TYPE_IFD_INTEROPERABILITY)) {
|
||||
registerIfd(IfdData.TYPE_IFD_EXIF, tag.getValueAt(0))
|
||||
}
|
||||
} else if(tid == TAG_GPS_IFD && checkAllowed(ifd, ExifInterface.TAG_GPS_IFD)) {
|
||||
if(isIfdRequested(IfdId.TYPE_IFD_GPS)) {
|
||||
registerIfd(IfdId.TYPE_IFD_GPS, tag.getValueAt(0))
|
||||
if(isIfdRequested(IfdData.TYPE_IFD_GPS)) {
|
||||
registerIfd(IfdData.TYPE_IFD_GPS, tag.getValueAt(0))
|
||||
}
|
||||
} else if(tid == TAG_INTEROPERABILITY_IFD && checkAllowed(
|
||||
ifd,
|
||||
ExifInterface.TAG_INTEROPERABILITY_IFD
|
||||
)) {
|
||||
if(isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY)) {
|
||||
registerIfd(IfdId.TYPE_IFD_INTEROPERABILITY, tag.getValueAt(0))
|
||||
if(isIfdRequested(IfdData.TYPE_IFD_INTEROPERABILITY)) {
|
||||
registerIfd(IfdData.TYPE_IFD_INTEROPERABILITY, tag.getValueAt(0))
|
||||
}
|
||||
} else if(tid == TAG_JPEG_INTERCHANGE_FORMAT && checkAllowed(
|
||||
ifd,
|
||||
|
@ -750,7 +749,7 @@ private constructor(
|
|||
}
|
||||
} else if(tid == TAG_STRIP_OFFSETS && checkAllowed(ifd, ExifInterface.TAG_STRIP_OFFSETS)) {
|
||||
if(isThumbnailRequested) {
|
||||
if(tag.hasValue()) {
|
||||
if(tag.hasValue) {
|
||||
for(i in 0 until tag.componentCount) {
|
||||
if(tag.dataType == ExifTag.TYPE_UNSIGNED_SHORT) {
|
||||
registerUncompressedStrip(i, tag.getValueAt(i))
|
||||
|
@ -765,16 +764,16 @@ private constructor(
|
|||
} else if(tid == TAG_STRIP_BYTE_COUNTS && checkAllowed(
|
||||
ifd,
|
||||
ExifInterface.TAG_STRIP_BYTE_COUNTS
|
||||
) && isThumbnailRequested && tag.hasValue()) {
|
||||
) && isThumbnailRequested && tag.hasValue) {
|
||||
mStripSizeTag = tag
|
||||
}
|
||||
}
|
||||
|
||||
fun isDefinedTag(ifdId : Int, tagId : Int) : Boolean {
|
||||
fun isDefinedTag(ifdId : Int, tagId : Short) : Boolean {
|
||||
return mInterface.tagInfo.get(
|
||||
ExifInterface.defineTag(
|
||||
ifdId,
|
||||
tagId.toShort()
|
||||
tagId
|
||||
)
|
||||
) != ExifInterface.DEFINITION_NULL
|
||||
}
|
||||
|
@ -946,12 +945,8 @@ private constructor(
|
|||
internal var isRequested : Boolean
|
||||
)
|
||||
|
||||
class Section {
|
||||
internal var size : Int = 0
|
||||
internal var type : Int = 0
|
||||
internal var data : ByteArray? = null
|
||||
}
|
||||
|
||||
class Section(var size : Int, var type : Int, var data : ByteArray)
|
||||
|
||||
companion object {
|
||||
private const val TAG = "ExifParser"
|
||||
|
||||
|
|
|
@ -37,12 +37,13 @@ internal class ExifReader(private val mInterface : ExifInterface) {
|
|||
@Throws(ExifInvalidFormatException::class, IOException::class)
|
||||
fun read(inputStream : InputStream, options : Int) : ExifData {
|
||||
val parser = ExifParser.parse(inputStream, options, mInterface)
|
||||
val exifData = ExifData(parser.byteOrder )
|
||||
exifData.sections = parser.sections
|
||||
exifData.mUncompressedDataPosition = parser.uncompressedDataPosition
|
||||
|
||||
exifData.qualityGuess = parser.qualityGuess
|
||||
exifData.jpegProcess = parser.jpegProcess
|
||||
val exifData = ExifData(
|
||||
byteOrder = parser.byteOrder,
|
||||
sections = parser.sections,
|
||||
mUncompressedDataPosition = parser.uncompressedDataPosition,
|
||||
qualityGuess = parser.qualityGuess,
|
||||
jpegProcess = parser.jpegProcess
|
||||
)
|
||||
|
||||
val w = parser.imageWidth
|
||||
val h = parser.imageLength
|
||||
|
@ -51,33 +52,34 @@ internal class ExifReader(private val mInterface : ExifInterface) {
|
|||
exifData.setImageSize(w, h)
|
||||
}
|
||||
|
||||
var tag : ExifTag?
|
||||
|
||||
var event = parser.next()
|
||||
while(event != ExifParser.EVENT_END) {
|
||||
when(event) {
|
||||
ExifParser.EVENT_START_OF_IFD -> exifData.addIfdData(IfdData(parser.currentIfd))
|
||||
|
||||
ExifParser.EVENT_START_OF_IFD ->
|
||||
exifData.addIfdData(IfdData(parser.currentIfd))
|
||||
|
||||
ExifParser.EVENT_NEW_TAG -> {
|
||||
tag = parser.tag
|
||||
|
||||
|
||||
|
||||
if(! tag !!.hasValue()) {
|
||||
parser.registerForTagValue(tag)
|
||||
} else {
|
||||
// Log.v(TAG, "parsing id " + tag.getTagId() + " = " + tag);
|
||||
if(parser.isDefinedTag(tag.ifd, tag.tagId.toInt())) {
|
||||
exifData.getIfdData(tag.ifd) !!.setTag(tag)
|
||||
} else {
|
||||
val tag = parser.tag
|
||||
when {
|
||||
tag == null ->
|
||||
Log.w(TAG, "parser.tag is null")
|
||||
|
||||
! tag.hasValue ->
|
||||
parser.registerForTagValue(tag)
|
||||
|
||||
! parser.isDefinedTag(tag.ifd, tag.tagId) ->
|
||||
Log.w(TAG, "skip tag because not registered in the tag table:$tag")
|
||||
}
|
||||
|
||||
else ->
|
||||
exifData.getIfdData(tag.ifd)?.setTag(tag)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ExifParser.EVENT_VALUE_OF_REGISTERED_TAG -> {
|
||||
tag = parser.tag
|
||||
if(tag !!.dataType == ExifTag.TYPE_UNDEFINED) {
|
||||
val tag = parser.tag !!
|
||||
if(tag.dataType == ExifTag.TYPE_UNDEFINED) {
|
||||
parser.readFullTagValue(tag)
|
||||
}
|
||||
exifData.getIfdData(tag.ifd) !!.setTag(tag)
|
||||
|
|
|
@ -52,11 +52,11 @@ open class ExifTag internal constructor(
|
|||
|
||||
// The ifd that this tag should be put in. the ID of the IFD this tag belongs to.
|
||||
/*
|
||||
* @see IfdId.TYPE_IFD_0
|
||||
* @see IfdId.TYPE_IFD_1
|
||||
* @see IfdId.TYPE_IFD_EXIF
|
||||
* @see IfdId.TYPE_IFD_GPS
|
||||
* @see IfdId.TYPE_IFD_INTEROPERABILITY
|
||||
* @see IfdData.TYPE_IFD_0
|
||||
* @see IfdData.TYPE_IFD_1
|
||||
* @see IfdData.TYPE_IFD_EXIF
|
||||
* @see IfdData.TYPE_IFD_GPS
|
||||
* @see IfdData.TYPE_IFD_INTEROPERABILITY
|
||||
*/
|
||||
var ifd : Int,
|
||||
|
||||
|
@ -69,7 +69,7 @@ open class ExifTag internal constructor(
|
|||
*/
|
||||
|
||||
// TODO: fix integer overflows with this
|
||||
var componentCount : Int = 0
|
||||
var componentCount : Int = componentCount
|
||||
private set
|
||||
|
||||
// The value (array of elements of type Tag Type)
|
||||
|
@ -83,7 +83,6 @@ open class ExifTag internal constructor(
|
|||
val dataSize : Int
|
||||
get() = componentCount * getElementSize(dataType)
|
||||
|
||||
|
||||
/**
|
||||
* Gets the value as a byte array. This method should be used for tags of
|
||||
* type [.TYPE_UNDEFINED] or [.TYPE_UNSIGNED_BYTE].
|
||||
|
@ -124,9 +123,9 @@ open class ExifTag internal constructor(
|
|||
*/
|
||||
// Truncates
|
||||
val valueAsInts : IntArray?
|
||||
get() = when(val v = mValue){
|
||||
is LongArray-> IntArray(v.size){ v[it].toInt()}
|
||||
else ->null
|
||||
get() = when(val v = mValue) {
|
||||
is LongArray -> IntArray(v.size) { v[it].toInt() }
|
||||
else -> null
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -143,7 +142,6 @@ open class ExifTag internal constructor(
|
|||
else -> null
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the [.TYPE_ASCII] data.
|
||||
*
|
||||
|
@ -159,11 +157,6 @@ open class ExifTag internal constructor(
|
|||
val stringByte : ByteArray?
|
||||
get() = mValue as? ByteArray
|
||||
|
||||
init {
|
||||
this.componentCount = componentCount
|
||||
mValue = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the component count of this tag. Call this function before
|
||||
* setValue() if the length of value does not match the component count.
|
||||
|
@ -176,9 +169,8 @@ open class ExifTag internal constructor(
|
|||
* Returns true if this ExifTag contains value; otherwise, this tag will
|
||||
* contain an offset value that is determined when the tag is written.
|
||||
*/
|
||||
fun hasValue() : Boolean {
|
||||
return mValue != null
|
||||
}
|
||||
val hasValue :Boolean
|
||||
get() = mValue != null
|
||||
|
||||
/**
|
||||
* Sets integer values into this tag. This method should be used for tags of
|
||||
|
@ -225,9 +217,7 @@ open class ExifTag internal constructor(
|
|||
* * The component count in the definition of this tag is not 1.
|
||||
*
|
||||
*/
|
||||
fun setValue(value : Int) : Boolean {
|
||||
return setValue(intArrayOf(value))
|
||||
}
|
||||
fun setValue(value : Int) = setValue(intArrayOf(value))
|
||||
|
||||
/**
|
||||
* Sets long values into this tag. This method should be used for tags of
|
||||
|
@ -260,9 +250,7 @@ open class ExifTag internal constructor(
|
|||
* * The component count in the definition for this tag is not 1.
|
||||
*
|
||||
*/
|
||||
fun setValue(value : Long) : Boolean {
|
||||
return setValue(longArrayOf(value))
|
||||
}
|
||||
fun setValue(value : Long) = setValue(longArrayOf(value))
|
||||
|
||||
/**
|
||||
* Sets Rational values into this tag. This method should be used for tags
|
||||
|
@ -309,8 +297,7 @@ open class ExifTag internal constructor(
|
|||
*
|
||||
* @see Rational
|
||||
*/
|
||||
fun setValue(value : Rational) : Boolean =
|
||||
setValue(arrayOf(value))
|
||||
fun setValue(value : Rational) =setValue(arrayOf(value))
|
||||
|
||||
/**
|
||||
* Sets byte values into this tag. This method should be used for tags of
|
||||
|
@ -332,7 +319,7 @@ open class ExifTag internal constructor(
|
|||
return false
|
||||
}
|
||||
componentCount = length
|
||||
mValue = ByteArray(length).also{
|
||||
mValue = ByteArray(length).also {
|
||||
System.arraycopy(value, offset, it, 0, length)
|
||||
}
|
||||
return true
|
||||
|
@ -348,8 +335,7 @@ open class ExifTag internal constructor(
|
|||
* * The component count in the definition for this tag is not 1.
|
||||
*
|
||||
*/
|
||||
fun setValue(value : Byte) : Boolean =
|
||||
setValue(byteArrayOf(value))
|
||||
fun setValue(value : Byte) = setValue(byteArrayOf(value))
|
||||
|
||||
/**
|
||||
* Sets the value for this tag using an appropriate setValue method for the
|
||||
|
@ -375,31 +361,33 @@ open class ExifTag internal constructor(
|
|||
is Int -> return setValue(obj.toInt())
|
||||
is Long -> return setValue(obj.toLong())
|
||||
|
||||
else ->{
|
||||
|
||||
else -> {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val ra = obj as? Array<Rational>
|
||||
if(ra != null) return setValue( ra )
|
||||
if(ra != null) return setValue(ra)
|
||||
|
||||
// Nulls in this array are treated as zeroes.
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val sa = obj as? Array<Short?>
|
||||
if( sa != null) return setValue(IntArray(sa.size){ (sa[it]?.toInt() ?: 0) and 0xffff})
|
||||
if(sa != null) return setValue(IntArray(sa.size) {
|
||||
(sa[it]?.toInt() ?: 0) and 0xffff
|
||||
})
|
||||
|
||||
// Nulls in this array are treated as zeroes.
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val ia = obj as? Array<Int?>
|
||||
if( ia != null) return setValue(IntArray(ia.size){ ia[it] ?: 0 })
|
||||
if(ia != null) return setValue(IntArray(ia.size) { ia[it] ?: 0 })
|
||||
|
||||
// Nulls in this array are treated as zeroes.
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val la = obj as? Array<Long?>
|
||||
if( la != null) return setValue(LongArray(la.size){ la[it] ?: 0L })
|
||||
if(la != null) return setValue(LongArray(la.size) { la[it] ?: 0L })
|
||||
|
||||
// Nulls in this array are treated as zeroes.
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val ba = obj as? Array<Byte?>
|
||||
if( ba != null) return setValue(ByteArray(ba.size){ ba[it] ?: 0 })
|
||||
if(ba != null) return setValue(ByteArray(ba.size) { ba[it] ?: 0 })
|
||||
|
||||
return false
|
||||
}
|
||||
|
@ -487,7 +475,7 @@ open class ExifTag internal constructor(
|
|||
*/
|
||||
fun getValueAsByte(defaultValue : Byte) : Byte {
|
||||
val array = valueAsBytes
|
||||
return if(array?.isNotEmpty()==true) array[0] else defaultValue
|
||||
return if(array?.isNotEmpty() == true) array[0] else defaultValue
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -501,7 +489,7 @@ open class ExifTag internal constructor(
|
|||
* @return the tag's value as a Rational, or the defaultValue.
|
||||
*/
|
||||
fun getValueAsRational(defaultValue : Long) : Rational =
|
||||
getValueAsRational( Rational(defaultValue, 1))
|
||||
getValueAsRational(Rational(defaultValue, 1))
|
||||
|
||||
/**
|
||||
* Gets the value as a Rational. If there are more than 1 Rationals in this
|
||||
|
@ -514,7 +502,7 @@ open class ExifTag internal constructor(
|
|||
*/
|
||||
private fun getValueAsRational(defaultValue : Rational) : Rational {
|
||||
val array = valueAsRationals
|
||||
return if(array?.isNotEmpty()==true) array[0] else defaultValue
|
||||
return if(array?.isNotEmpty() == true) array[0] else defaultValue
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -528,7 +516,7 @@ open class ExifTag internal constructor(
|
|||
*/
|
||||
fun getValueAsInt(defaultValue : Int) : Int {
|
||||
val array = valueAsInts
|
||||
return if(array?.isNotEmpty()==true) array[0] else defaultValue
|
||||
return if(array?.isNotEmpty() == true) array[0] else defaultValue
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -542,7 +530,7 @@ open class ExifTag internal constructor(
|
|||
*/
|
||||
fun getValueAsLong(defaultValue : Long) : Long {
|
||||
val array = valueAsLongs
|
||||
return if(array?.isNotEmpty()==true) array[0] else defaultValue
|
||||
return if(array?.isNotEmpty() == true) array[0] else defaultValue
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -636,54 +624,24 @@ open class ExifTag internal constructor(
|
|||
|
||||
var hasDefinedCount : Boolean
|
||||
get() = mHasDefinedDefaultComponentCount
|
||||
set(value){
|
||||
set(value) {
|
||||
mHasDefinedDefaultComponentCount = value
|
||||
}
|
||||
|
||||
private fun checkOverflowForUnsignedShort(value : IntArray) : Boolean {
|
||||
for(v in value) {
|
||||
if(v > UNSIGNED_SHORT_MAX || v < 0) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
private fun checkOverflowForUnsignedShort(value : IntArray) : Boolean =
|
||||
null != value.find { it !in 0 .. UNSIGNED_SHORT_MAX }
|
||||
|
||||
private fun checkOverflowForUnsignedLong(value : LongArray) : Boolean {
|
||||
for(v in value) {
|
||||
if(v < 0 || v > UNSIGNED_LONG_MAX) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
private fun checkOverflowForUnsignedLong(value : LongArray) : Boolean =
|
||||
null != value.find { it !in 0 .. UNSIGNED_LONG_MAX }
|
||||
|
||||
private fun checkOverflowForUnsignedLong(value : IntArray) : Boolean {
|
||||
for(v in value) {
|
||||
if(v < 0) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
private fun checkOverflowForUnsignedLong(value : IntArray) : Boolean =
|
||||
null != value.find { it < 0 }
|
||||
|
||||
private fun checkOverflowForUnsignedRational(value : Array<Rational>) : Boolean {
|
||||
for(v in value) {
|
||||
if(v.numerator < 0 || v.denominator < 0 || v.numerator > UNSIGNED_LONG_MAX || v.denominator > UNSIGNED_LONG_MAX) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
private fun checkOverflowForUnsignedRational(value : Array<Rational>) : Boolean =
|
||||
null != value.find { it.numerator !in 0 .. UNSIGNED_LONG_MAX || it.denominator !in 0 .. UNSIGNED_LONG_MAX }
|
||||
|
||||
private fun checkOverflowForRational(value : Array<Rational>) : Boolean {
|
||||
for(v in value) {
|
||||
if(v.numerator < LONG_MIN || v.denominator < LONG_MIN || v.numerator > LONG_MAX || v.denominator > LONG_MAX) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
private fun checkOverflowForRational(value : Array<Rational>) : Boolean =
|
||||
null != value.find { it.numerator !in LONG_MIN .. LONG_MAX || it.denominator !in LONG_MIN .. LONG_MAX }
|
||||
|
||||
override fun hashCode() : Int {
|
||||
var result = tagId.toInt()
|
||||
|
@ -766,8 +724,6 @@ open class ExifTag internal constructor(
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* The BYTE type in the EXIF standard. An 8-bit unsigned integer.
|
||||
|
@ -832,11 +788,11 @@ open class ExifTag internal constructor(
|
|||
*/
|
||||
fun isValidIfd(ifdId : Int) : Boolean =
|
||||
when(ifdId) {
|
||||
IfdId.TYPE_IFD_0,
|
||||
IfdId.TYPE_IFD_1,
|
||||
IfdId.TYPE_IFD_EXIF,
|
||||
IfdId.TYPE_IFD_INTEROPERABILITY,
|
||||
IfdId.TYPE_IFD_GPS -> true
|
||||
IfdData.TYPE_IFD_0,
|
||||
IfdData.TYPE_IFD_1,
|
||||
IfdData.TYPE_IFD_EXIF,
|
||||
IfdData.TYPE_IFD_INTEROPERABILITY,
|
||||
IfdData.TYPE_IFD_GPS -> true
|
||||
else -> false
|
||||
}
|
||||
|
||||
|
@ -883,11 +839,4 @@ open class ExifTag internal constructor(
|
|||
else -> ""
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* Equivalent to setValue(value, 0, value.length).
|
||||
*/
|
||||
/**
|
||||
* Equivalent to getBytes(buffer, 0, buffer.length).
|
||||
*/
|
||||
|
|
|
@ -18,35 +18,33 @@ package it.sephiroth.android.library.exif2
|
|||
|
||||
import java.util.HashMap
|
||||
|
||||
|
||||
/**
|
||||
* The constants of the IFD ID defined in EXIF spec.
|
||||
*/
|
||||
object IfdId {
|
||||
const val TYPE_IFD_0 = 0
|
||||
const val TYPE_IFD_1 = 1
|
||||
const val TYPE_IFD_EXIF = 2
|
||||
const val TYPE_IFD_INTEROPERABILITY = 3
|
||||
const val TYPE_IFD_GPS = 4
|
||||
/* This is used in ExifData to allocate enough IfdData */
|
||||
const val TYPE_IFD_COUNT = 5
|
||||
|
||||
val list = intArrayOf(
|
||||
TYPE_IFD_0,
|
||||
TYPE_IFD_1,
|
||||
TYPE_IFD_EXIF,
|
||||
TYPE_IFD_INTEROPERABILITY,
|
||||
TYPE_IFD_GPS
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// This class stores all the tags in an IFD.
|
||||
// an IfdData with given IFD ID.
|
||||
internal class IfdData(
|
||||
// the ID of this IFD.
|
||||
val id : Int
|
||||
val id : Int // the ID of this IFD.
|
||||
) {
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* The constants of the IFD ID defined in EXIF spec.
|
||||
*/
|
||||
const val TYPE_IFD_0 = 0
|
||||
const val TYPE_IFD_1 = 1
|
||||
const val TYPE_IFD_EXIF = 2
|
||||
const val TYPE_IFD_INTEROPERABILITY = 3
|
||||
const val TYPE_IFD_GPS = 4
|
||||
/* This is used in ExifData to allocate enough IfdData */
|
||||
const val TYPE_IFD_COUNT = 5
|
||||
|
||||
val list = intArrayOf(
|
||||
TYPE_IFD_0,
|
||||
TYPE_IFD_1,
|
||||
TYPE_IFD_EXIF,
|
||||
TYPE_IFD_INTEROPERABILITY,
|
||||
TYPE_IFD_GPS
|
||||
)
|
||||
}
|
||||
|
||||
private val mExifTags = HashMap<Short, ExifTag>()
|
||||
|
||||
// the offset of next IFD.
|
||||
|
@ -56,9 +54,9 @@ internal class IfdData(
|
|||
val tagCount : Int
|
||||
get() = mExifTags.size
|
||||
|
||||
// a array the contains all [ExifTag] in this IFD.
|
||||
val allTags : Array<ExifTag>
|
||||
get() = mExifTags.values.toTypedArray()
|
||||
// Collection the contains all [ExifTag] in this IFD.
|
||||
val allTagsCollection : Collection<ExifTag>
|
||||
get() = mExifTags.values
|
||||
|
||||
// checkCollision
|
||||
fun contains(tagId : Short) : Boolean {
|
||||
|
@ -87,19 +85,12 @@ internal class IfdData(
|
|||
* IFDs offset or thumbnail offset will be ignored.
|
||||
*/
|
||||
override fun equals(other : Any?) : Boolean {
|
||||
if(other === null) return false
|
||||
if(other === this) return true
|
||||
if(other is IfdData) {
|
||||
if(other === this) return true
|
||||
if(other.id == id && other.tagCount == tagCount) {
|
||||
val tags = other.allTags
|
||||
for(tag in tags) {
|
||||
if(ExifInterface.isOffsetTag(tag.tagId)) {
|
||||
continue
|
||||
}
|
||||
val tag2 = mExifTags[tag.tagId]
|
||||
if(tag != tag2) {
|
||||
return false
|
||||
}
|
||||
for(tag in other.allTagsCollection) {
|
||||
if(ExifInterface.isOffsetTag(tag.tagId)) continue
|
||||
if(tag != mExifTags[tag.tagId]) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ class Rational(
|
|||
) {
|
||||
|
||||
// copy from a Rational.
|
||||
@Suppress("unused")
|
||||
constructor(r : Rational) : this(
|
||||
numerator = r.numerator,
|
||||
denominator = r.denominator
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package it.sephiroth.android.library.exif2
|
||||
package it.sephiroth.android.library.exif2.utils
|
||||
|
||||
import java.io.EOFException
|
||||
import java.io.FilterInputStream
|
|
@ -14,8 +14,9 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package it.sephiroth.android.library.exif2
|
||||
package it.sephiroth.android.library.exif2.utils
|
||||
|
||||
import it.sephiroth.android.library.exif2.Rational
|
||||
import java.io.FilterOutputStream
|
||||
import java.io.IOException
|
||||
import java.io.OutputStream
|
|
@ -0,0 +1,7 @@
|
|||
package it.sephiroth.android.library.exif2.utils
|
||||
|
||||
internal fun <E> Collection<E>?.notEmpty() : Collection<E>? =
|
||||
if(this?.isNotEmpty() == true) this else null
|
||||
|
||||
internal fun <E> List<E>?.notEmpty() : List<E>? =
|
||||
if(this?.isNotEmpty() == true) this else null
|
|
@ -2,8 +2,7 @@ package it.sephiroth.android.library.exif2
|
|||
|
||||
import android.util.Log
|
||||
import android.util.SparseIntArray
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Assert.*
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
|
@ -12,7 +11,7 @@ class Test1 {
|
|||
|
||||
@Test
|
||||
fun testLog() {
|
||||
Log.v("TEST","test")
|
||||
Log.v("TEST", "test")
|
||||
assertTrue("using android.util.Log", true)
|
||||
}
|
||||
|
||||
|
@ -32,29 +31,48 @@ class Test1 {
|
|||
}
|
||||
}
|
||||
|
||||
private fun getOrientation(fileName : String) : Pair<Int?,Throwable?> =
|
||||
try{
|
||||
val o = FileInputStream(getFile(fileName)).use { inStream ->
|
||||
ExifInterface()
|
||||
.apply {
|
||||
readExif(
|
||||
inStream,
|
||||
ExifInterface.Options.OPTION_IFD_0
|
||||
or ExifInterface.Options.OPTION_IFD_1
|
||||
or ExifInterface.Options.OPTION_IFD_EXIF
|
||||
)
|
||||
}
|
||||
.getTagIntValue(ExifInterface.TAG_ORIENTATION)
|
||||
private fun getOrientation(fileName : String) : Pair<Int?, Throwable?> =
|
||||
try {
|
||||
val o = FileInputStream(getFile(fileName)).use { inStream ->
|
||||
ExifInterface()
|
||||
.apply {
|
||||
readExif(
|
||||
inStream,
|
||||
ExifInterface.Options.OPTION_IFD_0
|
||||
or ExifInterface.Options.OPTION_IFD_1
|
||||
or ExifInterface.Options.OPTION_IFD_EXIF
|
||||
)
|
||||
}
|
||||
.getTagIntValue(ExifInterface.TAG_ORIENTATION)
|
||||
}
|
||||
Pair(o, null)
|
||||
} catch(ex : Throwable) {
|
||||
Pair(null, ex)
|
||||
}
|
||||
|
||||
private fun getThumbnailBytes(fileName : String) : Pair<ByteArray?, Throwable?> =
|
||||
try {
|
||||
val o = FileInputStream(getFile(fileName)).use { inStream ->
|
||||
ExifInterface()
|
||||
.apply {
|
||||
readExif(
|
||||
inStream,
|
||||
ExifInterface.Options.OPTION_IFD_0
|
||||
or ExifInterface.Options.OPTION_IFD_1
|
||||
or ExifInterface.Options.OPTION_IFD_EXIF
|
||||
)
|
||||
}
|
||||
.thumbnailBytes
|
||||
}
|
||||
Pair(o, null)
|
||||
} catch(ex : Throwable) {
|
||||
Pair(null, ex)
|
||||
}
|
||||
Pair(o,null)
|
||||
}catch(ex:Throwable){
|
||||
Pair(null,ex)
|
||||
}
|
||||
|
||||
private fun testNotJpegSub(fileName : String) {
|
||||
val(o,ex) = getOrientation(fileName)
|
||||
assertTrue("testNotJpegSub",o ==null && ex!=null)
|
||||
if( ex!= null) println("exception raised: ${ex::class.java} ${ex.message}")
|
||||
val (o, ex) = getOrientation(fileName)
|
||||
assertTrue("testNotJpegSub", o == null && ex != null)
|
||||
if(ex != null) println("exception raised: ${ex::class.java} ${ex.message}")
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -66,23 +84,32 @@ class Test1 {
|
|||
|
||||
@Test
|
||||
fun testJpeg() {
|
||||
var fileName :String
|
||||
var rv : Pair<Int?,Throwable?>
|
||||
|
||||
// this file has orientation 1
|
||||
fileName = "test1.jpg"
|
||||
rv = getOrientation(fileName)
|
||||
assertEquals(fileName,1,rv.first)
|
||||
|
||||
// this file has no orientation, it raises exception.
|
||||
fileName = "test2.jpg"
|
||||
rv = getOrientation(fileName)
|
||||
assertTrue(fileName,rv.second != null)
|
||||
var fileName : String
|
||||
var rvO : Pair<Int?, Throwable?>
|
||||
var rvT : Pair<ByteArray?, Throwable?>
|
||||
|
||||
// this file has orientation 6.
|
||||
fileName = "test3.jpg"
|
||||
rv = getOrientation(fileName)
|
||||
assertEquals(fileName,rv.first , 6)
|
||||
rvO = getOrientation(fileName)
|
||||
assertEquals(fileName, 6, rvO.first)
|
||||
rvT = getThumbnailBytes(fileName)
|
||||
assertNull(fileName,rvT.first)
|
||||
|
||||
// this file has orientation 1
|
||||
fileName = "test1.jpg"
|
||||
rvO = getOrientation(fileName)
|
||||
assertEquals(fileName, 1, rvO.first)
|
||||
rvT = getThumbnailBytes(fileName)
|
||||
assertNull(fileName,rvT.first)
|
||||
|
||||
// this file has no orientation, it raises exception.
|
||||
fileName = "test2.jpg"
|
||||
rvO = getOrientation(fileName)
|
||||
assertNotNull(fileName, rvO.second) // <java.lang.IllegalStateException: stop before hitting compressed data>
|
||||
|
||||
rvT = getThumbnailBytes(fileName)
|
||||
assertNotNull(fileName,rvT.second) // <java.lang.IllegalStateException: stop before hitting compressed data>
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue