? =
- getTag(tagId, ifdId)?.valueAsRationals
-
- // single value
-
- fun getTagLongValue(tagId : Int, ifdId : Int = getDefinedTagDefaultIfd(tagId)) : Long? =
- getTagLongValues(tagId, ifdId)?.firstOrNull()
-
- fun getTagIntValue(tagId : Int, ifdId : Int = getDefinedTagDefaultIfd(tagId)) : Int? =
- getTagIntValues(tagId, ifdId)?.firstOrNull()
-
- private fun getTagByteValue(tagId : Int, ifdId : Int = getDefinedTagDefaultIfd(tagId)) : Byte? =
- getTagByteValues(tagId, ifdId)?.firstOrNull()
-
- private fun getTagRationalValue(
- tagId : Int,
- ifdId : Int = getDefinedTagDefaultIfd(tagId)
- ) : Rational? =
- getTagRationalValues(tagId, ifdId)?.firstOrNull()
-
- /**
- * Checks whether a tag has a defined number of elements.
- *
- * @param tagId a defined tag constant, e.g. [.TAG_IMAGE_WIDTH].
- * @return true if the tag has a defined number of elements.
- */
- fun isTagCountDefined(tagId : Int) : Boolean {
- val info = tagInfo.get(tagId)
- // No value in info can be zero, as all tags have a non-zero type
- return info != 0 && getComponentCountFromInfo(info) != ExifTag.SIZE_UNDEFINED
- }
-
- /**
- * Gets the defined number of elements for a tag.
- *
- * @param tagId a defined tag constant, e.g. [.TAG_IMAGE_WIDTH].
- * @return the number of elements or [ExifTag.SIZE_UNDEFINED] if the
- * tag or the number of elements is not defined.
- */
- fun getDefinedTagCount(tagId : Int) : Int =
- when(val info = tagInfo.get(tagId)) {
- 0 -> ExifTag.SIZE_UNDEFINED
- else -> getComponentCountFromInfo(info)
- }
-
- /**
- * Gets the number of elements for an ExifTag in a given IFD.
- *
- * @param tagId a defined tag constant, e.g. [.TAG_IMAGE_WIDTH].
- * @param ifdId the IFD containing the ExifTag to check.
- * @return the number of elements in the ExifTag, if the tag's size is
- * undefined this will return the actual number of elements that is
- * in the ExifTag's value.
- */
- fun getActualTagCount(tagId : Int, ifdId : Int) : Int {
- val t = getTag(tagId, ifdId) ?: return 0
- return t.componentCount
- }
-
- // Gets the defined type for a tag.
- // tagId : a defined tag constant, e.g. [.TAG_IMAGE_WIDTH].
- fun getDefinedTagType(tagId : Int) : Short {
- val info = tagInfo.get(tagId)
- return if(info == 0) - 1 else getTypeFromInfo(info)
- }
-
- fun buildUninitializedTag(tagId : Int) : ExifTag? {
- val info = tagInfo.get(tagId)
- if(info == 0) {
- return null
- }
- val type = getTypeFromInfo(info)
- val definedCount = getComponentCountFromInfo(info)
- val hasDefinedCount = definedCount != ExifTag.SIZE_UNDEFINED
- val ifdId = getTrueIfd(tagId)
- return ExifTag(getTrueTagKey(tagId), type, definedCount, ifdId, hasDefinedCount)
- }
-
- /**
- * Sets the value of an ExifTag if it exists in the given IFD. The value
- * must be the correct type and length for that ExifTag.
- *
- * @param tagId a tag constant, e.g. [.TAG_IMAGE_WIDTH].
- * @param ifdId the IFD that the ExifTag is in.
- * @param tagValue the value to set.
- * @return true if success, false if the ExifTag doesn't exist or the value
- * is the wrong type/length.
- * @see .setTagValue
- */
- private fun setTagValue(
- tagId : Int,
- tagValue : Any,
- ifdId : Int = getDefinedTagDefaultIfd(tagId)
- ) : Boolean =
- getTag(tagId, ifdId)?.setValueAny(tagValue) ?: false
-
- /**
- * Removes the ExifTag for a tag constant from the given IFD.
- *
- * @param tagId a tag constant, e.g. [.TAG_IMAGE_WIDTH].
- * @param ifdId the IFD of the ExifTag to remove.
- */
- private fun deleteTag(
- tagId : Int,
- ifdId : Int = getDefinedTagDefaultIfd(tagId)
- ) : ExifInterface {
- mData.removeTag(getTrueTagKey(tagId), ifdId)
- return this
- }
-
- /**
- * Creates a new tag definition in this ExifInterface object for a given TID
- * and default IFD. Creating a definition with the same TID and default IFD
- * as a previous definition will override it.
- *
- * @param tagId the TID for the tag.
- * @param defaultIfd the default IFD for the tag.
- * @param tagType the type of the tag
- * @param defaultComponentCount the number of elements of this tag's type in
- * the tags value.
- * @param allowedIfds the IFD's this tag is allowed to be put in.
- * @return the defined tag constant (e.g. [.TAG_IMAGE_WIDTH]) or
- * [.TAG_NULL] if the definition could not be made.
- */
- fun setTagDefinition(
- tagId : Short,
- defaultIfd : Int,
- tagType : Short,
- defaultComponentCount : Short,
- allowedIfds : IntArray
- ) : Int {
- if(sBannedDefines.contains(tagId)) {
- return TAG_NULL
- }
- if(ExifTag.isValidType(tagType) && ExifTag.isValidIfd(defaultIfd)) {
- val tagDef = defineTag(defaultIfd, tagId)
- if(tagDef == TAG_NULL) {
- return TAG_NULL
- }
- val otherDefs = getTagDefinitionsForTagId(tagId)
- val infos = tagInfo
- // Make sure defaultIfd is in allowedIfds
- var defaultCheck = false
- for(i in allowedIfds) {
- if(defaultIfd == i) {
- defaultCheck = true
- }
- if(! ExifTag.isValidIfd(i)) {
- return TAG_NULL
- }
- }
- if(! defaultCheck) {
- return TAG_NULL
- }
-
- val ifdFlags = getFlagsFromAllowedIfds(allowedIfds)
- // Make sure no identical tags can exist in allowedIfds
- if(otherDefs != null) {
- for(def in otherDefs) {
- val tagInfo = infos.get(def)
- val allowedFlags = getAllowedIfdFlagsFromInfo(tagInfo)
- if(ifdFlags and allowedFlags != 0) {
- return TAG_NULL
- }
- }
- }
- tagInfo.put(
- tagDef,
- ifdFlags shl 24 or (tagType shl 16) or defaultComponentCount.toInt()
- )
- return tagDef
- }
- return TAG_NULL
- }
-
- fun getTagDefinition(tagId : Short, defaultIfd : Int) : Int =
- tagInfo.get(defineTag(defaultIfd, tagId))
-
- private fun getTagDefinitionsForTagId(tagId : Short) : IntArray? {
- val ifds = IfdData.list
- val defs = IntArray(ifds.size)
- var counter = 0
- for(i in ifds) {
- val def = defineTag(i, tagId)
- if(tagInfo.get(def) != DEFINITION_NULL) {
- defs[counter ++] = def
- }
- }
- return if(counter == 0) null else defs.copyOfRange(0, counter)
-
- }
-
- fun getTagDefinitionForTag(tag : ExifTag) : Int =
- getTagDefinitionForTag(tag.tagId, tag.dataType, tag.componentCount, tag.ifd)
-
- private fun getTagDefinitionForTag(
- tagId : Short,
- type : Short,
- count : Int,
- ifd : Int
- ) : Int {
- getTagDefinitionsForTagId(tagId)?.forEach { i ->
- val info = tagInfo.get(i)
- val def_type = getTypeFromInfo(info)
- val def_count = getComponentCountFromInfo(info)
- val def_ifds = getAllowedIfdsFromInfo(info)
- var valid_ifd = false
- if(def_ifds != null) {
- for(j in def_ifds) {
- if(j == ifd) {
- valid_ifd = true
- break
- }
- }
- }
- if(valid_ifd
- && type == def_type
- && (count == def_count || def_count == ExifTag.SIZE_UNDEFINED)
- ) {
- return i
- }
- }
- return TAG_NULL
- }
-
- /**
- * Removes a tag definition for given defined tag constant.
- *
- * @param tagId a defined tag constant, e.g. [.TAG_IMAGE_WIDTH].
- */
- fun removeTagDefinition(tagId : Int) {
- tagInfo.delete(tagId)
- }
-
- // /**
- // * Resets tag definitions to the default ones.
- // */
- // fun resetTagDefinitions() {
- // mTagInfo = null
- // }
-
- /**
- * Sets the thumbnail to be a jpeg compressed bitmap. Clears any prior
- * thumbnail.
- *
- * @param thumb a bitmap to compress to a jpeg thumbnail.
- * @return true if the thumbnail was set.
- */
- fun setCompressedThumbnail(thumb : Bitmap) : Boolean {
- val thumbnail = ByteArrayOutputStream()
- return if(! thumb.compress(Bitmap.CompressFormat.JPEG, 90, thumbnail)) {
- false
- } else setCompressedThumbnail(thumbnail.toByteArray())
- }
-
- /**
- * Sets the thumbnail to be a jpeg compressed image. Clears any prior
- * thumbnail.
- *
- * @param thumb a byte array containing a jpeg compressed image.
- * @return true if the thumbnail was set.
- */
- private fun setCompressedThumbnail(thumb : ByteArray) : Boolean {
- mData.clearThumbnailAndStrips()
- mData.compressedThumbnail = thumb
- return true
- }
-
- /**
- * Clears the compressed thumbnail if it exists.
- */
- fun removeCompressedThumbnail() {
- mData.compressedThumbnail = null
- }
-
- /**
- * Return the altitude in meters. If the exif tag does not exist, return
- * defaultValue.
- *
- * @param defaultValue the value to return if the tag is not available.
- */
- fun getAltitude(defaultValue : Double) : Double {
-
- val ref = getTagByteValue(TAG_GPS_ALTITUDE_REF)
- val gpsAltitude = getTagRationalValue(TAG_GPS_ALTITUDE)
-
- var seaLevel = 1
- if(null != ref) {
- seaLevel = if(ref == 1.toByte()) - 1 else 1
- }
-
- return if(gpsAltitude != null) {
- gpsAltitude.toDouble() * seaLevel
- } else defaultValue
-
- }
-
- /**
- * Creates, formats, and sets the DateTimeStamp tag for one of:
- * [.TAG_DATE_TIME], [.TAG_DATE_TIME_DIGITIZED],
- * [.TAG_DATE_TIME_ORIGINAL].
- *
- * @param tagId one of the DateTimeStamp tags.
- * @param timestamp a timestamp to format.
- * @param timezone a TimeZone object.
- * @return true if success, false if the tag could not be set.
- */
- fun addDateTimeStampTag(tagId : Int, timestamp : Long, timezone : TimeZone) : Boolean {
- if(tagId == TAG_DATE_TIME || tagId == TAG_DATE_TIME_DIGITIZED || tagId == TAG_DATE_TIME_ORIGINAL) {
- mDateTimeStampFormat.timeZone = timezone
- val t = buildTag(tagId, mDateTimeStampFormat.format(timestamp)) ?: return false
- setTag(t)
- } else {
- return false
- }
- return true
- }
-
- /**
- * Creates a tag for a defined tag constant in a given IFD if that IFD is
- * allowed for the tag. This method will fail anytime the appropriate
- * [ExifTag.setValue] for this tag's datatype would fail.
- *
- * @param tagId a tag constant, e.g. [.TAG_IMAGE_WIDTH].
- * @param ifdId the IFD that the tag should be in.
- * @param tagValue the value of the tag to set.
- * @return an ExifTag object or null if one could not be constructed.
- * @see .buildTag
- */
- fun buildTag(tagId : Int, tagValue : Any, ifdId : Int = getTrueIfd(tagId)) : ExifTag? {
- val info = tagInfo.get(tagId)
- if(info == 0 || ! isIfdAllowed(info, ifdId)) return null
-
- val definedCount = getComponentCountFromInfo(info)
-
- val t = ExifTag(
- tagId = getTrueTagKey(tagId),
- dataType = getTypeFromInfo(info),
- componentCount = definedCount,
- ifd = ifdId,
- mHasDefinedDefaultComponentCount = definedCount != ExifTag.SIZE_UNDEFINED
- )
-
- return when {
- t.setValueAny(tagValue) -> t
- else -> null
- }
- }
-
- /**
- * Creates and sets all to the GPS tags for a give latitude and longitude.
- *
- * @param latitude a GPS latitude coordinate.
- * @param longitude a GPS longitude coordinate.
- * @return true if success, false if they could not be created or set.
- */
- fun addGpsTags(latitude : Double, longitude : Double) : Boolean {
- val latTag = buildTag(TAG_GPS_LATITUDE, toExifLatLong(latitude))
- val longTag = buildTag(TAG_GPS_LONGITUDE, toExifLatLong(longitude))
- val latRefTag = buildTag(
- TAG_GPS_LATITUDE_REF,
- if(latitude >= 0) GpsLatitudeRef.NORTH else GpsLatitudeRef.SOUTH
- )
- val longRefTag = buildTag(
- TAG_GPS_LONGITUDE_REF,
- if(longitude >= 0) GpsLongitudeRef.EAST else GpsLongitudeRef.WEST
- )
- if(latTag == null || longTag == null || latRefTag == null || longRefTag == null) {
- return false
- }
- setTag(latTag)
- setTag(longTag)
- setTag(latRefTag)
- setTag(longRefTag)
- return true
- }
-
- /**
- * Creates and sets the GPS timestamp tag.
- *
- * @param timestamp a GPS timestamp.
- * @return true if success, false if could not be created or set.
- */
- fun addGpsDateTimeStampTag(timestamp : Long) : Boolean {
- var t = buildTag(TAG_GPS_DATE_STAMP, mGPSDateStampFormat.format(timestamp)) ?: return false
- setTag(t)
- mGPSTimeStampCalendar.timeInMillis = timestamp
- t = buildTag(
- TAG_GPS_TIME_STAMP,
- arrayOf(
- Rational(mGPSTimeStampCalendar.get(Calendar.HOUR_OF_DAY).toLong(), 1),
- Rational(mGPSTimeStampCalendar.get(Calendar.MINUTE).toLong(), 1),
- Rational(mGPSTimeStampCalendar.get(Calendar.SECOND).toLong(), 1)
- )
- ) ?: return false
- setTag(t)
- return true
- }
-
- /**
- * Constants for [.TAG_ORIENTATION]. They can be interpreted as
- * follows:
- *
- * * TOP_LEFT is the normal orientation.
- * * TOP_RIGHT is a left-right mirror.
- * * BOTTOM_LEFT is a 180 degree rotation.
- * * BOTTOM_RIGHT is a top-bottom mirror.
- * * LEFT_TOP is mirrored about the top-left<->bottom-right axis.
- * * RIGHT_TOP is a 90 degree clockwise rotation.
- * * LEFT_BOTTOM is mirrored about the top-right<->bottom-left axis.
- * * RIGHT_BOTTOM is a 270 degree clockwise rotation.
- *
- */
- object Orientation {
-
- const val TOP_LEFT : Short = 1
- const val TOP_RIGHT : Short = 2
- const val BOTTOM_RIGHT : Short = 3
- const val BOTTOM_LEFT : Short = 4
- const val LEFT_TOP : Short = 5
- const val RIGHT_TOP : Short = 6
- const val RIGHT_BOTTOM : Short = 7
- const val LEFT_BOTTOM : Short = 8
- }
-
- /**
- * Constants for [.TAG_Y_CB_CR_POSITIONING]
- */
- object YCbCrPositioning {
-
- const val CENTERED : Short = 1
- const val CO_SITED : Short = 2
- }
-
- /**
- * Constants for [.TAG_COMPRESSION]
- */
- object Compression {
-
- const val UNCOMPRESSION : Short = 1
- const val JPEG : Short = 6
- }
-
- /**
- * Constants for [.TAG_RESOLUTION_UNIT]
- */
- object ResolutionUnit {
-
- const val INCHES : Short = 2
- const val CENTIMETERS : Short = 3
- const val MILLIMETERS : Short = 4
- const val MICROMETERS : Short = 5
- }
-
- /**
- * Constants for [.TAG_PHOTOMETRIC_INTERPRETATION]
- */
- object PhotometricInterpretation {
-
- const val RGB : Short = 2
- const val YCBCR : Short = 6
- }
-
- /**
- * Constants for [.TAG_PLANAR_CONFIGURATION]
- */
- object PlanarConfiguration {
-
- const val CHUNKY : Short = 1
- const val PLANAR : Short = 2
- }
-
- // Convenience methods:
-
- /**
- * Constants for [.TAG_EXPOSURE_PROGRAM]
- */
- object ExposureProgram {
-
- const val NOT_DEFINED : Short = 0
- const val MANUAL : Short = 1
- const val NORMAL_PROGRAM : Short = 2
- const val APERTURE_PRIORITY : Short = 3
- const val SHUTTER_PRIORITY : Short = 4
- const val CREATIVE_PROGRAM : Short = 5
- const val ACTION_PROGRAM : Short = 6
- const val PROTRAIT_MODE : Short = 7
- const val LANDSCAPE_MODE : Short = 8
- }
-
- /**
- * Constants for [.TAG_METERING_MODE]
- */
- object MeteringMode {
-
- const val UNKNOWN : Short = 0
- const val AVERAGE : Short = 1
- const val CENTER_WEIGHTED_AVERAGE : Short = 2
- const val SPOT : Short = 3
- const val MULTISPOT : Short = 4
- const val PATTERN : Short = 5
- const val PARTAIL : Short = 6
- const val OTHER : Short = 255
- }
-
- /**
- * Constants for [.TAG_FLASH] As the definition in Jeita EXIF 2.2
- */
- object Flash {
-
- /**
- * first bit
- */
- enum class FlashFired {
-
- NO, YES
- }
-
- /**
- * Values for bits 1 and 2 indicating the status of returned light
- */
- enum class StrobeLightDetection {
-
- NO_DETECTION, RESERVED, LIGHT_NOT_DETECTED, LIGHT_DETECTED
- }
-
- /**
- * Values for bits 3 and 4 indicating the camera's flash mode
- */
- enum class CompulsoryMode {
-
- UNKNOWN,
- FIRING,
- SUPPRESSION,
- AUTO
- }
-
- /**
- * Values for bit 5 indicating the presence of a flash function.
- */
- enum class FlashFunction {
-
- FUNCTION_PRESENT,
- FUNCTION_NOR_PRESENT
- }
-
- /**
- * Values for bit 6 indicating the camera's red-eye mode.
- */
- enum class RedEyeMode {
-
- NONE,
- SUPPORTED
- }
- }
-
- /**
- * Constants for [.TAG_COLOR_SPACE]
- */
- object ColorSpace {
-
- const val SRGB : Short = 1
- const val UNCALIBRATED = 0xFFFF.toShort()
- }
-
- /**
- * Constants for [.TAG_EXPOSURE_MODE]
- */
- object ExposureMode {
-
- const val AUTO_EXPOSURE : Short = 0
- const val MANUAL_EXPOSURE : Short = 1
- const val AUTO_BRACKET : Short = 2
- }
-
- /**
- * Constants for [.TAG_WHITE_BALANCE]
- */
- object WhiteBalance {
-
- const val AUTO : Short = 0
- const val MANUAL : Short = 1
- }
-
- /**
- * Constants for [.TAG_SCENE_CAPTURE_TYPE]
- */
- object SceneCapture {
-
- const val STANDARD : Short = 0
- const val LANDSCAPE : Short = 1
- const val PROTRAIT : Short = 2
- const val NIGHT_SCENE : Short = 3
- }
-
- /**
- * Constants for [.TAG_COMPONENTS_CONFIGURATION]
- */
- object ComponentsConfiguration {
-
- const val NOT_EXIST : Short = 0
- const val Y : Short = 1
- const val CB : Short = 2
- const val CR : Short = 3
- const val R : Short = 4
- const val G : Short = 5
- const val B : Short = 6
- }
-
- /**
- * Constants for [.TAG_LIGHT_SOURCE]
- */
- object LightSource {
-
- const val UNKNOWN : Short = 0
- const val DAYLIGHT : Short = 1
- const val FLUORESCENT : Short = 2
- const val TUNGSTEN : Short = 3
- const val FLASH : Short = 4
- const val FINE_WEATHER : Short = 9
- const val CLOUDY_WEATHER : Short = 10
- const val SHADE : Short = 11
- const val DAYLIGHT_FLUORESCENT : Short = 12
- const val DAY_WHITE_FLUORESCENT : Short = 13
- const val COOL_WHITE_FLUORESCENT : Short = 14
- const val WHITE_FLUORESCENT : Short = 15
- const val STANDARD_LIGHT_A : Short = 17
- const val STANDARD_LIGHT_B : Short = 18
- const val STANDARD_LIGHT_C : Short = 19
- const val D55 : Short = 20
- const val D65 : Short = 21
- const val D75 : Short = 22
- const val D50 : Short = 23
- const val ISO_STUDIO_TUNGSTEN : Short = 24
- const val OTHER : Short = 255
- }
-
- /**
- * Constants for [.TAG_SENSING_METHOD]
- */
- object SensingMethod {
-
- const val NOT_DEFINED : Short = 1
- const val ONE_CHIP_COLOR : Short = 2
- const val TWO_CHIP_COLOR : Short = 3
- const val THREE_CHIP_COLOR : Short = 4
- const val COLOR_SEQUENTIAL_AREA : Short = 5
- const val TRILINEAR : Short = 7
- const val COLOR_SEQUENTIAL_LINEAR : Short = 8
- }
-
- /**
- * Constants for [.TAG_FILE_SOURCE]
- */
- object FileSource {
-
- const val DSC : Short = 3
- }
-
- /**
- * Constants for [.TAG_SCENE_TYPE]
- */
- object SceneType {
-
- const val DIRECT_PHOTOGRAPHED : Short = 1
- }
-
- /**
- * Constants for [.TAG_GAIN_CONTROL]
- */
- object GainControl {
-
- const val NONE : Short = 0
- const val LOW_UP : Short = 1
- const val HIGH_UP : Short = 2
- const val LOW_DOWN : Short = 3
- const val HIGH_DOWN : Short = 4
- }
-
- /**
- * Constants for [.TAG_CONTRAST]
- */
- object Contrast {
-
- const val NORMAL : Short = 0
- const val SOFT : Short = 1
- const val HARD : Short = 2
- }
-
- /**
- * Constants for [.TAG_SATURATION]
- */
- object Saturation {
-
- const val NORMAL : Short = 0
- const val LOW : Short = 1
- const val HIGH : Short = 2
- }
-
- /**
- * Constants for [.TAG_SHARPNESS]
- */
- object Sharpness {
-
- const val NORMAL : Short = 0
- const val SOFT : Short = 1
- const val HARD : Short = 2
- }
-
- /**
- * Constants for [.TAG_SUBJECT_DISTANCE]
- */
- object SubjectDistance {
-
- const val UNKNOWN : Short = 0
- const val MACRO : Short = 1
- const val CLOSE_VIEW : Short = 2
- const val DISTANT_VIEW : Short = 3
- }
-
- /**
- * Constants for [.TAG_GPS_LATITUDE_REF],
- * [.TAG_GPS_DEST_LATITUDE_REF]
- */
- object GpsLatitudeRef {
-
- const val NORTH = "N"
- const val SOUTH = "S"
- }
-
- /**
- * Constants for [.TAG_GPS_LONGITUDE_REF],
- * [.TAG_GPS_DEST_LONGITUDE_REF]
- */
- object GpsLongitudeRef {
-
- const val EAST = "E"
- const val WEST = "W"
- }
-
- /**
- * Constants for [.TAG_GPS_ALTITUDE_REF]
- */
- object GpsAltitudeRef {
-
- const val SEA_LEVEL : Short = 0
- const val SEA_LEVEL_NEGATIVE : Short = 1
- }
-
- /**
- * Constants for [.TAG_GPS_STATUS]
- */
- object GpsStatus {
-
- const val IN_PROGRESS = "A"
- const val INTEROPERABILITY = "V"
- }
-
- /**
- * Constants for [.TAG_GPS_MEASURE_MODE]
- */
- object GpsMeasureMode {
-
- const val MODE_2_DIMENSIONAL = "2"
- const val MODE_3_DIMENSIONAL = "3"
- }
-
- /**
- * Constants for [.TAG_GPS_SPEED_REF],
- * [.TAG_GPS_DEST_DISTANCE_REF]
- */
- object GpsSpeedRef {
-
- const val KILOMETERS = "K"
- const val MILES = "M"
- const val KNOTS = "N"
- }
-
- /**
- * Constants for [.TAG_GPS_TRACK_REF],
- * [.TAG_GPS_IMG_DIRECTION_REF], [.TAG_GPS_DEST_BEARING_REF]
- */
- object GpsTrackRef {
-
- const val TRUE_DIRECTION = "T"
- const val MAGNETIC_DIRECTION = "M"
- }
-
- /**
- * Constants for [.TAG_GPS_DIFFERENTIAL]
- */
- object GpsDifferential {
-
- const val WITHOUT_DIFFERENTIAL_CORRECTION : Short = 0
- const val DIFFERENTIAL_CORRECTION_APPLIED : Short = 1
- }
-
- /**
- * Constants for the jpeg process algorithm used.
- *
- * @see .getJpegProcess
- */
- object JpegProcess {
-
- const val BASELINE = 0xFFC0.toShort()
- const val EXTENDED_SEQUENTIAL = 0xFFC1.toShort()
- const val PROGRESSIVE = 0xFFC2.toShort()
- const val LOSSLESS = 0xFFC3.toShort()
- const val DIFFERENTIAL_SEQUENTIAL = 0xFFC5.toShort()
- const val DIFFERENTIAL_PROGRESSIVE = 0xFFC6.toShort()
- const val DIFFERENTIAL_LOSSLESS = 0xFFC7.toShort()
- const val EXTENDED_SEQ_ARITHMETIC_CODING = 0xFFC9.toShort()
- const val PROGRESSIVE_AIRTHMETIC_CODING = 0xFFCA.toShort()
- const val LOSSLESS_AITHMETIC_CODING = 0xFFCB.toShort()
- const val DIFFERENTIAL_SEQ_ARITHMETIC_CODING = 0xFFCD.toShort()
- const val DIFFERENTIAL_PROGRESSIVE_ARITHMETIC_CODING = 0xFFCE.toShort()
- const val DIFFERENTIAL_LOSSLESS_ARITHMETIC_CODING = 0xFFCF.toShort()
- }
-
- /**
- * Constants for the [.TAG_SENSITIVITY_TYPE] tag
- */
- object SensitivityType {
-
-
- const val UNKNOWN : Short = 0
-
- /**
- * Standard output sensitivity
- */
- const val SOS : Short = 1
-
- /**
- * Recommended exposure index
- */
- const val REI : Short = 2
-
- /**
- * ISO Speed
- */
- const val ISO : Short = 3
-
- /**
- * Standard output sensitivity and Recommended output index
- */
- const val SOS_REI : Short = 4
-
- /**
- * Standard output sensitivity and ISO speed
- */
- const val SOS_ISO : Short = 5
-
- /**
- * Recommended output index and ISO Speed
- */
- const val REI_ISO : Short = 6
-
- /**
- * Standard output sensitivity and Recommended output index and ISO Speed
- */
- const val SOS_REI_ISO : Short = 7
- }
-
- /**
- * Options for calling [.readExif], [.readExif],
- * [.readExif]
- */
- object Options {
-
- /**
- * Option bit to request to parse IFD0.
- */
- const val OPTION_IFD_0 = 1
- /**
- * Option bit to request to parse IFD1.
- */
- const val OPTION_IFD_1 = 1 shl 1
- /**
- * Option bit to request to parse Exif-IFD.
- */
- const val OPTION_IFD_EXIF = 1 shl 2
- /**
- * Option bit to request to parse GPS-IFD.
- */
- const val OPTION_IFD_GPS = 1 shl 3
- /**
- * Option bit to request to parse Interoperability-IFD.
- */
- const val OPTION_IFD_INTEROPERABILITY = 1 shl 4
- /**
- * Option bit to request to parse thumbnail.
- */
- const val OPTION_THUMBNAIL = 1 shl 5
- /**
- * Option bit to request all the options
- */
- const val OPTION_ALL =
- OPTION_IFD_0 xor OPTION_IFD_1 xor OPTION_IFD_EXIF xor OPTION_IFD_GPS xor OPTION_IFD_INTEROPERABILITY xor OPTION_THUMBNAIL
-
- }
-
- @Suppress("unused")
- companion object {
-
- private const val TAG = "ExifInterface"
-
- const val TAG_NULL = - 1
- const val IFD_NULL = - 1
- const val DEFINITION_NULL = 0
-
- /**
- * Tag constants for Jeita EXIF 2.2
- */
-
- // IFD 0
- private val TAG_IMAGE_WIDTH = defineTag(IfdData.TYPE_IFD_0, 0x0100.toShort())
- private val TAG_IMAGE_LENGTH =
- defineTag(IfdData.TYPE_IFD_0, 0x0101.toShort()) // Image height
- private val TAG_BITS_PER_SAMPLE = defineTag(IfdData.TYPE_IFD_0, 0x0102.toShort())
-
- /**
- * Value is unsigned int.
- * (Read only tag) The compression scheme used for the image data. When a primary image is JPEG compressed, this designation is
- * not necessary and is omitted. When thumbnails use JPEG compression, this tag value is set to 6.
- *
- * * 1 = uncompressed
- * * 6 = JPEG compression (thumbnails only)
- * * Other = reserved
- */
- private val TAG_COMPRESSION = defineTag(IfdData.TYPE_IFD_0, 0x0103.toShort())
- private val TAG_PHOTOMETRIC_INTERPRETATION = defineTag(IfdData.TYPE_IFD_0, 0x0106.toShort())
- private val TAG_IMAGE_DESCRIPTION = defineTag(IfdData.TYPE_IFD_0, 0x010E.toShort())
-
- /**
- * Value is ascii string
- * The manufacturer of the recording equipment. This is the manufacturer of the DSC, scanner, video digitizer or other equipment
- * that generated the image. When the field is left blank, it is treated as unknown.
- */
- private val TAG_MAKE = defineTag(IfdData.TYPE_IFD_0, 0x010F.toShort())
-
- /**
- * Value is ascii string
- * The model name or model number of the equipment. This is the model name of number of the DSC, scanner, video digitizer or
- * other equipment that generated the image. When the field is left blank, it is treated as unknown.
- */
- private val TAG_MODEL = defineTag(IfdData.TYPE_IFD_0, 0x0110.toShort())
- val TAG_STRIP_OFFSETS = defineTag(IfdData.TYPE_IFD_0, 0x0111.toShort())
-
- /**
- * Value is int
- * The orientation of the camera relative to the scene, when the image was captured. The start point of stored data is:
- *
- * * '0' undefined
- * * '1' normal
- * * '2' flip horizontal
- * * '3' rotate 180
- * * '4' flip vertical
- * * '5' transpose, flipped about top-left <--> bottom-right axis
- * * '6' rotate 90 cw
- * * '7' transverse, flipped about top-right <--> bottom-left axis
- * * '8' rotate 270
- * * '9' undefined
- *
- */
- val TAG_ORIENTATION = defineTag(IfdData.TYPE_IFD_0, 0x0112.toShort())
- private val TAG_SAMPLES_PER_PIXEL = defineTag(IfdData.TYPE_IFD_0, 0x0115.toShort())
- private val TAG_ROWS_PER_STRIP = defineTag(IfdData.TYPE_IFD_0, 0x0116.toShort())
- val TAG_STRIP_BYTE_COUNTS = defineTag(IfdData.TYPE_IFD_0, 0x0117.toShort())
-
- private val TAG_INTEROP_VERSION =
- defineTag(IfdData.TYPE_IFD_INTEROPERABILITY, 0x0002.toShort())
-
- /**
- * Value is unsigned double.
- * Display/Print resolution of image. Large number of digicam uses 1/72inch, but it has no mean because personal computer doesn't
- * use this value to display/print out.
- */
- private val TAG_X_RESOLUTION = defineTag(IfdData.TYPE_IFD_0, 0x011A.toShort())
-
- /**
- * @see .TAG_X_RESOLUTION
- */
- private val TAG_Y_RESOLUTION = defineTag(IfdData.TYPE_IFD_0, 0x011B.toShort())
- private val TAG_PLANAR_CONFIGURATION = defineTag(IfdData.TYPE_IFD_0, 0x011C.toShort())
-
- /**
- * Value is unsigned int.
- * Unit of XResolution(0x011a)/YResolution(0x011b)
- *
- * * '1' means no-unit ( use inch )
- * * '2' inch
- * * '3' centimeter
- * * '4' millimeter
- * * '5' micrometer
- *
- */
- private val TAG_RESOLUTION_UNIT = defineTag(IfdData.TYPE_IFD_0, 0x0128.toShort())
- private val TAG_TRANSFER_FUNCTION = defineTag(IfdData.TYPE_IFD_0, 0x012D.toShort())
-
- /**
- * Value is ascii string
- * Shows firmware(internal software of digicam) version number.
- */
- private val TAG_SOFTWARE = defineTag(IfdData.TYPE_IFD_0, 0x0131.toShort())
-
- /**
- * Value is ascii string (20)
- * Date/Time of image was last modified. Data format is "YYYY:MM:DD HH:MM:SS"+0x00, total 20bytes. In usual, it has the same
- * value of DateTimeOriginal(0x9003)
- */
- val TAG_DATE_TIME = defineTag(IfdData.TYPE_IFD_0, 0x0132.toShort())
-
- /**
- * Vallue is ascii String
- * This tag records the name of the camera owner, photographer or image creator. The detailed format is not specified, but it is
- * recommended that the information be written as in the example below for ease of Interoperability. When the field is left
- * blank, it is treated as unknown.
- */
- private val TAG_ARTIST = defineTag(IfdData.TYPE_IFD_0, 0x013B.toShort())
- private val TAG_WHITE_POINT = defineTag(IfdData.TYPE_IFD_0, 0x013E.toShort())
- private val TAG_PRIMARY_CHROMATICITIES = defineTag(IfdData.TYPE_IFD_0, 0x013F.toShort())
- private val TAG_Y_CB_CR_COEFFICIENTS = defineTag(IfdData.TYPE_IFD_0, 0x0211.toShort())
- private val TAG_Y_CB_CR_SUB_SAMPLING = defineTag(IfdData.TYPE_IFD_0, 0x0212.toShort())
- private val TAG_Y_CB_CR_POSITIONING = defineTag(IfdData.TYPE_IFD_0, 0x0213.toShort())
- private val TAG_REFERENCE_BLACK_WHITE = defineTag(IfdData.TYPE_IFD_0, 0x0214.toShort())
-
- /**
- * Values is ascii string
- * Shows copyright information
- */
- private val TAG_COPYRIGHT = defineTag(IfdData.TYPE_IFD_0, 0x8298.toShort())
- val TAG_EXIF_IFD = defineTag(IfdData.TYPE_IFD_0, 0x8769.toShort())
- val TAG_GPS_IFD = defineTag(IfdData.TYPE_IFD_0, 0x8825.toShort())
- // IFD 1
- val TAG_JPEG_INTERCHANGE_FORMAT = defineTag(IfdData.TYPE_IFD_1, 0x0201.toShort())
- val TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = defineTag(IfdData.TYPE_IFD_1, 0x0202.toShort())
- // IFD Exif Tags
-
- /**
- * Value is unsigned double
- * Exposure time (reciprocal of shutter speed). Unit is second
- */
- private val TAG_EXPOSURE_TIME = defineTag(IfdData.TYPE_IFD_EXIF, 0x829A.toShort())
-
- /**
- * Value is unsigned double
- * The actual F-number(F-stop) of lens when the image was taken
- *
- * @see .TAG_APERTURE_VALUE
- */
- val TAG_F_NUMBER = defineTag(IfdData.TYPE_IFD_EXIF, 0x829D.toShort())
-
- /**
- * Value is unsigned int.
- * Exposure program that the camera used when image was taken.
- *
- * * '1' means manual control
- * * '2' program normal
- * * '3' aperture priority
- * * '4' shutter priority
- * * '5' program creative (slow program)
- * * '6' program action(high-speed program)
- * * '7' portrait mode
- * * '8' landscape mode.
- *
- */
- private val TAG_EXPOSURE_PROGRAM = defineTag(IfdData.TYPE_IFD_EXIF, 0x8822.toShort())
- private val TAG_SPECTRAL_SENSITIVITY = defineTag(IfdData.TYPE_IFD_EXIF, 0x8824.toShort())
-
- /**
- * Value is unsigned int.
- * CCD sensitivity equivalent to Ag-Hr film speedrate.
- * Indicates the ISO Speed and ISO Latitude of the camera or input device as specified in ISO 12232
- */
- private val TAG_ISO_SPEED_RATINGS = defineTag(IfdData.TYPE_IFD_EXIF, 0x8827.toShort())
- private val TAG_OECF = defineTag(IfdData.TYPE_IFD_EXIF, 0x8828.toShort())
-
- /**
- * ASCII string (4).
- * The version of this standard supported. Nonexistence of this field is taken to mean nonconformance to the standard (see
- * section 4.2). Conformance to this standard is indicated by recording "0220" as 4-byte ASCII
- */
- private val TAG_EXIF_VERSION = defineTag(IfdData.TYPE_IFD_EXIF, 0x9000.toShort())
-
- /**
- * Value is ascii string (20)
- * Date/Time of original image taken. This value should not be modified by user program.
- */
- val TAG_DATE_TIME_ORIGINAL = defineTag(IfdData.TYPE_IFD_EXIF, 0x9003.toShort())
-
- /**
- * Value is ascii string (20)
- * Date/Time of image digitized. Usually, it contains the same value of DateTimeOriginal(0x9003).
- */
- val TAG_DATE_TIME_DIGITIZED = defineTag(IfdData.TYPE_IFD_EXIF, 0x9004.toShort())
- private val TAG_COMPONENTS_CONFIGURATION =
- defineTag(IfdData.TYPE_IFD_EXIF, 0x9101.toShort())
- private val TAG_COMPRESSED_BITS_PER_PIXEL =
- defineTag(IfdData.TYPE_IFD_EXIF, 0x9102.toShort())
-
- /**
- * Value is signed double.
- * Shutter speed. To convert this value to ordinary 'Shutter Speed'; calculate this value's power of 2, then reciprocal. For
- * example, if value is '4', shutter speed is 1/(2^4)=1/16 second.
- */
- private val TAG_SHUTTER_SPEED_VALUE = defineTag(IfdData.TYPE_IFD_EXIF, 0x9201.toShort())
-
- /**
- * Value is unsigned double
- * The actual aperture value of lens when the image was taken.
- * To convert this value to ordinary F-number(F-stop), calculate this value's power of root 2 (=1.4142).
- * For example, if value is '5', F-number is 1.4142^5 = F5.6
- *
- *
- *
- * FNumber = Math.exp( ApertureValue * Math.log( 2 ) * 0.5 );
-
*
- *
- * @see .TAG_F_NUMBER
- */
- val TAG_APERTURE_VALUE = defineTag(IfdData.TYPE_IFD_EXIF, 0x9202.toShort())
-
- /**
- * Value is signed double
- * Brightness of taken subject, unit is EV.
- */
- private val TAG_BRIGHTNESS_VALUE = defineTag(IfdData.TYPE_IFD_EXIF, 0x9203.toShort())
-
- /**
- * Value is signed double.
- * The exposure bias. The unit is the APEX value. Ordinarily it is given in the range of -99.99 to 99.99
- */
- private val TAG_EXPOSURE_BIAS_VALUE = defineTag(IfdData.TYPE_IFD_EXIF, 0x9204.toShort())
-
- /**
- * Value is unsigned double.
- * Maximum aperture value of lens.
- * You can convert to F-number by calculating power of root 2 (same process of ApertureValue(0x9202).
- *
- *
- *
- * FNumber = Math.exp( MaxApertureValue * Math.log( 2 ) * 0.5 )
-
*
- */
- private val TAG_MAX_APERTURE_VALUE = defineTag(IfdData.TYPE_IFD_EXIF, 0x9205.toShort())
-
- /**
- * Value if signed double.
- * Distance to focus point, unit is meter. If value < 0 then focus point is infinite
- */
- private val TAG_SUBJECT_DISTANCE = defineTag(IfdData.TYPE_IFD_EXIF, 0x9206.toShort())
-
- /**
- * Value is unsigned int.
- * Exposure metering method:
- *
- * * 0 = unknown
- * * 1 = Average
- * * 2 = CenterWeightedAverage
- * * 3 = Spot
- * * 4 = MultiSpot
- * * 5 = Pattern
- * * 6 = Partial
- * * Other = reserved
- * * 255 = other
- *
- */
- private val TAG_METERING_MODE = defineTag(IfdData.TYPE_IFD_EXIF, 0x9207.toShort())
-
- /**
- * Value is unsigned int.
- * Light source, actually this means white balance setting.
- *
- * * 0 = means auto
- * * 1 = Daylight
- * * 2 = Fluorescent
- * * 3 = Tungsten (incandescent light)
- * * 4 = Flash
- * * 9 = Fine weather
- * * 10 = Cloudy weather
- * * 11 = Shade
- * * 12 = Daylight fluorescent (D 5700 - 7100K)
- * * 13 = Day white fluorescent (N 4600 - 5400K)
- * * 14 = Cool white fluorescent (W 3900 - 4500K)
- * * 15 = White fluorescent (WW 3200 - 3700K)
- * * 17 = Standard light A
- * * 18 = Standard light B
- * * 19 = Standard light C
- * * 20 = D55
- * * 21 = D65
- * * 22 = D75
- * * 23 = D50
- * * 24 = ISO studio tungsten
- * * 255 = other light source
- * * Other = reserved
- *
- */
- private val TAG_LIGHT_SOURCE = defineTag(IfdData.TYPE_IFD_EXIF, 0x9208.toShort())
-
- /**
- * Value is unsigned integer
- * The 8 bits can be extracted and evaluated in this way:
- *
- * 1. Bit 0 indicates the flash firing status
- * 1. bits 1 and 2 indicate the flash return status
- * 1. bits 3 and 4 indicate the flash mode
- * 1. bit 5 indicates whether the flash function is present
- * 1. and bit 6 indicates "red eye" mode
- * 1. bit 7 unused
- *
- *
- *
- * Resulting Flash tag values are:
- *
- * * 0000.H = Flash did not fire
- * * 0001.H = Flash fired
- * * 0005.H = Strobe return light not detected
- * * 0007.H = Strobe return light detected
- * * 0009.H = Flash fired, compulsory flash mode
- * * 000D.H = Flash fired, compulsory flash mode, return light not detected
- * * 000F.H = Flash fired, compulsory flash mode, return light detected
- * * 0010.H = Flash did not fire, compulsory flash mode
- * * 0018.H = Flash did not fire, auto mode
- * * 0019.H = Flash fired, auto mode
- * * 001D.H = Flash fired, auto mode, return light not detected
- * * 001F.H = Flash fired, auto mode, return light detected
- * * 0020.H = No flash function
- * * 0041.H = Flash fired, red-eye reduction mode
- * * 0045.H = Flash fired, red-eye reduction mode, return light not detected
- * * 0047.H = Flash fired, red-eye reduction mode, return light detected
- * * 0049.H = Flash fired, compulsory flash mode, red-eye reduction mode
- * * 004D.H = Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected
- * * 004F.H = Flash fired, compulsory flash mode, red-eye reduction mode, return light detected
- * * 0059.H = Flash fired, auto mode, red-eye reduction mode
- * * 005D.H = Flash fired, auto mode, return light not detected, red-eye reduction mode
- * * 005F.H = Flash fired, auto mode, return light detected, red-eye reduction mode
- * * Other = reserved
- *
- *
- * @see [http://www.exif.org/Exif2-2.PDF](http://www.exif.org/Exif2-2.PDF)
- */
- private val TAG_FLASH = defineTag(IfdData.TYPE_IFD_EXIF, 0x9209.toShort())
-
- /**
- * Value is unsigned double
- * Focal length of lens used to take image. Unit is millimeter.
- */
- private val TAG_FOCAL_LENGTH = defineTag(IfdData.TYPE_IFD_EXIF, 0x920A.toShort())
- private val TAG_SUBJECT_AREA = defineTag(IfdData.TYPE_IFD_EXIF, 0x9214.toShort())
- private val TAG_MAKER_NOTE = defineTag(IfdData.TYPE_IFD_EXIF, 0x927C.toShort())
- val TAG_USER_COMMENT = defineTag(IfdData.TYPE_IFD_EXIF, 0x9286.toShort())
- private val TAG_SUB_SEC_TIME = defineTag(IfdData.TYPE_IFD_EXIF, 0x9290.toShort())
- private val TAG_SUB_SEC_TIME_ORIGINAL = defineTag(IfdData.TYPE_IFD_EXIF, 0x9291.toShort())
- private val TAG_SUB_SEC_TIME_DIGITIZED = defineTag(IfdData.TYPE_IFD_EXIF, 0x9292.toShort())
- private val TAG_FLASHPIX_VERSION = defineTag(IfdData.TYPE_IFD_EXIF, 0xA000.toShort())
-
- /**
- * Value is int.
- * Normally sRGB (=1) is used to define the color space based on the PC monitor conditions and environment. If a color space
- * other than sRGB is used, Uncalibrated (=FFFF.H) is set. Image data recorded as Uncalibrated can be treated as sRGB when it is
- * converted to Flashpix. On sRGB see Annex E.
- *
- * * '1' = sRGB
- * * 'FFFF' = Uncalibrated
- * * 'other' = Reserved
- *
- */
- private val TAG_COLOR_SPACE = defineTag(IfdData.TYPE_IFD_EXIF, 0xA001.toShort())
-
- /**
- * Value is unsigned int.
- * Specific to compressed data; the valid width of the meaningful image. When a compressed file is recorded, the valid width of
- * the meaningful image shall be recorded in this tag, whether or not there is padding data or a restart marker. This tag should
- * not exist in an uncompressed file.
- */
- private val TAG_PIXEL_X_DIMENSION = defineTag(IfdData.TYPE_IFD_EXIF, 0xA002.toShort())
-
- /**
- * @see .TAG_PIXEL_X_DIMENSION
- */
- private val TAG_PIXEL_Y_DIMENSION = defineTag(IfdData.TYPE_IFD_EXIF, 0xA003.toShort())
- private val TAG_RELATED_SOUND_FILE = defineTag(IfdData.TYPE_IFD_EXIF, 0xA004.toShort())
- val TAG_INTEROPERABILITY_IFD = defineTag(IfdData.TYPE_IFD_EXIF, 0xA005.toShort())
- private val TAG_FLASH_ENERGY = defineTag(IfdData.TYPE_IFD_EXIF, 0xA20B.toShort())
- private val TAG_SPATIAL_FREQUENCY_RESPONSE =
- defineTag(IfdData.TYPE_IFD_EXIF, 0xA20C.toShort())
-
- /**
- * Value is unsigned double.
- * Indicates the number of pixels in the image width (X) direction per FocalPlaneResolutionUnit on the camera focal plane. CCD's
- * pixel density
- *
- * @see .TAG_FOCAL_PLANE_RESOLUTION_UNIT
- */
- private val TAG_FOCAL_PLANE_X_RESOLUTION =
- defineTag(IfdData.TYPE_IFD_EXIF, 0xA20E.toShort())
-
- /**
- * @see .TAG_FOCAL_PLANE_X_RESOLUTION
- */
- private val TAG_FOCAL_PLANE_Y_RESOLUTION =
- defineTag(IfdData.TYPE_IFD_EXIF, 0xA20F.toShort())
-
- /**
- * Value is unsigned int.
- * Unit of FocalPlaneXResoluton/FocalPlaneYResolution.
- *
- * * '1' means no-unit
- * * '2' inch
- * * '3' centimeter
- * * '4' millimeter
- * * '5' micrometer
- *
- *
- *
- * This tag can be used to calculate the CCD Width:
- *
- *
- *
- * CCDWidth = ( PixelXDimension * FocalPlaneResolutionUnit / FocalPlaneXResolution )
-
*
- */
- private val TAG_FOCAL_PLANE_RESOLUTION_UNIT =
- defineTag(IfdData.TYPE_IFD_EXIF, 0xA210.toShort())
- private val TAG_SUBJECT_LOCATION = defineTag(IfdData.TYPE_IFD_EXIF, 0xA214.toShort())
- private val TAG_EXPOSURE_INDEX = defineTag(IfdData.TYPE_IFD_EXIF, 0xA215.toShort())
-
- /**
- * Value is unsigned int.
- * Indicates the image sensor type on the camera or input device. The values are as follows:
- *
- * * 1 = Not defined
- * * 2 = One-chip color area sensor
- * * 3 = Two-chip color area sensor JEITA CP-3451 - 41
- * * 4 = Three-chip color area sensor
- * * 5 = Color sequential area sensor
- * * 7 = Trilinear sensor
- * * 8 = Color sequential linear sensor
- * * Other = reserved
- *
- */
- private val TAG_SENSING_METHOD = defineTag(IfdData.TYPE_IFD_EXIF, 0xA217.toShort())
- private val TAG_FILE_SOURCE = defineTag(IfdData.TYPE_IFD_EXIF, 0xA300.toShort())
- private val TAG_SCENE_TYPE = defineTag(IfdData.TYPE_IFD_EXIF, 0xA301.toShort())
- private val TAG_CFA_PATTERN = defineTag(IfdData.TYPE_IFD_EXIF, 0xA302.toShort())
- private val TAG_CUSTOM_RENDERED = defineTag(IfdData.TYPE_IFD_EXIF, 0xA401.toShort())
-
- /**
- * Value is int.
- * This tag indicates the exposure mode set when the image was shot. In auto-bracketing mode, the camera shoots a series of
- * frames of the same scene at different exposure settings.
- *
- * * 0 = Auto exposure
- * * 1 = Manual exposure
- * * 2 = Auto bracket
- * * Other = reserved
- *
- */
- private val TAG_EXPOSURE_MODE = defineTag(IfdData.TYPE_IFD_EXIF, 0xA402.toShort())
- private val TAG_WHITE_BALANCE = defineTag(IfdData.TYPE_IFD_EXIF, 0xA403.toShort())
-
- /**
- * Value is double.
- * This tag indicates the digital zoom ratio when the image was shot. If the numerator of the recorded value is 0, this indicates
- * that digital zoom was not used
- */
- private val TAG_DIGITAL_ZOOM_RATIO = defineTag(IfdData.TYPE_IFD_EXIF, 0xA404.toShort())
-
- /**
- * Value is unsigned int.
- * This tag indicates the equivalent focal length assuming a 35mm film camera, in mm.
- * Exif 2.2 tag, usually not present, it can be calculated by:
- *
- *
- *
- * CCDWidth = ( PixelXDimension * FocalplaneUnits / FocalplaneXRes );
- * FocalLengthIn35mmFilm = ( FocalLength / CCDWidth * 36 + 0.5 );
-
*
- */
- private val TAG_FOCAL_LENGTH_IN_35_MM_FILE =
- defineTag(IfdData.TYPE_IFD_EXIF, 0xA405.toShort())
-
- /**
- * Value is int.
- * This tag indicates the type of scene that was shot. It can also be used to record the mode in which the image was shot. Note
- * that this differs from the scene type (SceneType) tag.
- *
- * * 0 = Standard
- * * 1 = Landscape
- * * 2 = Portrait
- * * 3 = Night scene
- * * Other = reserved
- *
- */
- private val TAG_SCENE_CAPTURE_TYPE = defineTag(IfdData.TYPE_IFD_EXIF, 0xA406.toShort())
-
- /**
- * Value is int.
- * This tag indicates the degree of overall image gain adjustment.
- *
- * * 0 = None
- * * 1 = Low gain up
- * * 2 = High gain up
- * * 3 = Low gain down
- * * 4 = High gain down
- * * Other = reserved
- *
- */
- private val TAG_GAIN_CONTROL = defineTag(IfdData.TYPE_IFD_EXIF, 0xA407.toShort())
-
- /**
- * Value is int.
- * This tag indicates the direction of contrast processing applied by the camera when the image was shot.
- *
- * * 0 = Normal
- * * 1 = Soft
- * * 2 = Hard
- * * Other = reserved
- *
- */
- private val TAG_CONTRAST = defineTag(IfdData.TYPE_IFD_EXIF, 0xA408.toShort())
-
- /**
- * Value is int.
- * This tag indicates the direction of saturation processing applied by the camera when the image was shot.
- *
- * * 0 = Normal
- * * 1 = Low saturation
- * * 2 = High saturation
- * * Other = reserved
- *
- */
- private val TAG_SATURATION = defineTag(IfdData.TYPE_IFD_EXIF, 0xA409.toShort())
-
- /**
- * Value is int.
- * This tag indicates the direction of sharpness processing applied by the camera when the image was shot
- *
- * * 0 = Normal
- * * 1 = Soft
- * * 2 = Hard
- * * Other = reserved
- *
- */
- private val TAG_SHARPNESS = defineTag(IfdData.TYPE_IFD_EXIF, 0xA40A.toShort())
- private val TAG_DEVICE_SETTING_DESCRIPTION =
- defineTag(IfdData.TYPE_IFD_EXIF, 0xA40B.toShort())
-
- /**
- * Value is int.
- * This tag indicates the distance to the subject.
- *
- * * 0 = unknown
- * * 1 = Macro
- * * 2 = Close view
- * * 3 = Distant view
- * * Other = reserved
- *
- */
- private val TAG_SUBJECT_DISTANCE_RANGE = defineTag(IfdData.TYPE_IFD_EXIF, 0xA40C.toShort())
-
- /**
- * [ExifTag.TYPE_ASCII]
- */
- private val TAG_IMAGE_UNIQUE_ID = defineTag(IfdData.TYPE_IFD_EXIF, 0xA420.toShort())
-
- /**
- * Lens Specifications. The value it's a 4 rational containing:
- *
- * 1. Minimum focal length (in mm)
- * 1. Maximum focal length (in mm)
- * 1. Minimum F Number in the minimum focal length
- * 1. Maximum F Number in the maximum focal length
- *
- *
- *
- * [ExifTag.TYPE_RATIONAL]
- *
- * @see it.sephiroth.android.library.exif2.ExifUtil.processLensSpecifications
- * @since EXIF 2.3
- */
- val TAG_LENS_SPECS = defineTag(IfdData.TYPE_IFD_EXIF, 0xA432.toShort())
-
- /**
- * Lens maker
- * [ExifTag.TYPE_ASCII]
- *
- * @since EXIF 2.3
- */
- private val TAG_LENS_MAKE = defineTag(IfdData.TYPE_IFD_EXIF, 0xA433.toShort())
- /**
- * Lens model name and number
- * [ExifTag.TYPE_ASCII]
- *
- * @since EXIF 2.3
- */
- val TAG_LENS_MODEL = defineTag(IfdData.TYPE_IFD_EXIF, 0xA434.toShort())
-
- /**
- * The SensitivityType tag indicates which one of the parameters of ISO12232 is the
- * PhotographicSensitivity tag. Although it is an optional tag, it should be recorded
- * when a PhotographicSensitivity tag is recorded.
- * Value = 4, 5, 6, or 7 may be used in case that the values of plural
- * parameters are the same.
- * Values:
- *
- * * 0: Unknown
- * * 1: Standardoutputsensitivity(SOS)
- * * 2: Recommended exposure index (REI)
- * * 3: ISOspeed
- * * 4: Standard output sensitivity (SOS) and recommended exposure index (REI)
- * * 5: Standardoutputsensitivity(SOS)andISOspeed
- * * 6: Recommendedexposureindex(REI)andISOspeed
- * * 7: Standard output sensitivity (SOS) and recommended exposure index (REI) and ISO speed
- * * Other: Reserved
- *
- *
- *
- * [ExifTag.TYPE_UNSIGNED_SHORT]
- *
- * @see it.sephiroth.android.library.exif2.ExifInterface.SensitivityType
- *
- * @since EXIF 2.3
- */
- private val TAG_SENSITIVITY_TYPE = defineTag(IfdData.TYPE_IFD_EXIF, 0x8830.toShort())
-
- // IFD GPS tags
- private val TAG_GPS_VERSION_ID = defineTag(IfdData.TYPE_IFD_GPS, 0.toShort())
-
- /**
- * Value is string(1)
- * Indicates whether the latitude is north or south latitude. The ASCII value 'N' indicates north latitude, and 'S' is south latitude.
- */
- val TAG_GPS_LATITUDE_REF = defineTag(IfdData.TYPE_IFD_GPS, 1.toShort())
-
- /**
- * Value is string.
- * Indicates the latitude. The latitude is expressed as three RATIONAL values giving the degrees, minutes, and
- * seconds, respectively. If latitude is expressed as degrees, minutes and seconds, a typical format would be
- * dd/1,mm/1,ss/1. When degrees and minutes are used and, for example, fractions of minutes are given up to two
- * decimal places, the format would be dd/1,mmmm/100,0/1.
- */
- val TAG_GPS_LATITUDE = defineTag(IfdData.TYPE_IFD_GPS, 2.toShort())
-
- /**
- * Value is string(1)
- * Indicates whether the longitude is east or west longitude. ASCII 'E' indicates east longitude, and 'W' is west longitude.
- */
- val TAG_GPS_LONGITUDE_REF = defineTag(IfdData.TYPE_IFD_GPS, 3.toShort())
-
- /**
- * Value is string.
- * Indicates the longitude. The longitude is expressed as three RATIONAL values giving the degrees, minutes, and
- * seconds, respectively. If longitude is expressed as degrees, minutes and seconds, a typical format would be
- * ddd/1,mm/1,ss/1. When degrees and minutes are used and, for example, fractions of minutes are given up to two
- * decimal places, the format would be ddd/1,mmmm/100,0/1.
- */
- val TAG_GPS_LONGITUDE = defineTag(IfdData.TYPE_IFD_GPS, 4.toShort())
-
- /**
- * Value is byte
- * Indicates the altitude used as the reference altitude. If the reference is sea level and the altitude is above sea level,
- * 0 is given. If the altitude is below sea level, a value of 1 is given and the altitude is indicated as an absolute value in
- * the GPSAltitude tag. The reference unit is meters. Note that this tag is BYTE type, unlike other reference tags
- */
- val TAG_GPS_ALTITUDE_REF = defineTag(IfdData.TYPE_IFD_GPS, 5.toShort())
-
- /**
- * Value is string.
- * Indicates the altitude based on the reference in GPSAltitudeRef. Altitude is expressed as one RATIONAL value. The reference unit is meters.
- */
- val TAG_GPS_ALTITUDE = defineTag(IfdData.TYPE_IFD_GPS, 6.toShort())
- val TAG_GPS_TIME_STAMP = defineTag(IfdData.TYPE_IFD_GPS, 7.toShort())
- private val TAG_GPS_SATTELLITES = defineTag(IfdData.TYPE_IFD_GPS, 8.toShort())
- private val TAG_GPS_STATUS = defineTag(IfdData.TYPE_IFD_GPS, 9.toShort())
- private val TAG_GPS_MEASURE_MODE = defineTag(IfdData.TYPE_IFD_GPS, 10.toShort())
- private val TAG_GPS_DOP = defineTag(IfdData.TYPE_IFD_GPS, 11.toShort())
-
- /**
- * Value is string(1).
- * Indicates the unit used to express the GPS receiver speed of movement. 'K' 'M' and 'N' represents kilometers per hour, miles per hour, and knots.
- */
- private val TAG_GPS_SPEED_REF = defineTag(IfdData.TYPE_IFD_GPS, 12.toShort())
-
- /**
- * Value is string.
- * Indicates the speed of GPS receiver movement
- */
- private val TAG_GPS_SPEED = defineTag(IfdData.TYPE_IFD_GPS, 13.toShort())
- private val TAG_GPS_TRACK_REF = defineTag(IfdData.TYPE_IFD_GPS, 14.toShort())
- private val TAG_GPS_TRACK = defineTag(IfdData.TYPE_IFD_GPS, 15.toShort())
- private val TAG_GPS_IMG_DIRECTION_REF = defineTag(IfdData.TYPE_IFD_GPS, 16.toShort())
- private val TAG_GPS_IMG_DIRECTION = defineTag(IfdData.TYPE_IFD_GPS, 17.toShort())
- private val TAG_GPS_MAP_DATUM = defineTag(IfdData.TYPE_IFD_GPS, 18.toShort())
- private val TAG_GPS_DEST_LATITUDE_REF = defineTag(IfdData.TYPE_IFD_GPS, 19.toShort())
- private val TAG_GPS_DEST_LATITUDE = defineTag(IfdData.TYPE_IFD_GPS, 20.toShort())
- val TAG_GPS_DEST_LONGITUDE_REF = defineTag(IfdData.TYPE_IFD_GPS, 21.toShort())
- val TAG_GPS_DEST_LONGITUDE = defineTag(IfdData.TYPE_IFD_GPS, 22.toShort())
- private val TAG_GPS_DEST_BEARING_REF = defineTag(IfdData.TYPE_IFD_GPS, 23.toShort())
- private val TAG_GPS_DEST_BEARING = defineTag(IfdData.TYPE_IFD_GPS, 24.toShort())
- private val TAG_GPS_DEST_DISTANCE_REF = defineTag(IfdData.TYPE_IFD_GPS, 25.toShort())
- private val TAG_GPS_DEST_DISTANCE = defineTag(IfdData.TYPE_IFD_GPS, 26.toShort())
- private val TAG_GPS_PROCESSING_METHOD = defineTag(IfdData.TYPE_IFD_GPS, 27.toShort())
- private val TAG_GPS_AREA_INFORMATION = defineTag(IfdData.TYPE_IFD_GPS, 28.toShort())
- val TAG_GPS_DATE_STAMP = defineTag(IfdData.TYPE_IFD_GPS, 29.toShort())
- private val TAG_GPS_DIFFERENTIAL = defineTag(IfdData.TYPE_IFD_GPS, 30.toShort())
- // IFD Interoperability tags
- private val TAG_INTEROPERABILITY_INDEX =
- defineTag(IfdData.TYPE_IFD_INTEROPERABILITY, 1.toShort())
-
- val DEFAULT_BYTE_ORDER : ByteOrder = ByteOrder.BIG_ENDIAN
- private const val NULL_ARGUMENT_STRING = "Argument is null"
-
- private const val GPS_DATE_FORMAT_STR = "yyyy:MM:dd"
- private val mGPSDateStampFormat = SimpleDateFormat(GPS_DATE_FORMAT_STR, Locale.ENGLISH)
- .apply { timeZone = TimeZone.getTimeZone("UTC") }
-
- private const val DATETIME_FORMAT_STR = "yyyy:MM:dd kk:mm:ss"
- private val mDateTimeStampFormat = SimpleDateFormat(DATETIME_FORMAT_STR, Locale.ENGLISH)
-
- /**
- * Tags that contain offset markers. These are included in the banned
- * defines.
- */
- private val sOffsetTags = HashSet().apply {
- add(getTrueTagKey(TAG_GPS_IFD))
- add(getTrueTagKey(TAG_EXIF_IFD))
- add(getTrueTagKey(TAG_JPEG_INTERCHANGE_FORMAT))
- add(getTrueTagKey(TAG_INTEROPERABILITY_IFD))
- add(getTrueTagKey(TAG_STRIP_OFFSETS))
- }
-
- /**
- * Tags with definitions that cannot be overridden (banned defines).
- */
- var sBannedDefines = HashSet(sOffsetTags).apply {
- add(getTrueTagKey(TAG_NULL))
- add(getTrueTagKey(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH))
- add(getTrueTagKey(TAG_STRIP_BYTE_COUNTS))
- }
-
- /**
- * Returns true if tag TID is one of the following: [.TAG_EXIF_IFD],
- * [.TAG_GPS_IFD], [.TAG_JPEG_INTERCHANGE_FORMAT],
- * [.TAG_STRIP_OFFSETS], [.TAG_INTEROPERABILITY_IFD]
- *
- *
- * Note: defining tags with these TID's is disallowed.
- *
- * @param tag a tag's TID (can be obtained from a defined tag constant with
- * [.getTrueTagKey]).
- * @return true if the TID is that of an offset tag.
- */
- fun isOffsetTag(tag : Short) : Boolean =
- sOffsetTags.contains(tag)
-
- /**
- * Returns the Orientation ExifTag value for a given number of degrees.
- *
- * @param degreesArg the amount an image is rotated in degrees.
- */
- fun getOrientationValueForRotation(degreesArg : Int) : Short {
- var degrees = degreesArg
- degrees %= 360
- if(degrees < 0) {
- degrees += 360
- }
- return when {
- degrees < 90 -> Orientation.TOP_LEFT // 0 degrees
- degrees < 180 -> Orientation.RIGHT_TOP // 90 degrees cw
- degrees < 270 -> Orientation.BOTTOM_LEFT // 180 degrees
- else -> Orientation.RIGHT_BOTTOM // 270 degrees cw
- }
- }
-
- /**
- * Returns the rotation degrees corresponding to an ExifTag Orientation
- * value.
- *
- * @param orientation the ExifTag Orientation value.
- */
- fun getRotationForOrientationValue(orientation : Short) : Int =
- when(orientation) {
- Orientation.TOP_LEFT -> 0
- Orientation.RIGHT_TOP -> 90
- Orientation.BOTTOM_LEFT -> 180
- Orientation.RIGHT_BOTTOM -> 270
- else -> 0
- }
-
- /**
- * Gets the double representation of the GPS latitude or longitude
- * coordinate.
- *
- * @param coordinate an array of 3 Rationals representing the degrees,
- * minutes, and seconds of the GPS location as defined in the
- * exif specification.
- * @param reference a GPS reference reperesented by a String containing "N",
- * "S", "E", or "W".
- * @return the GPS coordinate represented as degrees + minutes/60 +
- * seconds/3600
- */
- fun convertLatOrLongToDouble(coordinate : Array, reference : String) : Double {
- val degrees = coordinate[0].toDouble()
- val minutes = coordinate[1].toDouble()
- val seconds = coordinate[2].toDouble()
- val result = degrees + minutes / 60.0 + seconds / 3600.0
- return when {
- reference.startsWith("S") || reference.startsWith("W") -> - result
- else -> result
- }
- }
-
- fun getAllowedIfdsFromInfo(info : Int) : IntArray? {
- val ifdFlags = getAllowedIfdFlagsFromInfo(info)
- val ifds = IfdData.list
- val l = ArrayList()
- for(i in 0 until IfdData.TYPE_IFD_COUNT) {
- val flag = ifdFlags shr i and 1
- if(flag == 1) {
- l.add(ifds[i])
- }
- }
- if(l.size <= 0) {
- return null
- }
- val ret = IntArray(l.size)
- var j = 0
- for(i in l) {
- ret[j ++] = i
- }
- return ret
- }
-
- @Throws(IOException::class)
- private fun writeExif_internal(
- input : InputStream,
- output : OutputStream,
- exifData : ExifData
- ) : Int {
- // Log.i( TAG, "writeExif_internal" );
-
- // 1. read the output file first
- val src_exif = ExifInterface()
- src_exif.readExif(input, 0)
-
- // 4. Create the destination outputstream
- // 5. write headers
- output.write(0xFF)
- output.write(JpegHeader.TAG_SOI)
-
- val sections = src_exif.mData.sections
-
- // 6. write all the sections from the srcFilename
- if(sections.firstOrNull()?.type != JpegHeader.TAG_M_JFIF) {
- Log.w(TAG, "first section is not a JFIF or EXIF tag")
- output.write(JpegHeader.JFIF_HEADER)
- }
-
- // 6.1 write the *new* EXIF tag
- val eo = ExifOutputStream(src_exif, exifData)
- eo.writeExifData(output)
-
- // 6.2 write all the sections except for the SOS ( start of scan )
- sections.forEach {
- // Log.v( TAG, "writing section.. " + String.format( "0x%2X", current.type ) );
- output.write(0xFF)
- output.write(it.type)
- output.write(it.data)
- }
-
- // 6.3 write the last SOS marker
- val current = sections[sections.size - 1]
- // Log.v( TAG, "writing last section.. " + String.format( "0x%2X", current.type ) );
- output.write(0xFF)
- output.write(current.type)
- output.write(current.data)
-
- // return the position where the input stream should be copied
- return src_exif.mData.mUncompressedDataPosition
- }
-
- /**
- * Returns the default IFD for a tag constant.
- */
- fun getTrueIfd(tag : Int) : Int = tag.ushr(16)
-
- /**
- * Returns the TID for a tag constant.
- */
- fun getTrueTagKey(tag : Int) : Short = tag.toShort()
-
- private fun getFlagsFromAllowedIfds(allowedIfds : IntArray) : Int {
- if(allowedIfds.isEmpty()) return 0
- var flags = 0
- val ifds = IfdData.list
- for(i in 0 until IfdData.TYPE_IFD_COUNT) {
- for(j in allowedIfds) {
- if(ifds[i] == j) {
- flags = flags or (1 shl i)
- break
- }
- }
- }
- return flags
- }
-
- private fun getComponentCountFromInfo(info : Int) : Int = info and 0x0ffff
-
- private fun getTypeFromInfo(info : Int) : Short = (info shr 16 and 0x0ff).toShort()
-
- /**
- * Returns the constant representing a tag with a given TID and default IFD.
- */
- fun defineTag(ifdId : Int, tagId : Short) : Int = tagId or (ifdId shl 16)
-
- private fun convertRationalLatLonToString(
- coord : Array,
- refArg : String
- ) : String? {
- return try {
- var ref = refArg
-
- val degrees = coord[0].toDouble()
- val minutes = coord[1].toDouble()
- val seconds = coord[2].toDouble()
- ref = ref.substring(0, 1)
-
-
-
- String.format(
- Locale.ENGLISH,
- "%1$.0f° %2$.0f' %3$.0f\" %4\$s",
- degrees,
- minutes,
- seconds,
- ref.toUpperCase(Locale.ENGLISH)
- )
- } catch(ex : Throwable) {
- ex.printStackTrace()
- null
- }
- }
-
- /**
- * Given an exif date time, like [.TAG_DATE_TIME] or [.TAG_DATE_TIME_DIGITIZED]
- * returns a java Date object
- *
- * @param dateTimeString one of the value of [.TAG_DATE_TIME] or [.TAG_DATE_TIME_DIGITIZED]
- * @param timeZone the target timezone
- * @return the parsed date
- */
- fun getDateTime(dateTimeString : String, timeZone : TimeZone) : Date? {
-
- return try {
- val formatter = SimpleDateFormat(DATETIME_FORMAT_STR, Locale.ENGLISH)
- formatter.timeZone = timeZone
- formatter.parse(dateTimeString)
- } catch(e : Throwable) {
- e.printStackTrace()
- null
- }
- }
-
- fun isIfdAllowed(info : Int, ifd : Int) : Boolean {
- val ifdFlags = getAllowedIfdFlagsFromInfo(info)
- IfdData.list.forEachIndexed { itemIndex, itemId ->
- if(ifd == itemId && ifdFlags shr itemIndex and 1 == 1) return true
- }
- return false
- }
-
- private fun getAllowedIfdFlagsFromInfo(info : Int) : Int = info.ushr(24)
-
- private fun toExifLatLong(valueArg : Double) : Array {
- // convert to the format dd/1 mm/1 ssss/100
- var value = abs(valueArg)
- val degrees = value
- value = (value - degrees) * 60
- val minutes = value
- value = (value - minutes) * 6000
- val seconds = value
- return arrayOf(
- Rational(degrees.toLong(), 1),
- Rational(minutes.toLong(), 1),
- Rational(seconds.toLong(), 100)
- )
- }
-
- fun toBitArray(value : Short) : ByteArray {
- val result = ByteArray(16)
- for(i in 0 .. 15) {
- result[15 - i] = (value shr i and 1).toByte()
- }
- return result
- }
-
- infix fun Short.shl(bits : Int) : Int = (this.toInt() and 0xffff) shl bits
- infix fun Short.shr(bits : Int) : Int = (this.toInt() and 0xffff) shr bits
- infix fun Short.or(bits : Int) : Int = (this.toInt() and 0xffff) or bits
-
- private fun SparseIntArray.initTagInfo() : SparseIntArray {
-
- var f : Int
-
- /*
- * We put tag information in a 4-bytes integer. The first byte a bitmask
- * representing the allowed IFDs of the tag, the second byte is the data
- * type, and the last two byte are a short value indicating the default
- * component count of this tag.
- */
-
- // IFD0 tags
- f = getFlagsFromAllowedIfds(intArrayOf(IfdData.TYPE_IFD_0, IfdData.TYPE_IFD_1)) shl 24
-
- put(TAG_MAKE, f or (ExifTag.TYPE_ASCII shl 16))
- put(TAG_IMAGE_WIDTH, f or (ExifTag.TYPE_UNSIGNED_LONG shl 16) or 1)
- put(TAG_IMAGE_LENGTH, f or (ExifTag.TYPE_UNSIGNED_LONG shl 16) or 1)
- put(TAG_BITS_PER_SAMPLE, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 3)
- put(TAG_COMPRESSION, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
- put(TAG_PHOTOMETRIC_INTERPRETATION, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
- put(TAG_ORIENTATION, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
- put(TAG_SAMPLES_PER_PIXEL, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
- put(TAG_PLANAR_CONFIGURATION, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
- put(TAG_Y_CB_CR_SUB_SAMPLING, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 2)
- put(TAG_Y_CB_CR_POSITIONING, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
- put(TAG_X_RESOLUTION, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
- put(TAG_Y_RESOLUTION, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
- put(TAG_RESOLUTION_UNIT, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
- put(TAG_STRIP_OFFSETS, f or (ExifTag.TYPE_UNSIGNED_LONG shl 16))
- put(TAG_ROWS_PER_STRIP, f or (ExifTag.TYPE_UNSIGNED_LONG shl 16) or 1)
- put(TAG_STRIP_BYTE_COUNTS, f or (ExifTag.TYPE_UNSIGNED_LONG shl 16))
- put(TAG_TRANSFER_FUNCTION, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 3 * 256)
- put(TAG_WHITE_POINT, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 2)
- put(TAG_PRIMARY_CHROMATICITIES, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 6)
- put(TAG_Y_CB_CR_COEFFICIENTS, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 3)
- put(TAG_REFERENCE_BLACK_WHITE, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 6)
- put(TAG_DATE_TIME, f or (ExifTag.TYPE_ASCII shl 16) or 20)
- put(TAG_IMAGE_DESCRIPTION, f or (ExifTag.TYPE_ASCII shl 16))
- put(TAG_MODEL, f or (ExifTag.TYPE_ASCII shl 16))
- put(TAG_SOFTWARE, f or (ExifTag.TYPE_ASCII shl 16))
- put(TAG_ARTIST, f or (ExifTag.TYPE_ASCII shl 16))
- put(TAG_COPYRIGHT, f or (ExifTag.TYPE_ASCII shl 16))
- put(TAG_EXIF_IFD, f or (ExifTag.TYPE_UNSIGNED_LONG shl 16) or 1)
- put(TAG_GPS_IFD, f or (ExifTag.TYPE_UNSIGNED_LONG shl 16) or 1)
-
- // IFD1 tags
- f = getFlagsFromAllowedIfds(intArrayOf(IfdData.TYPE_IFD_1)) shl 24
- put(TAG_JPEG_INTERCHANGE_FORMAT, f or (ExifTag.TYPE_UNSIGNED_LONG shl 16) or 1)
- put(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, f or (ExifTag.TYPE_UNSIGNED_LONG shl 16) or 1)
-
- // Exif tags
- f = getFlagsFromAllowedIfds(intArrayOf(IfdData.TYPE_IFD_EXIF)) shl 24
- put(TAG_EXIF_VERSION, f or (ExifTag.TYPE_UNDEFINED shl 16) or 4)
- put(TAG_FLASHPIX_VERSION, f or (ExifTag.TYPE_UNDEFINED shl 16) or 4)
- put(TAG_COLOR_SPACE, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
- put(TAG_COMPONENTS_CONFIGURATION, f or (ExifTag.TYPE_UNDEFINED shl 16) or 4)
- put(TAG_COMPRESSED_BITS_PER_PIXEL, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
- put(TAG_PIXEL_X_DIMENSION, f or (ExifTag.TYPE_UNSIGNED_LONG shl 16) or 1)
- put(TAG_PIXEL_Y_DIMENSION, f or (ExifTag.TYPE_UNSIGNED_LONG shl 16) or 1)
- put(TAG_MAKER_NOTE, f or (ExifTag.TYPE_UNDEFINED shl 16))
- put(TAG_USER_COMMENT, f or (ExifTag.TYPE_UNDEFINED shl 16))
- put(TAG_RELATED_SOUND_FILE, f or (ExifTag.TYPE_ASCII shl 16) or 13)
- put(TAG_DATE_TIME_ORIGINAL, f or (ExifTag.TYPE_ASCII shl 16) or 20)
- put(TAG_DATE_TIME_DIGITIZED, f or (ExifTag.TYPE_ASCII shl 16) or 20)
- put(TAG_SUB_SEC_TIME, f or (ExifTag.TYPE_ASCII shl 16))
- put(TAG_SUB_SEC_TIME_ORIGINAL, f or (ExifTag.TYPE_ASCII shl 16))
- put(TAG_SUB_SEC_TIME_DIGITIZED, f or (ExifTag.TYPE_ASCII shl 16))
- put(TAG_IMAGE_UNIQUE_ID, f or (ExifTag.TYPE_ASCII shl 16) or 33)
- put(TAG_LENS_SPECS, f or (ExifTag.TYPE_RATIONAL shl 16) or 4)
- put(TAG_LENS_MAKE, f or (ExifTag.TYPE_ASCII shl 16))
- put(TAG_LENS_MODEL, f or (ExifTag.TYPE_ASCII shl 16))
- put(TAG_SENSITIVITY_TYPE, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
- put(TAG_EXPOSURE_TIME, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
- put(TAG_F_NUMBER, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
- put(TAG_EXPOSURE_PROGRAM, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
- put(TAG_SPECTRAL_SENSITIVITY, f or (ExifTag.TYPE_ASCII shl 16))
- put(TAG_ISO_SPEED_RATINGS, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16))
- put(TAG_OECF, f or (ExifTag.TYPE_UNDEFINED shl 16))
- put(TAG_SHUTTER_SPEED_VALUE, f or (ExifTag.TYPE_RATIONAL shl 16) or 1)
- put(TAG_APERTURE_VALUE, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
- put(TAG_BRIGHTNESS_VALUE, f or (ExifTag.TYPE_RATIONAL shl 16) or 1)
- put(TAG_EXPOSURE_BIAS_VALUE, f or (ExifTag.TYPE_RATIONAL shl 16) or 1)
- put(TAG_MAX_APERTURE_VALUE, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
- put(TAG_SUBJECT_DISTANCE, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
- put(TAG_METERING_MODE, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
- put(TAG_LIGHT_SOURCE, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
- put(TAG_FLASH, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
- put(TAG_FOCAL_LENGTH, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
- put(TAG_SUBJECT_AREA, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16))
- put(TAG_FLASH_ENERGY, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
- put(TAG_SPATIAL_FREQUENCY_RESPONSE, f or (ExifTag.TYPE_UNDEFINED shl 16))
- put(TAG_FOCAL_PLANE_X_RESOLUTION, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
- put(TAG_FOCAL_PLANE_Y_RESOLUTION, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
- put(TAG_FOCAL_PLANE_RESOLUTION_UNIT, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
- put(TAG_SUBJECT_LOCATION, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 2)
- put(TAG_EXPOSURE_INDEX, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
- put(TAG_SENSING_METHOD, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
- put(TAG_FILE_SOURCE, f or (ExifTag.TYPE_UNDEFINED shl 16) or 1)
- put(TAG_SCENE_TYPE, f or (ExifTag.TYPE_UNDEFINED shl 16) or 1)
- put(TAG_CFA_PATTERN, f or (ExifTag.TYPE_UNDEFINED shl 16))
- put(TAG_CUSTOM_RENDERED, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
- put(TAG_EXPOSURE_MODE, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
- put(TAG_WHITE_BALANCE, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
- put(TAG_DIGITAL_ZOOM_RATIO, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
- put(TAG_FOCAL_LENGTH_IN_35_MM_FILE, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
- put(TAG_SCENE_CAPTURE_TYPE, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
- put(TAG_GAIN_CONTROL, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
- put(TAG_CONTRAST, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
- put(TAG_SATURATION, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
- put(TAG_SHARPNESS, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
- put(TAG_DEVICE_SETTING_DESCRIPTION, f or (ExifTag.TYPE_UNDEFINED shl 16))
- put(TAG_SUBJECT_DISTANCE_RANGE, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
- put(TAG_INTEROPERABILITY_IFD, f or (ExifTag.TYPE_UNSIGNED_LONG shl 16) or 1)
-
- // GPS tag
- f = getFlagsFromAllowedIfds(intArrayOf(IfdData.TYPE_IFD_GPS)) shl 24
- put(TAG_GPS_VERSION_ID, f or (ExifTag.TYPE_UNSIGNED_BYTE shl 16) or 4)
- put(TAG_GPS_LATITUDE_REF, f or (ExifTag.TYPE_ASCII shl 16) or 2)
- put(TAG_GPS_LONGITUDE_REF, f or (ExifTag.TYPE_ASCII shl 16) or 2)
- put(TAG_GPS_LATITUDE, f or (ExifTag.TYPE_RATIONAL shl 16) or 3)
- put(TAG_GPS_LONGITUDE, f or (ExifTag.TYPE_RATIONAL shl 16) or 3)
- put(TAG_GPS_ALTITUDE_REF, f or (ExifTag.TYPE_UNSIGNED_BYTE shl 16) or 1)
- put(TAG_GPS_ALTITUDE, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
- put(TAG_GPS_TIME_STAMP, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 3)
- put(TAG_GPS_SATTELLITES, f or (ExifTag.TYPE_ASCII shl 16))
- put(TAG_GPS_STATUS, f or (ExifTag.TYPE_ASCII shl 16) or 2)
- put(TAG_GPS_MEASURE_MODE, f or (ExifTag.TYPE_ASCII shl 16) or 2)
- put(TAG_GPS_DOP, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
- put(TAG_GPS_SPEED_REF, f or (ExifTag.TYPE_ASCII shl 16) or 2)
- put(TAG_GPS_SPEED, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
- put(TAG_GPS_TRACK_REF, f or (ExifTag.TYPE_ASCII shl 16) or 2)
- put(TAG_GPS_TRACK, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
- put(TAG_GPS_IMG_DIRECTION_REF, f or (ExifTag.TYPE_ASCII shl 16) or 2)
- put(TAG_GPS_IMG_DIRECTION, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
- put(TAG_GPS_MAP_DATUM, f or (ExifTag.TYPE_ASCII shl 16))
- put(TAG_GPS_DEST_LATITUDE_REF, f or (ExifTag.TYPE_ASCII shl 16) or 2)
- put(TAG_GPS_DEST_LATITUDE, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
- put(TAG_GPS_DEST_BEARING_REF, f or (ExifTag.TYPE_ASCII shl 16) or 2)
- put(TAG_GPS_DEST_BEARING, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
- put(TAG_GPS_DEST_DISTANCE_REF, f or (ExifTag.TYPE_ASCII shl 16) or 2)
- put(TAG_GPS_DEST_DISTANCE, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
- put(TAG_GPS_PROCESSING_METHOD, f or (ExifTag.TYPE_UNDEFINED shl 16))
- put(TAG_GPS_AREA_INFORMATION, f or (ExifTag.TYPE_UNDEFINED shl 16))
- put(TAG_GPS_DATE_STAMP, f or (ExifTag.TYPE_ASCII shl 16) or 11)
- put(TAG_GPS_DIFFERENTIAL, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 11)
-
- // Interoperability tag
- f = getFlagsFromAllowedIfds(intArrayOf(IfdData.TYPE_IFD_INTEROPERABILITY)) shl 24
- put(TAG_INTEROPERABILITY_INDEX, f or (ExifTag.TYPE_ASCII shl 16))
- put(TAG_INTEROP_VERSION, f or (ExifTag.TYPE_UNDEFINED shl 16) or 4)
-
- return this
- }
-
- }
-}
diff --git a/exif/src/main/java/it/sephiroth/android/library/exif2/ExifInvalidFormatException.kt b/exif/src/main/java/it/sephiroth/android/library/exif2/ExifInvalidFormatException.kt
deleted file mode 100644
index b5c08886..00000000
--- a/exif/src/main/java/it/sephiroth/android/library/exif2/ExifInvalidFormatException.kt
+++ /dev/null
@@ -1,19 +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
-
-class ExifInvalidFormatException(meg : String) : Exception(meg)
\ No newline at end of file
diff --git a/exif/src/main/java/it/sephiroth/android/library/exif2/ExifOutputStream.kt b/exif/src/main/java/it/sephiroth/android/library/exif2/ExifOutputStream.kt
deleted file mode 100644
index b20299c1..00000000
--- a/exif/src/main/java/it/sephiroth/android/library/exif2/ExifOutputStream.kt
+++ /dev/null
@@ -1,378 +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 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.*
-
-@Suppress("unused")
-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(
- 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...")
-
- val nullTags = stripNullValueTags(exifData)
- 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)
- val dataOutputStream =
- OrderedDataOutputStream(outputStream)
-
- 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())
- if(exifData.byteOrder == ByteOrder.BIG_ENDIAN) {
- dataOutputStream.writeShort(TIFF_BIG_ENDIAN)
- } else {
- dataOutputStream.writeShort(TIFF_LITTLE_ENDIAN)
- }
- dataOutputStream.setByteOrder(exifData.byteOrder)
- dataOutputStream.writeShort(TIFF_HEADER)
- dataOutputStream.writeInt(8)
- writeAllTags(dataOutputStream)
-
- writeThumbnail(dataOutputStream)
-
- for(t in nullTags) {
- exifData.addTag(t)
- }
-
- dataOutputStream.flush()
- }
-
- // strip tags that has null value
- // return list of removed tags
- private fun stripNullValueTags(data : ExifData) =
- ArrayList()
- .apply {
- for(t in data.allTags) {
- if(t.getValue() == null && ! ExifInterface.isOffsetTag(t.tagId)) {
- data.removeTag(t.tagId, t.ifd)
- add(t)
- }
- }
- }
-
- @Throws(IOException::class)
- private fun writeThumbnail(dataOutputStream : OrderedDataOutputStream) {
- val compressedThumbnail = exifData.compressedThumbnail
- if(compressedThumbnail != null) {
- Log.d(TAG, "writing thumbnail..")
- 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(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) {
- ifd ?: return
- val tags = ifd.allTagsCollection
- 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
-
- for(tag in ifd.allTagsCollection) {
- if(tag.dataSize > 4) {
- tag.offset = offset
- offset += tag.dataSize
- }
- }
- return offset
- }
-
- @Throws(IOException::class)
- private fun createRequiredIfdAndTag() {
-
- // IFD0 is required for all file
- var ifd0 = exifData.getIfdData(IfdData.TYPE_IFD_0)
- if(ifd0 == null) {
- 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(IfdData.TYPE_IFD_EXIF)
- if(exifIfd == null) {
- exifIfd = IfdData(IfdData.TYPE_IFD_EXIF)
- exifData.addIfdData(exifIfd)
- }
-
- // GPS IFD
- 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}")
- ifd0.setTag(gpsOffsetTag)
- }
-
- // Interoperability IFD
- 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}")
- exifIfd.setTag(interOffsetTag)
- }
-
- var ifd1 = exifData.getIfdData(IfdData.TYPE_IFD_1)
-
- // thumbnail
- val compressedThumbnail = exifData.compressedThumbnail
- val stripList = exifData.stripList
- when {
-
- compressedThumbnail != null -> {
- if(ifd1 == null) {
- ifd1 = IfdData(IfdData.TYPE_IFD_1)
- exifData.addIfdData(ifd1)
- }
-
- 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}")
-
- 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))
- }
-
- stripList != null -> {
- if(ifd1 == null) {
- ifd1 = IfdData(IfdData.TYPE_IFD_1)
- exifData.addIfdData(ifd1)
- }
- 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 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))
- 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()
-
- val ifd0 = exifData.getIfdData(IfdData.TYPE_IFD_0)?.also {
- offset = calculateOffsetOfIfd(it, offset)
- }
-
- val exifIfd = exifData.getIfdData(IfdData.TYPE_IFD_EXIF)?.also { it ->
- ifd0?.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_EXIF_IFD))
- ?.setValue(offset)
- offset = calculateOffsetOfIfd(it, offset)
- }
-
- exifData.getIfdData(IfdData.TYPE_IFD_INTEROPERABILITY)?.also { it ->
- exifIfd?.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_INTEROPERABILITY_IFD))
- ?.setValue(offset)
- offset = calculateOffsetOfIfd(it, offset)
- }
-
- exifData.getIfdData(IfdData.TYPE_IFD_GPS)?.also {
- ifd0?.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_GPS_IFD))
- ?.setValue(offset)
- 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)
- }
- }
-
- 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())
- }
- }
- }
- }
- }
-}
diff --git a/exif/src/main/java/it/sephiroth/android/library/exif2/ExifParser.kt b/exif/src/main/java/it/sephiroth/android/library/exif2/ExifParser.kt
deleted file mode 100644
index 6f4d99c1..00000000
--- a/exif/src/main/java/it/sephiroth/android/library/exif2/ExifParser.kt
+++ /dev/null
@@ -1,1165 +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 android.util.Log
-import it.sephiroth.android.library.exif2.utils.CountedDataInputStream
-import java.io.ByteArrayInputStream
-import java.io.IOException
-import java.io.InputStream
-import java.nio.ByteBuffer
-import java.nio.ByteOrder
-import java.nio.charset.Charset
-import java.util.*
-import kotlin.math.min
-
-internal open class ExifParser
-@Throws(IOException::class, ExifInvalidFormatException::class)
-private constructor(
- inputStream : InputStream,
- private val mOptions : Int,
- private val mInterface : ExifInterface
-) {
-
- // number of tags in the current IFD area.
- private var tagCountInCurrentIfd = 0
-
- /**
- * the ID of current IFD.
- *
- * @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
- * corresponding tag.
- *
- *
- * For [.EVENT_NEW_TAG], the tag may not contain the value if the size
- * of the value is greater than 4 bytes. One should call
- * [ExifTag.hasValue] to check if the tag contains value. If there
- * is no value,call [.registerForTagValue] to have the parser
- * emit [.EVENT_VALUE_OF_REGISTERED_TAG] when it reaches the area
- * pointed by the offset.
- *
- *
- * When [.EVENT_VALUE_OF_REGISTERED_TAG] is emitted, the value of the
- * tag will have already been read except for tags of undefined type. For
- * tags of undefined type, call one of the read methods to get the value.
- *
- * @see .registerForTagValue
- * @see .read
- * @see .read
- * @see .readLong
- * @see .readRational
- * @see .readString
- * @see .readString
- */
- var tag : ExifTag? = null
- private set
-
- private var mImageEvent : ImageEvent? = null
- private var mStripSizeTag : ExifTag? = null
- private var mJpegSizeTag : ExifTag? = null
- private var mNeedToParseOffsetsInCurrentIfd : Boolean = false
- private var mDataAboveIfd0 : ByteArray? = null
- private var mIfd0Position : Int = 0
-
- var qualityGuess : Int = 0
- private set
- var imageWidth : Int = 0
- private set
- var imageLength : Int = 0
- private set
- var jpegProcess : Short = 0
- private set
-
- var uncompressedDataPosition = 0
- private set
-
- private val mByteArray = ByteArray(8)
- private val mByteBuffer = ByteBuffer.wrap(mByteArray)
-
- private val isThumbnailRequested : Boolean
- get() = mOptions and ExifInterface.Options.OPTION_THUMBNAIL != 0
-
- /**
- * When receiving [.EVENT_UNCOMPRESSED_STRIP], call this function to
- * get the index of this strip.
- */
- val stripIndex : Int
- get() = mImageEvent?.stripIndex ?: 0
-
- /**
- * When receiving [.EVENT_UNCOMPRESSED_STRIP], call this function to
- * get the strip size.
- */
- val stripSize : Int
- get() = mStripSizeTag?.getValueAt(0)?.toInt() ?: 0
-
- /**
- * When receiving [.EVENT_COMPRESSED_IMAGE], call this function to get
- * the image data size.
- */
- val compressedImageSize : Int
- get() = mJpegSizeTag?.getValueAt(0)?.toInt() ?: 0
-
- /**
- * Gets the byte order of the current InputStream.
- */
- val byteOrder : ByteOrder
- get() = mTiffStream.byteOrder
-
- val sections : List
- get() = mSections
-
- private val mCorrespondingEvent = TreeMap()
-
- private val mSections = ArrayList(0)
-
- private var mIfdStartOffset = 0
-
- private val mTiffStream : CountedDataInputStream = seekTiffData(inputStream)
-
- init {
-
- // Log.d( TAG, "sections size: " + mSections.size() );
-
- val tiffStream = mTiffStream
-
- parseTiffHeader(tiffStream)
-
- val offset = tiffStream.readUnsignedInt()
- if(offset > Integer.MAX_VALUE) {
- throw ExifInvalidFormatException("Invalid offset $offset")
- }
- mIfd0Position = offset.toInt()
- currentIfd = IfdData.TYPE_IFD_0
-
- 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
- read(ba)
- }
- }
- }
-
- private fun readInt(b : ByteArray, @Suppress("SameParameterValue") offset : Int) : Int {
- mByteBuffer.rewind()
- mByteBuffer.put(b, offset, 4)
- mByteBuffer.rewind()
- return mByteBuffer.int
- }
-
- private fun readShort(b : ByteArray, @Suppress("SameParameterValue") offset : Int) : Short {
- mByteBuffer.rewind()
- mByteBuffer.put(b, offset, 2)
- mByteBuffer.rewind()
- return mByteBuffer.short
- }
-
- @Throws(IOException::class, ExifInvalidFormatException::class)
- private fun seekTiffData(inputStream : InputStream) : CountedDataInputStream {
- 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) {
- val itemlen : Int
- var marker : Int
-
- val got : Int
- val data : ByteArray
-
- var prev = 0
- a = 0
- while(true) {
- marker = dataStream.readUnsignedByte()
- if(marker != 0xff && prev == 0xff) break
- prev = marker
- a ++
- }
-
- if(a > 10) {
- Log.w(TAG, "Extraneous ${a - 1} padding bytes before section $marker")
- }
-
- // Read the length of the section.
- val lh = dataStream.readByte().toInt()
- val ll = dataStream.readByte().toInt()
- itemlen = lh and 0xff shl 8 or (ll and 0xff)
-
- if(itemlen < 2) {
- throw ExifInvalidFormatException("Invalid marker")
- }
-
- data = ByteArray(itemlen)
- data[0] = lh.toByte()
- data[1] = ll.toByte()
-
- // Log.i( TAG, "marker: " + String.format( "0x%2X", marker ) + ": " + itemlen + ", position: " + dataStream.getReadByteCount() + ", available: " + dataStream.available() );
- // got = dataStream.read( data, 2, itemlen-2 );
-
- got = readBytes(dataStream, data, 2, itemlen - 2)
-
- if(got != itemlen - 2) {
- throw ExifInvalidFormatException("Premature end of file? Expecting " + (itemlen - 2) + ", received " + got)
- }
-
- val section = Section(type = marker, size = itemlen, data = data)
-
- var ignore = false
-
- when(marker) {
- JpegHeader.TAG_M_SOS -> {
- // stop before hitting compressed data
- mSections.add(section)
- uncompressedDataPosition = dataStream.readByteCount
- return tiffStream ?: error("stop before hitting compressed data")
- }
-
- JpegHeader.TAG_M_DQT ->
- // Use for jpeg quality guessing
- process_M_DQT(data)
-
- JpegHeader.TAG_M_DHT -> {
- }
-
- // in case it's a tables-only JPEG stream
- JpegHeader.TAG_M_EOI -> {
- error("\"No image in jpeg!\"")
- }
-
- JpegHeader.TAG_M_COM ->
- // Comment section
- ignore = true
-
- JpegHeader.TAG_M_JFIF -> if(itemlen < 16) {
- ignore = true
- }
-
- JpegHeader.TAG_M_IPTC -> {
- }
-
- JpegHeader.TAG_M_SOF0, JpegHeader.TAG_M_SOF1, JpegHeader.TAG_M_SOF2, JpegHeader.TAG_M_SOF3, JpegHeader.TAG_M_SOF5, JpegHeader.TAG_M_SOF6, JpegHeader.TAG_M_SOF7, JpegHeader.TAG_M_SOF9, JpegHeader.TAG_M_SOF10, JpegHeader.TAG_M_SOF11, JpegHeader.TAG_M_SOF13, JpegHeader.TAG_M_SOF14, JpegHeader.TAG_M_SOF15 -> process_M_SOFn(
- data,
- marker
- )
-
- JpegHeader.TAG_M_EXIF -> if(itemlen >= 8) {
- val header = readInt(data, 2)
- val headerTail = readShort(data, 6)
- // header = Exif, headerTail=\0\0
- if(header == EXIF_HEADER && headerTail == EXIF_HEADER_TAIL) {
- tiffStream =
- CountedDataInputStream(
- ByteArrayInputStream(data, 8, itemlen - 8)
- )
- tiffStream.end = itemlen - 6
- ignore = false
- } else {
- Log.v(TAG, "Image cotains XMP section")
- }
- }
-
- else -> Log.w(
- TAG,
- "Unknown marker: " + String.format("0x%2X", marker) + ", length: " + itemlen
- )
- }
-
- if(! ignore) {
- // Log.d( TAG, "adding section with size: " + section.size );
- mSections.add(section)
- } else {
- Log.v(
- TAG,
- "ignoring marker: " + String.format("0x%2X", marker) + ", length: " + itemlen
- )
- }
- }
- }
-
- /**
- * Using this instead of the default [java.io.InputStream.read] because
- * on remote input streams reading large amount of data can fail
- *
- * @param dataStream
- * @param data
- * @param offsetArg
- * @param length
- * @return
- * @throws IOException
- */
- @Throws(IOException::class)
- private fun readBytes(
- dataStream : InputStream,
- data : ByteArray,
- @Suppress("SameParameterValue") offsetArg : Int,
- length : Int
- ) : Int {
- var offset = offsetArg
- var count = 0
- var n : Int
- var max_length = min(1024, length)
- while(true) {
- n = dataStream.read(data, offset, max_length)
- if(n <= 0) break
- count += n
- offset += n
- max_length = min(max_length, length - count)
- }
- return count
- }
-
- private fun process_M_SOFn(data : ByteArray, marker : Int) {
- if(data.size > 7) {
- //int data_precision = data[2] & 0xff;
- //int num_components = data[7] & 0xff;
- imageLength = Get16m(data, 3)
- imageWidth = Get16m(data, 5)
- }
- jpegProcess = marker.toShort()
- }
-
- private fun process_M_DQT(data : ByteArray) {
- var a = 2
- var c : Int
- var tableindex : Int
- var coefindex : Int
- var cumsf = 0.0
- var reftable : IntArray? = null
- var allones = 1
-
- while(a < data.size) {
- c = data[a ++].toInt()
- tableindex = c and 0x0f
-
- if(tableindex < 2) {
- reftable = deftabs[tableindex]
- }
-
- // Read in the table, compute statistics relative to reference table
- coefindex = 0
- while(coefindex < 64) {
- val `val` : Int
- if(c shr 4 != 0) {
- var temp : Int
- temp = data[a ++].toInt()
- temp *= 256
- `val` = data[a ++].toInt() + temp
- } else {
- `val` = data[a ++].toInt()
- }
- if(reftable != null) {
- val x : Double
- // scaling factor in percent
- x = 100.0 * `val`.toDouble() / reftable[coefindex].toDouble()
- cumsf += x
- // separate check for all-ones table (Q 100)
- if(`val` != 1) allones = 0
- }
- coefindex ++
- }
- // Print summary stats
- if(reftable != null) { // terse output includes quality
- val qual : Double
- cumsf /= 64.0 // mean scale factor
-
- qual = when {
- allones != 0 -> 100.0 // special case for all-ones table
- cumsf <= 100.0 -> (200.0 - cumsf) / 2.0
- else -> 5000.0 / cumsf
- }
-
- if(tableindex == 0) {
- qualityGuess = (qual + 0.5).toInt()
- // Log.v( TAG, "quality guess: " + mQualityGuess );
- }
- }
- }
- }
-
- @Throws(IOException::class, ExifInvalidFormatException::class)
- private fun parseTiffHeader(stream : CountedDataInputStream) {
-
- stream.byteOrder = when(stream.readShort()) {
- LITTLE_ENDIAN_TAG -> ByteOrder.LITTLE_ENDIAN
- BIG_ENDIAN_TAG -> ByteOrder.BIG_ENDIAN
- else -> throw ExifInvalidFormatException("Invalid TIFF header")
- }
-
- if(stream.readShort() != TIFF_HEADER_TAIL) {
- throw ExifInvalidFormatException("Invalid TIFF header")
- }
- }
-
- private fun isIfdRequested(ifdType : Int) : Boolean {
- when(ifdType) {
- 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
- }
-
- private fun needToParseOffsetsInCurrentIfd() : Boolean {
- return when(currentIfd) {
-
- IfdData.TYPE_IFD_0 ->
- isIfdRequested(IfdData.TYPE_IFD_EXIF) ||
- isIfdRequested(IfdData.TYPE_IFD_GPS) ||
- isIfdRequested(IfdData.TYPE_IFD_INTEROPERABILITY) ||
- isIfdRequested(IfdData.TYPE_IFD_1)
-
- IfdData.TYPE_IFD_1 -> isThumbnailRequested
-
- IfdData.TYPE_IFD_EXIF ->
- // The offset to interoperability IFD is located in Exif IFD
- isIfdRequested(IfdData.TYPE_IFD_INTEROPERABILITY)
-
- else -> false
- }
- }
-
- private fun registerIfd(ifdType : Int, offset : Long) {
- // Cast unsigned int to int since the offset is always smaller
- // than the size of M_EXIF (65536)
- mCorrespondingEvent[offset.toInt()] = IfdEvent(ifdType, isIfdRequested(ifdType))
- }
-
- /**
- * Equivalent to read(buffer, 0, buffer.length).
- */
- @Throws(IOException::class)
- fun read(buffer : ByteArray) : Int = mTiffStream.read(buffer)
-
- //
- // /**
- // * Parses the the given InputStream with default options; that is, every IFD
- // * and thumbnaill will be parsed.
- // *
- // * @throws java.io.IOException
- // * @throws ExifInvalidFormatException
- // */
- // protected static ExifParser parse( InputStream inputStream, boolean requestThumbnail, ExifInterface iRef ) throws IOException, ExifInvalidFormatException {
- // return new ExifParser( inputStream, OPTION_IFD_0 | OPTION_IFD_1 | OPTION_IFD_EXIF | OPTION_IFD_GPS | OPTION_IFD_INTEROPERABILITY | ( requestThumbnail ? OPTION_THUMBNAIL : 0 ), iRef );
- // }
-
- /**
- * Moves the parser forward and returns the next parsing event
- *
- * @throws java.io.IOException
- * @throws ExifInvalidFormatException
- * @see .EVENT_START_OF_IFD
- * @see .EVENT_NEW_TAG
- * @see .EVENT_VALUE_OF_REGISTERED_TAG
- * @see .EVENT_COMPRESSED_IMAGE
- * @see .EVENT_UNCOMPRESSED_STRIP
- * @see .EVENT_END
- */
- @Throws(IOException::class, ExifInvalidFormatException::class)
- operator fun next() : Int {
-
- val offset = mTiffStream.readByteCount
- val endOfTags = mIfdStartOffset + OFFSET_SIZE + TAG_SIZE * tagCountInCurrentIfd
- if(offset < endOfTags) {
- val tag = readTag()
- this.tag = tag
- if(tag == null) {
- return next()
- } else if(mNeedToParseOffsetsInCurrentIfd) {
- checkOffsetOrImageTag(tag)
- }
- return EVENT_NEW_TAG
- } else if(offset == endOfTags) {
- // There is a link to ifd1 at the end of ifd0
- if(currentIfd == IfdData.TYPE_IFD_0) {
- val ifdOffset = readUnsignedLong()
- if(isIfdRequested(IfdData.TYPE_IFD_1) || isThumbnailRequested) {
- if(ifdOffset != 0L) {
- registerIfd(IfdData.TYPE_IFD_1, ifdOffset)
- }
- }
- } else {
- var offsetSize = 4
- // Some camera models use invalid length of the offset
- if(mCorrespondingEvent.size > 0) {
- val firstEntry = mCorrespondingEvent.firstEntry() !!
- offsetSize = firstEntry.key - mTiffStream.readByteCount
- }
- if(offsetSize < 4) {
- Log.w(TAG, "Invalid size of link to next IFD: $offsetSize")
- } else {
- val ifdOffset = readUnsignedLong()
- if(ifdOffset != 0L) {
- Log.w(TAG, "Invalid link to next IFD: $ifdOffset")
- }
- }
- }
- }
- while(mCorrespondingEvent.size != 0) {
- val entry = mCorrespondingEvent.pollFirstEntry() !!
- val event = entry.value
- try {
- // Log.v(TAG, "skipTo: " + entry.getKey());
- skipTo(entry.key)
- } catch(e : IOException) {
- Log.w(
- TAG,
- "Failed to skip to data at: ${entry.key} for ${event.javaClass.name}, the file may be broken."
- )
- continue
- }
-
- if(event is IfdEvent) {
- currentIfd = event.ifd
- tagCountInCurrentIfd = mTiffStream.readUnsignedShort()
- mIfdStartOffset = entry.key
-
- if(tagCountInCurrentIfd * TAG_SIZE + mIfdStartOffset + OFFSET_SIZE > mTiffStream.end) {
- Log.w(TAG, "Invalid size of IFD $currentIfd")
- return EVENT_END
- }
-
- mNeedToParseOffsetsInCurrentIfd = needToParseOffsetsInCurrentIfd()
- if(event.isRequested) {
- return EVENT_START_OF_IFD
- } else {
- skipRemainingTagsInCurrentIfd()
- }
- } else if(event is ImageEvent) {
- mImageEvent = event
- return event.type
- } else {
- val tagEvent = event as ExifTagEvent
- val tag = tagEvent.tag
- this.tag = tag
- if(tag.dataType != ExifTag.TYPE_UNDEFINED) {
- readFullTagValue(tag)
- checkOffsetOrImageTag(tag)
- }
- if(tagEvent.isRequested) {
- return EVENT_VALUE_OF_REGISTERED_TAG
- }
- }
- }
- return EVENT_END
- }
-
- /**
- * Skips the tags area of current IFD, if the parser is not in the tag area,
- * nothing will happen.
- *
- * @throws java.io.IOException
- * @throws ExifInvalidFormatException
- */
- @Throws(IOException::class, ExifInvalidFormatException::class)
- protected fun skipRemainingTagsInCurrentIfd() {
-
- val endOfTags = mIfdStartOffset + OFFSET_SIZE + TAG_SIZE * tagCountInCurrentIfd
- var offset = mTiffStream.readByteCount
- if(offset > endOfTags) return
-
- if(mNeedToParseOffsetsInCurrentIfd) {
- while(offset < endOfTags) {
- val tag = readTag()
- this.tag = tag
- offset += TAG_SIZE
- if(tag == null) {
- continue
- }
- checkOffsetOrImageTag(tag)
- }
- } else {
- skipTo(endOfTags)
- }
- val ifdOffset = readUnsignedLong()
- // For ifd0, there is a link to ifd1 in the end of all tags
- if(currentIfd == IfdData.TYPE_IFD_0 && (isIfdRequested(IfdData.TYPE_IFD_1) || isThumbnailRequested)) {
- if(ifdOffset > 0) {
- registerIfd(IfdData.TYPE_IFD_1, ifdOffset)
- }
- }
- }
-
- @Throws(IOException::class)
- private fun skipTo(offset : Int) {
- mTiffStream.skipTo(offset.toLong())
-
- // Log.v(TAG, "available: " + mTiffStream.available() );
- while(! mCorrespondingEvent.isEmpty() && mCorrespondingEvent.firstKey() < offset) {
- mCorrespondingEvent.pollFirstEntry()
- }
- }
-
- /**
- * When getting [.EVENT_NEW_TAG] in the tag area of IFD, the tag may
- * not contain the value if the size of the value is greater than 4 bytes.
- * When the value is not available here, call this method so that the parser
- * will emit [.EVENT_VALUE_OF_REGISTERED_TAG] when it reaches the area
- * where the value is located.
- *
- * @see .EVENT_VALUE_OF_REGISTERED_TAG
- */
- fun registerForTagValue(tag : ExifTag) {
- if(tag.offset >= mTiffStream.readByteCount) {
- mCorrespondingEvent[tag.offset] = ExifTagEvent(tag, true)
- }
- }
-
- private fun registerCompressedImage(offset : Long) {
- mCorrespondingEvent[offset.toInt()] = ImageEvent(EVENT_COMPRESSED_IMAGE)
- }
-
- private fun registerUncompressedStrip(stripIndex : Int, offset : Long) {
- mCorrespondingEvent[offset.toInt()] = ImageEvent(EVENT_UNCOMPRESSED_STRIP, stripIndex)
- }
-
- @Throws(IOException::class, ExifInvalidFormatException::class)
- private fun readTag() : ExifTag? {
-
- val tagId = mTiffStream.readShort()
- val dataFormat = mTiffStream.readShort()
- val numOfComp = mTiffStream.readUnsignedInt()
- if(numOfComp > Integer.MAX_VALUE) {
- throw ExifInvalidFormatException("Number of component is larger then Integer.MAX_VALUE")
- }
- // Some invalid image file contains invalid data type. Ignore those tags
- if(! ExifTag.isValidType(dataFormat)) {
- Log.w(TAG, String.format("Tag %04x: Invalid data type %d", tagId, dataFormat))
- mTiffStream.skip(4)
- return null
- }
- // TODO: handle numOfComp overflow
- val tag = ExifTag(
- tagId,
- dataFormat,
- numOfComp.toInt(),
- currentIfd,
- numOfComp.toInt() != ExifTag.SIZE_UNDEFINED
- )
- val dataSize = tag.dataSize
- if(dataSize > 4) {
- val offset = mTiffStream.readUnsignedInt()
- if(offset > Integer.MAX_VALUE) {
- throw ExifInvalidFormatException("offset is larger then Integer.MAX_VALUE")
- }
- // Some invalid images put some undefined data before IFD0.
- // Read the data here.
- if(offset < mIfd0Position && dataFormat == ExifTag.TYPE_UNDEFINED) {
- val buf = ByteArray(numOfComp.toInt())
- System.arraycopy(
- mDataAboveIfd0 !!,
- offset.toInt() - DEFAULT_IFD0_OFFSET,
- buf,
- 0,
- numOfComp.toInt()
- )
- tag.setValue(buf)
- } else {
- tag.offset = offset.toInt()
- }
- } else {
- val defCount = tag.hasDefinedCount
- // Set defined count to 0 so we can add \0 to non-terminated strings
- tag.hasDefinedCount = false
- // Read value
- readFullTagValue(tag)
- tag.hasDefinedCount = defCount
- mTiffStream.skip((4 - dataSize).toLong())
- // Set the offset to the position of value.
- tag.offset = mTiffStream.readByteCount - 4
- }
- return tag
- }
-
- /**
- * Check the tag, if the tag is one of the offset tag that points to the IFD
- * or image the caller is interested in, register the IFD or image.
- */
- private fun checkOffsetOrImageTag(tag : ExifTag) {
- // Some invalid formattd image contains tag with 0 size.
- if(tag.componentCount == 0) {
- return
- }
- val tid = tag.tagId
- val ifd = tag.ifd
- if(tid == TAG_EXIF_IFD && checkAllowed(ifd, ExifInterface.TAG_EXIF_IFD)) {
- 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(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(IfdData.TYPE_IFD_INTEROPERABILITY)) {
- registerIfd(IfdData.TYPE_IFD_INTEROPERABILITY, tag.getValueAt(0))
- }
- } else if(tid == TAG_JPEG_INTERCHANGE_FORMAT && checkAllowed(
- ifd,
- ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT
- )) {
- if(isThumbnailRequested) {
- registerCompressedImage(tag.getValueAt(0))
- }
- } else if(tid == TAG_JPEG_INTERCHANGE_FORMAT_LENGTH && checkAllowed(
- ifd,
- ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH
- )) {
- if(isThumbnailRequested) {
- mJpegSizeTag = tag
- }
- } else if(tid == TAG_STRIP_OFFSETS && checkAllowed(ifd, ExifInterface.TAG_STRIP_OFFSETS)) {
- if(isThumbnailRequested) {
- if(tag.hasValue) {
- for(i in 0 until tag.componentCount) {
- if(tag.dataType == ExifTag.TYPE_UNSIGNED_SHORT) {
- registerUncompressedStrip(i, tag.getValueAt(i))
- } else {
- registerUncompressedStrip(i, tag.getValueAt(i))
- }
- }
- } else {
- mCorrespondingEvent[tag.offset] = ExifTagEvent(tag, false)
- }
- }
- } else if(tid == TAG_STRIP_BYTE_COUNTS && checkAllowed(
- ifd,
- ExifInterface.TAG_STRIP_BYTE_COUNTS
- ) && isThumbnailRequested && tag.hasValue) {
- mStripSizeTag = tag
- }
- }
-
- fun isDefinedTag(ifdId : Int, tagId : Short) : Boolean {
- return mInterface.tagInfo.get(
- ExifInterface.defineTag(
- ifdId,
- tagId
- )
- ) != ExifInterface.DEFINITION_NULL
- }
-
- private fun checkAllowed(ifd : Int, tagId : Int) : Boolean {
- val info = mInterface.tagInfo.get(tagId)
- return if(info == ExifInterface.DEFINITION_NULL) {
- false
- } else ExifInterface.isIfdAllowed(info, ifd)
- }
-
- @Throws(IOException::class)
- fun readFullTagValue(tag : ExifTag) {
-
- // Some invalid images contains tags with wrong size, check it here
- val type = tag.dataType
- val componentCount = tag.componentCount
-
- // sanity check
- if(componentCount >= 0x66000000) throw IOException("size out of bounds")
-
- if(type == ExifTag.TYPE_ASCII || type == ExifTag.TYPE_UNDEFINED ||
- type == ExifTag.TYPE_UNSIGNED_BYTE) {
- var size = tag.componentCount
- if(mCorrespondingEvent.size > 0) {
- val firstEntry = mCorrespondingEvent.firstEntry() !!
- if(firstEntry.key < mTiffStream.readByteCount + size) {
- val event = firstEntry.value
- if(event is ImageEvent) {
- // Tag value overlaps thumbnail, ignore thumbnail.
- Log.w(TAG, "Thumbnail overlaps value for tag: \n$tag")
- val entry = mCorrespondingEvent.pollFirstEntry() !!
- Log.w(TAG, "Invalid thumbnail offset: " + entry.key)
- } else {
- // Tag value overlaps another tag, shorten count
- if(event is IfdEvent) {
- Log.w(TAG, "Ifd ${event.ifd} overlaps value for tag: \n$tag")
- } else if(event is ExifTagEvent) {
- Log.w(
- TAG,
- "Tag value for tag: \n${event.tag} overlaps value for tag: \n$tag"
- )
- }
- size = firstEntry.key - mTiffStream.readByteCount
- Log.w(TAG, "Invalid size of tag: \n$tag setting count to: $size")
- tag.forceSetComponentCount(size)
- }
- }
- }
- }
- when(tag.dataType) {
- ExifTag.TYPE_UNSIGNED_BYTE, ExifTag.TYPE_UNDEFINED ->
- tag.setValue(ByteArray(componentCount).also { read(it) })
-
- ExifTag.TYPE_ASCII ->
- tag.setValue(readString(componentCount))
-
- ExifTag.TYPE_UNSIGNED_SHORT ->
- tag.setValue(IntArray(componentCount) { readUnsignedShort() })
-
- ExifTag.TYPE_LONG ->
- tag.setValue(IntArray(componentCount) { readLong().toInt() })
- ExifTag.TYPE_UNSIGNED_LONG ->
- tag.setValue(LongArray(componentCount) { readUnsignedLong() })
-
- ExifTag.TYPE_RATIONAL ->
- tag.setValue(Array(componentCount) { readRational() })
- ExifTag.TYPE_UNSIGNED_RATIONAL ->
- tag.setValue(Array(componentCount) { readUnsignedRational() })
-
- }
-
- // Log.v( TAG, "\n" + tag.toString() );
- }
-
- /**
- * Reads bytes from the InputStream.
- */
- @Throws(IOException::class)
- protected fun read(buffer : ByteArray, offset : Int, length : Int) : Int =
- mTiffStream.read(buffer, offset, length)
-
- /**
- * Reads a String from the InputStream with US-ASCII charset. The parser
- * will read n bytes and convert it to ascii string. This is used for
- * reading values of type [ExifTag.TYPE_ASCII].
- */
-
- /**
- * Reads a String from the InputStream with the given charset. The parser
- * will read n bytes and convert it to string. This is used for reading
- * values of type [ExifTag.TYPE_ASCII].
- */
- @Throws(IOException::class)
- @JvmOverloads
- protected fun readString(n : Int, charset : Charset = US_ASCII) : String =
- when {
- n <= 0 -> ""
- else -> mTiffStream.readString(n, charset)
- }
-
- /**
- * Reads value of type [ExifTag.TYPE_UNSIGNED_SHORT] from the
- * InputStream.
- */
- @Throws(IOException::class)
- protected fun readUnsignedShort() : Int =
- mTiffStream.readShort().toInt() and 0xffff
-
- /**
- * Reads value of type [ExifTag.TYPE_UNSIGNED_LONG] from the
- * InputStream.
- */
- @Throws(IOException::class)
- protected fun readUnsignedLong() : Long {
- return readLong() and 0xffffffffL
- }
-
- /**
- * Reads value of type [ExifTag.TYPE_UNSIGNED_RATIONAL] from the
- * InputStream.
- */
- @Throws(IOException::class)
- protected fun readUnsignedRational() : Rational {
- val nomi = readUnsignedLong()
- val denomi = readUnsignedLong()
- return Rational(nomi, denomi)
- }
-
- /**
- * Reads value of type [ExifTag.TYPE_LONG] from the InputStream.
- */
- @Throws(IOException::class)
- protected fun readLong() : Long =
- mTiffStream.readInt().toLong()
-
- /**
- * Reads value of type [ExifTag.TYPE_RATIONAL] from the InputStream.
- */
- @Throws(IOException::class)
- protected fun readRational() : Rational {
- val nomi = readLong()
- val denomi = readLong()
- return Rational(nomi, denomi)
- }
-
- private class ImageEvent {
- internal var stripIndex : Int = 0
- internal var type : Int = 0
-
- internal constructor(type : Int) {
- this.stripIndex = 0
- this.type = type
- }
-
- internal constructor(type : Int, stripIndex : Int) {
- this.type = type
- this.stripIndex = stripIndex
- }
- }
-
- private class IfdEvent internal constructor(
- internal var ifd : Int,
- internal var isRequested : Boolean
- )
-
- private class ExifTagEvent internal constructor(
- internal var tag : ExifTag,
- internal var isRequested : Boolean
- )
-
- class Section(var size : Int, var type : Int, var data : ByteArray)
-
- companion object {
- private const val TAG = "ExifParser"
-
- /**
- * When the parser reaches a new IFD area. Call [.getCurrentIfd] to
- * know which IFD we are in.
- */
- const val EVENT_START_OF_IFD = 0
- /**
- * When the parser reaches a new tag. Call [.getTag]to get the
- * corresponding tag.
- */
- const val EVENT_NEW_TAG = 1
- /**
- * When the parser reaches the value area of tag that is registered by
- * [.registerForTagValue] previously. Call [.getTag]
- * to get the corresponding tag.
- */
- const val EVENT_VALUE_OF_REGISTERED_TAG = 2
- /**
- * When the parser reaches the compressed image area.
- */
- const val EVENT_COMPRESSED_IMAGE = 3
- /**
- * When the parser reaches the uncompressed image strip. Call
- * [.getStripIndex] to get the index of the strip.
- *
- * @see .getStripIndex
- */
- const val EVENT_UNCOMPRESSED_STRIP = 4
- /**
- * When there is nothing more to parse.
- */
- const val EVENT_END = 5
-
- protected const val EXIF_HEADER = 0x45786966 // EXIF header "Exif"
- protected const val EXIF_HEADER_TAIL = 0x0000.toShort() // EXIF header in M_EXIF
- // TIFF header
- protected const val LITTLE_ENDIAN_TAG = 0x4949.toShort() // "II"
- protected const val BIG_ENDIAN_TAG = 0x4d4d.toShort() // "MM"
- protected const val TIFF_HEADER_TAIL : Short = 0x002A
- protected const val TAG_SIZE = 12
- protected const val OFFSET_SIZE = 2
- protected const val DEFAULT_IFD0_OFFSET = 8
- private val US_ASCII = Charset.forName("US-ASCII")
- private val TAG_EXIF_IFD = ExifInterface.getTrueTagKey(ExifInterface.TAG_EXIF_IFD)
- private val TAG_GPS_IFD = ExifInterface.getTrueTagKey(ExifInterface.TAG_GPS_IFD)
- private val TAG_INTEROPERABILITY_IFD =
- ExifInterface.getTrueTagKey(ExifInterface.TAG_INTEROPERABILITY_IFD)
- private val TAG_JPEG_INTERCHANGE_FORMAT =
- ExifInterface.getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT)
- private val TAG_JPEG_INTERCHANGE_FORMAT_LENGTH =
- ExifInterface.getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH)
- private val TAG_STRIP_OFFSETS = ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS)
- private val TAG_STRIP_BYTE_COUNTS =
- ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_BYTE_COUNTS)
-
- private val std_luminance_quant_tbl : IntArray
- private val std_chrominance_quant_tbl : IntArray
- val deftabs : Array
-
- init {
- std_luminance_quant_tbl = intArrayOf(
- 16,
- 11,
- 12,
- 14,
- 12,
- 10,
- 16,
- 14,
- 13,
- 14,
- 18,
- 17,
- 16,
- 19,
- 24,
- 40,
- 26,
- 24,
- 22,
- 22,
- 24,
- 49,
- 35,
- 37,
- 29,
- 40,
- 58,
- 51,
- 61,
- 60,
- 57,
- 51,
- 56,
- 55,
- 64,
- 72,
- 92,
- 78,
- 64,
- 68,
- 87,
- 69,
- 55,
- 56,
- 80,
- 109,
- 81,
- 87,
- 95,
- 98,
- 103,
- 104,
- 103,
- 62,
- 77,
- 113,
- 121,
- 112,
- 100,
- 120,
- 92,
- 101,
- 103,
- 99
- )
-
- std_chrominance_quant_tbl = intArrayOf(
- 17,
- 18,
- 18,
- 24,
- 21,
- 24,
- 47,
- 26,
- 26,
- 47,
- 99,
- 66,
- 56,
- 66,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99,
- 99
- )
-
- deftabs = arrayOf(std_luminance_quant_tbl, std_chrominance_quant_tbl)
- }
-
- fun Get16m(data : ByteArray, position : Int) : Int {
- val b1 = data[position].toInt() and 0xFF shl 8
- val b2 = data[position + 1].toInt() and 0xFF
- return b1 or b2
- }
-
- /**
- * Parses the the given InputStream with the given options
- *
- * @throws java.io.IOException
- * @throws ExifInvalidFormatException
- */
- @Throws(IOException::class, ExifInvalidFormatException::class)
- fun parse(inputStream : InputStream, options : Int, iRef : ExifInterface) : ExifParser =
- ExifParser(inputStream, options, iRef)
- }
-}
\ No newline at end of file
diff --git a/exif/src/main/java/it/sephiroth/android/library/exif2/ExifReader.kt b/exif/src/main/java/it/sephiroth/android/library/exif2/ExifReader.kt
deleted file mode 100644
index 078dcfe0..00000000
--- a/exif/src/main/java/it/sephiroth/android/library/exif2/ExifReader.kt
+++ /dev/null
@@ -1,114 +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 android.util.Log
-
-import java.io.IOException
-import java.io.InputStream
-
-/**
- * This class reads the EXIF header of a JPEG file and stores it in
- * [ExifData].
- */
-internal class ExifReader(private val mInterface : ExifInterface) {
-
- /**
- * Parses the inputStream and and returns the EXIF data in an
- * [ExifData].
- *
- * @throws ExifInvalidFormatException
- * @throws java.io.IOException
- */
- @Throws(ExifInvalidFormatException::class, IOException::class)
- fun read(inputStream : InputStream, options : Int) : ExifData {
- val parser = ExifParser.parse(inputStream, options, mInterface)
- 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
-
- if(w > 0 && h > 0) {
- exifData.setImageSize(w, h)
- }
-
- var event = parser.next()
- while(event != ExifParser.EVENT_END) {
- when(event) {
-
- ExifParser.EVENT_START_OF_IFD ->
- exifData.addIfdData(IfdData(parser.currentIfd))
-
- ExifParser.EVENT_NEW_TAG -> {
- 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 -> {
- val tag = parser.tag !!
- if(tag.dataType == ExifTag.TYPE_UNDEFINED) {
- parser.readFullTagValue(tag)
- }
- exifData.getIfdData(tag.ifd) !!.setTag(tag)
- }
-
- ExifParser.EVENT_COMPRESSED_IMAGE -> {
- val buf = ByteArray(parser.compressedImageSize)
- if(buf.size == parser.read(buf)) {
- exifData.compressedThumbnail = buf
- } else {
- Log.w(TAG, "Failed to read the compressed thumbnail")
- }
- }
-
- ExifParser.EVENT_UNCOMPRESSED_STRIP -> {
- val buf = ByteArray(parser.stripSize)
- if(buf.size == parser.read(buf)) {
- exifData.setStripBytes(parser.stripIndex, buf)
- } else {
- Log.w(TAG, "Failed to read the strip bytes")
- }
- }
- }
- event = parser.next()
- }
- return exifData
- }
-
- companion object {
- private const val TAG = "ExifReader"
- }
-}
diff --git a/exif/src/main/java/it/sephiroth/android/library/exif2/ExifTag.kt b/exif/src/main/java/it/sephiroth/android/library/exif2/ExifTag.kt
deleted file mode 100644
index d7b15231..00000000
--- a/exif/src/main/java/it/sephiroth/android/library/exif2/ExifTag.kt
+++ /dev/null
@@ -1,842 +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.nio.charset.Charset
-import java.text.SimpleDateFormat
-import java.util.*
-
-/**
- * This class stores information of an EXIF tag. For more information about
- * defined EXIF tags, please read the Jeita EXIF 2.2 standard. Tags should be
- * instantiated using [ExifInterface.buildTag].
- *
- * @see ExifInterface
- */
-// Use builtTag in ExifInterface instead of constructor.
-@Suppress("unused")
-open class ExifTag internal constructor(
-
- // Exif TagId. the TID of this tag.
- val tagId : Short,
-
- // Exif Tag Type. the data type of this tag
-
- /*
- * @see .TYPE_ASCII
- * @see .TYPE_LONG
- * @see .TYPE_RATIONAL
- * @see .TYPE_UNDEFINED
- * @see .TYPE_UNSIGNED_BYTE
- * @see .TYPE_UNSIGNED_LONG
- * @see .TYPE_UNSIGNED_RATIONAL
- * @see .TYPE_UNSIGNED_SHORT
- */
- val dataType : Short,
-
- componentCount : Int,
-
- // The ifd that this tag should be put in. the ID of the IFD this tag belongs to.
- /*
- * @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,
-
- // If tag has defined count
- private var mHasDefinedDefaultComponentCount : Boolean
-) {
- // Actual data count in tag (should be number of elements in value array)
- /**
- * Gets the component count of this tag.
- */
-
- // TODO: fix integer overflows with this
- var componentCount : Int = componentCount
- private set
-
- // The value (array of elements of type Tag Type)
- private var mValue : Any? = null
-
- // Value offset in exif header. the offset of this tag.
- // This is only valid if this data size > 4 and contains an offset to the location of the actual value.
- var offset : Int = 0
-
- // the total data size in bytes of the value of this tag.
- val dataSize : Int
- get() = componentCount * getElementSize(dataType)
-
- /**
- * Returns true if this ExifTag contains value; otherwise, this tag will
- * contain an offset value that is determined when the tag is written.
- */
- val hasValue :Boolean
- get() = mValue != null
-
- /**
- * Gets the value as a byte array. This method should be used for tags of
- * type [.TYPE_UNDEFINED] or [.TYPE_UNSIGNED_BYTE].
- *
- * @return the value as a byte array, or null if the tag's value does not
- * exist or cannot be converted to a byte array.
- */
- val valueAsBytes : ByteArray?
- get() = mValue as? ByteArray
-
- /**
- * Gets the value as an array of longs. This method should be used for tags
- * of type [.TYPE_UNSIGNED_LONG].
- *
- * @return the value as as an array of longs, or null if the tag's value
- * does not exist or cannot be converted to an array of longs.
- */
- val valueAsLongs : LongArray?
- get() = mValue as? LongArray
-
- /**
- * Gets the value as an array of Rationals. This method should be used for
- * tags of type [.TYPE_RATIONAL] or [.TYPE_UNSIGNED_RATIONAL].
- *
- * @return the value as as an array of Rationals, or null if the tag's value
- * does not exist or cannot be converted to an array of Rationals.
- */
- @Suppress("UNCHECKED_CAST")
- val valueAsRationals : Array?
- get() = mValue as? Array
-
- /**
- * Gets the value as an array of ints. This method should be used for tags
- * of type [.TYPE_UNSIGNED_SHORT], [.TYPE_UNSIGNED_LONG].
- *
- * @return the value as as an array of ints, or null if the tag's value does
- * not exist or cannot be converted to an array of ints.
- */
- // Truncates
- val valueAsInts : IntArray?
- get() = when(val v = mValue) {
- is LongArray -> IntArray(v.size) { v[it].toInt() }
- else -> null
- }
-
- /**
- * Gets the value as a String. This method should be used for tags of type
- * [.TYPE_ASCII].
- *
- * @return the value as a String, or null if the tag's value does not exist
- * or cannot be converted to a String.
- */
- val valueAsString : String?
- get() = when(val v = mValue) {
- is String -> v
- is ByteArray -> String(v, US_ASCII)
- else -> null
- }
-
- /**
- * Gets the [.TYPE_ASCII] data.
- *
- * @throws IllegalArgumentException If the type is NOT
- * [.TYPE_ASCII].
- */
- protected val string : String
- get() = valueAsString !!
-
- /*
- * Get the converted ascii byte. Used by ExifOutputStream.
- */
- val stringByte : ByteArray?
- get() = mValue as? ByteArray
-
- /**
- * Sets the component count of this tag. Call this function before
- * setValue() if the length of value does not match the component count.
- */
- fun forceSetComponentCount(count : Int) {
- componentCount = count
- }
-
- /**
- * Sets integer values into this tag. This method should be used for tags of
- * type [.TYPE_UNSIGNED_SHORT]. This method will fail if:
- *
- * * The component type of this tag is not [.TYPE_UNSIGNED_SHORT],
- * [.TYPE_UNSIGNED_LONG], or [.TYPE_LONG].
- * * The value overflows.
- * * The value.length does NOT match the component count in the definition
- * for this tag.
- *
- */
- fun setValue(value : IntArray) : Boolean {
- if(checkBadComponentCount(value.size)) {
- return false
- }
- if(dataType != TYPE_UNSIGNED_SHORT && dataType != TYPE_LONG &&
- dataType != TYPE_UNSIGNED_LONG) {
- return false
- }
- if(dataType == TYPE_UNSIGNED_SHORT && checkOverflowForUnsignedShort(value)) {
- return false
- } else if(dataType == TYPE_UNSIGNED_LONG && checkOverflowForUnsignedLong(value)) {
- return false
- }
-
- val data = LongArray(value.size)
- for(i in value.indices) {
- data[i] = value[i].toLong()
- }
- mValue = data
- componentCount = value.size
- return true
- }
-
- /**
- * Sets integer value into this tag. This method should be used for tags of
- * type [.TYPE_UNSIGNED_SHORT], or [.TYPE_LONG]. This method
- * will fail if:
- *
- * * The component type of this tag is not [.TYPE_UNSIGNED_SHORT],
- * [.TYPE_UNSIGNED_LONG], or [.TYPE_LONG].
- * * The value overflows.
- * * The component count in the definition of this tag is not 1.
- *
- */
- fun setValue(value : Int) = setValue(intArrayOf(value))
-
- /**
- * Sets long values into this tag. This method should be used for tags of
- * type [.TYPE_UNSIGNED_LONG]. This method will fail if:
- *
- * * The component type of this tag is not [.TYPE_UNSIGNED_LONG].
- * * The value overflows.
- * * The value.length does NOT match the component count in the definition
- * for this tag.
- *
- */
- fun setValue(value : LongArray) : Boolean {
- if(checkBadComponentCount(value.size) || dataType != TYPE_UNSIGNED_LONG) {
- return false
- }
- if(checkOverflowForUnsignedLong(value)) {
- return false
- }
- mValue = value
- componentCount = value.size
- return true
- }
-
- /**
- * Sets long values into this tag. This method should be used for tags of
- * type [.TYPE_UNSIGNED_LONG]. This method will fail if:
- *
- * * The component type of this tag is not [.TYPE_UNSIGNED_LONG].
- * * The value overflows.
- * * The component count in the definition for this tag is not 1.
- *
- */
- fun setValue(value : Long) = setValue(longArrayOf(value))
-
- /**
- * Sets Rational values into this tag. This method should be used for tags
- * of type [.TYPE_UNSIGNED_RATIONAL], or [.TYPE_RATIONAL]. This
- * method will fail if:
- *
- * * The component type of this tag is not [.TYPE_UNSIGNED_RATIONAL]
- * or [.TYPE_RATIONAL].
- * * The value overflows.
- * * The value.length does NOT match the component count in the definition
- * for this tag.
- *
- *
- * @see Rational
- */
- fun setValue(value : Array) : Boolean {
- if(checkBadComponentCount(value.size)) {
- return false
- }
- if(dataType != TYPE_UNSIGNED_RATIONAL && dataType != TYPE_RATIONAL) {
- return false
- }
- if(dataType == TYPE_UNSIGNED_RATIONAL && checkOverflowForUnsignedRational(value)) {
- return false
- } else if(dataType == TYPE_RATIONAL && checkOverflowForRational(value)) {
- return false
- }
-
- mValue = value
- componentCount = value.size
- return true
- }
-
- /**
- * Sets a Rational value into this tag. This method should be used for tags
- * of type [.TYPE_UNSIGNED_RATIONAL], or [.TYPE_RATIONAL]. This
- * method will fail if:
- *
- * * The component type of this tag is not [.TYPE_UNSIGNED_RATIONAL]
- * or [.TYPE_RATIONAL].
- * * The value overflows.
- * * The component count in the definition for this tag is not 1.
- *
- *
- * @see Rational
- */
- fun setValue(value : Rational) =setValue(arrayOf(value))
-
- /**
- * Sets byte values into this tag. This method should be used for tags of
- * type [.TYPE_UNSIGNED_BYTE] or [.TYPE_UNDEFINED]. This method
- * will fail if:
- *
- * * The component type of this tag is not [.TYPE_UNSIGNED_BYTE] or
- * [.TYPE_UNDEFINED] .
- * * The length does NOT match the component count in the definition for
- * this tag.
- *
- */
- @JvmOverloads
- fun setValue(value : ByteArray, offset : Int = 0, length : Int = value.size) : Boolean {
- if(checkBadComponentCount(length)) {
- return false
- }
- if(dataType != TYPE_UNSIGNED_BYTE && dataType != TYPE_UNDEFINED) {
- return false
- }
- componentCount = length
- mValue = ByteArray(length).also {
- System.arraycopy(value, offset, it, 0, length)
- }
- return true
- }
-
- /**
- * Sets byte value into this tag. This method should be used for tags of
- * type [.TYPE_UNSIGNED_BYTE] or [.TYPE_UNDEFINED]. This method
- * will fail if:
- *
- * * The component type of this tag is not [.TYPE_UNSIGNED_BYTE] or
- * [.TYPE_UNDEFINED] .
- * * The component count in the definition for this tag is not 1.
- *
- */
- fun setValue(value : Byte) = setValue(byteArrayOf(value))
-
- /**
- * Sets the value for this tag using an appropriate setValue method for the
- * given object. This method will fail if:
- *
- * * The corresponding setValue method for the class of the object passed
- * in would fail.
- * * There is no obvious way to cast the object passed in into an EXIF tag
- * type.
- *
- */
- inline fun setValueAny(obj : T) : Boolean {
- when(obj) {
- // null -> return false
-
- is String -> return setValue(obj)
- is ByteArray -> return setValue(obj)
- is IntArray -> return setValue(obj)
- is LongArray -> return setValue(obj)
- is Rational -> return setValue(obj)
- is Byte -> return setValue(obj.toByte())
- is Short -> return setValue(obj.toInt() and 0x0ffff)
- is Int -> return setValue(obj.toInt())
- is Long -> return setValue(obj.toLong())
-
- else -> {
-
- @Suppress("UNCHECKED_CAST")
- val ra = obj as? Array
- if(ra != null) return setValue(ra)
-
- // Nulls in this array are treated as zeroes.
- @Suppress("UNCHECKED_CAST")
- val sa = obj as? Array
- 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
- 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
- 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
- if(ba != null) return setValue(ByteArray(ba.size) { ba[it] ?: 0 })
-
- return false
- }
- }
- }
-
- /**
- * Sets a timestamp to this tag. The method converts the timestamp with the
- * format of "yyyy:MM:dd kk:mm:ss" and calls [.setValue]. This
- * method will fail if the data type is not [.TYPE_ASCII] or the
- * component count of this tag is not 20 or undefined.
- *
- * @param time the number of milliseconds since Jan. 1, 1970 GMT
- * @return true on success
- */
- fun setValueTime(time : Long) : Boolean {
- // synchronized on TIME_FORMAT as SimpleDateFormat is not thread safe
- synchronized(TIME_FORMAT) {
- return setValue(TIME_FORMAT.format(Date(time)))
- }
- }
-
- /**
- * Sets a string value into this tag. This method should be used for tags of
- * type [.TYPE_ASCII]. The string is converted to an ASCII string.
- * Characters that cannot be converted are replaced with '?'. The length of
- * the string must be equal to either (component count -1) or (component
- * count). The final byte will be set to the string null terminator '\0',
- * overwriting the last character in the string if the value.length is equal
- * to the component count. This method will fail if:
- *
- * * The data type is not [.TYPE_ASCII] or [.TYPE_UNDEFINED].
- * * The length of the string is not equal to (component count -1) or
- * (component count) in the definition for this tag.
- *
- */
- fun setValue(value : String) : Boolean {
- if(dataType != TYPE_ASCII && dataType != TYPE_UNDEFINED) {
- return false
- }
-
- val buf = value.toByteArray(US_ASCII)
-
- val finalBuf = when {
- buf.isNotEmpty() -> when {
- buf[buf.size - 1].toInt() == 0 || dataType == TYPE_UNDEFINED -> buf
- else -> buf.copyOf(buf.size + 1)
- }
- dataType == TYPE_ASCII && componentCount == 1 -> byteArrayOf(0)
- else -> buf
- }
- val count = finalBuf.size
- if(checkBadComponentCount(count)) {
- return false
- }
- componentCount = count
- mValue = finalBuf
- return true
- }
-
- private fun checkBadComponentCount(count : Int) : Boolean {
- return mHasDefinedDefaultComponentCount && componentCount != count
- }
-
- /**
- * Gets the value as a String. This method should be used for tags of type
- * [.TYPE_ASCII].
- *
- * @param defaultValue the String to return if the tag's value does not
- * exist or cannot be converted to a String.
- * @return the tag's value as a String, or the defaultValue.
- */
- fun getValueAsString(defaultValue : String) : String {
- return valueAsString ?: defaultValue
- }
-
- /**
- * Gets the value as a byte. If there are more than 1 bytes in this value,
- * gets the first byte. This method should be used for tags of type
- * [.TYPE_UNDEFINED] or [.TYPE_UNSIGNED_BYTE].
- *
- * @param defaultValue the byte to return if tag's value does not exist or
- * cannot be converted to a byte.
- * @return the tag's value as a byte, or the defaultValue.
- */
- fun getValueAsByte(defaultValue : Byte) : Byte {
- val array = valueAsBytes
- return if(array?.isNotEmpty() == true) array[0] else defaultValue
- }
-
- /**
- * Gets the value as a Rational. If there are more than 1 Rationals in this
- * value, gets the first one. This method should be used for tags of type
- * [.TYPE_RATIONAL] or [.TYPE_UNSIGNED_RATIONAL].
- *
- * @param defaultValue the numerator of the Rational to return if tag's
- * value does not exist or cannot be converted to a Rational (the
- * denominator will be 1).
- * @return the tag's value as a Rational, or the defaultValue.
- */
- fun getValueAsRational(defaultValue : Long) : Rational =
- getValueAsRational(Rational(defaultValue, 1))
-
- /**
- * Gets the value as a Rational. If there are more than 1 Rationals in this
- * value, gets the first one. This method should be used for tags of type
- * [.TYPE_RATIONAL] or [.TYPE_UNSIGNED_RATIONAL].
- *
- * @param defaultValue the Rational to return if tag's value does not exist
- * or cannot be converted to a Rational.
- * @return the tag's value as a Rational, or the defaultValue.
- */
- private fun getValueAsRational(defaultValue : Rational) : Rational {
- val array = valueAsRationals
- return if(array?.isNotEmpty() == true) array[0] else defaultValue
- }
-
- /**
- * Gets the value as an int. If there are more than 1 ints in this value,
- * gets the first one. This method should be used for tags of type
- * [.TYPE_UNSIGNED_SHORT], [.TYPE_UNSIGNED_LONG].
- *
- * @param defaultValue the int to return if tag's value does not exist or
- * cannot be converted to an int.
- * @return the tag's value as a int, or the defaultValue.
- */
- fun getValueAsInt(defaultValue : Int) : Int {
- val array = valueAsInts
- return if(array?.isNotEmpty() == true) array[0] else defaultValue
- }
-
- /**
- * Gets the value or null if none exists. If there are more than 1 longs in
- * this value, gets the first one. This method should be used for tags of
- * type [.TYPE_UNSIGNED_LONG].
- *
- * @param defaultValue the long to return if tag's value does not exist or
- * cannot be converted to a long.
- * @return the tag's value as a long, or the defaultValue.
- */
- fun getValueAsLong(defaultValue : Long) : Long {
- val array = valueAsLongs
- return if(array?.isNotEmpty() == true) array[0] else defaultValue
- }
-
- /**
- * Gets the tag's value or null if none exists.
- */
- fun getValue() : Any? {
- return mValue
- }
-
- /**
- * Gets a long representation of the value.
- *
- * @param defaultValue value to return if there is no value or value is a
- * rational with a denominator of 0.
- * @return the tag's value as a long, or defaultValue if no representation
- * exists.
- */
- fun forceGetValueAsLong(defaultValue : Long) : Long {
- when(val v = mValue) {
- is LongArray -> if(v.isNotEmpty()) return v[0]
- is ByteArray -> if(v.isNotEmpty()) return v[0].toLong()
-
- else -> {
- val r = valueAsRationals
- if(r?.isNotEmpty() == true && r[0].denominator != 0L) {
- return r[0].toDouble().toLong()
- }
- }
- }
- return defaultValue
- }
-
- /**
- * Gets the value for type [.TYPE_ASCII], [.TYPE_LONG],
- * [.TYPE_UNDEFINED], [.TYPE_UNSIGNED_BYTE],
- * [.TYPE_UNSIGNED_LONG], or [.TYPE_UNSIGNED_SHORT]. For
- * [.TYPE_RATIONAL] or [.TYPE_UNSIGNED_RATIONAL], call
- * [.getRational] instead.
- *
- * @throws IllegalArgumentException if the data type is
- * [.TYPE_RATIONAL] or [.TYPE_UNSIGNED_RATIONAL].
- */
- fun getValueAt(index : Int) : Long {
- return when(val v = mValue) {
- is LongArray -> v[index]
- is ByteArray -> v[index].toLong()
- else -> error(
- "Cannot get integer value from ${convertTypeToString(dataType)}"
- )
- }
- }
-
- /**
- * Gets the [.TYPE_RATIONAL] or [.TYPE_UNSIGNED_RATIONAL] data.
- *
- * @throws IllegalArgumentException If the type is NOT
- * [.TYPE_RATIONAL] or [.TYPE_UNSIGNED_RATIONAL].
- */
- fun getRational(index : Int) : Rational? {
- require(! (dataType != TYPE_RATIONAL && dataType != TYPE_UNSIGNED_RATIONAL)) {
- "Cannot get RATIONAL value from " + convertTypeToString(dataType)
- }
- return valueAsRationals?.get(index)
- }
-
- /**
- * Gets the [.TYPE_UNDEFINED] or [.TYPE_UNSIGNED_BYTE] data.
- *
- * @param buf the byte array in which to store the bytes read.
- * @param offset the initial position in buffer to store the bytes.
- * @param length the maximum number of bytes to store in buffer. If length >
- * component count, only the valid bytes will be stored.
- * @throws IllegalArgumentException If the type is NOT
- * [.TYPE_UNDEFINED] or [.TYPE_UNSIGNED_BYTE].
- */
- @JvmOverloads
- fun getBytes(buf : ByteArray, offset : Int = 0, length : Int = buf.size) {
- require(! (dataType != TYPE_UNDEFINED && dataType != TYPE_UNSIGNED_BYTE)) {
- "Cannot get BYTE value from " + convertTypeToString(
- dataType
- )
- }
- System.arraycopy(
- mValue !!,
- 0,
- buf,
- offset,
- if(length > componentCount) componentCount else length
- )
- }
-
- var hasDefinedCount : Boolean
- get() = mHasDefinedDefaultComponentCount
- set(value) {
- mHasDefinedDefaultComponentCount = value
- }
-
- private fun checkOverflowForUnsignedShort(value : IntArray) : Boolean =
- null != value.find { it !in 0 .. UNSIGNED_SHORT_MAX }
-
- private fun checkOverflowForUnsignedLong(value : LongArray) : Boolean =
- null != value.find { it !in 0 .. UNSIGNED_LONG_MAX }
-
- private fun checkOverflowForUnsignedLong(value : IntArray) : Boolean =
- null != value.find { it < 0 }
-
- private fun checkOverflowForUnsignedRational(value : Array) : Boolean =
- null != value.find { it.numerator !in 0 .. UNSIGNED_LONG_MAX || it.denominator !in 0 .. UNSIGNED_LONG_MAX }
-
- private fun checkOverflowForRational(value : Array) : 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()
- result = 31 * result + dataType.toInt()
- result = 31 * result + ifd
- result = 31 * result + componentCount
- result = 31 * result + offset
- result = 31 * result + mHasDefinedDefaultComponentCount.hashCode()
- result = 31 * result + (mValue?.hashCode() ?: 0)
- return result
- }
-
- override fun equals(other : Any?) : Boolean {
- if(other !is ExifTag) return false
-
- if(other.tagId != this.tagId
- || other.componentCount != this.componentCount
- || other.dataType != this.dataType
- ) {
- return false
- }
-
- val va = this.mValue
- val vb = other.mValue
-
- return when {
-
- va == null -> vb == null
-
- vb == null -> false
-
- va is LongArray -> when(vb) {
- is LongArray -> Arrays.equals(va, vb)
- else -> false
- }
-
- va is ByteArray -> when(vb) {
- is ByteArray -> Arrays.equals(va, vb)
- else -> false
- }
-
- va is Array<*> && va.isArrayOf() -> when {
- vb is Array<*> && vb.isArrayOf() -> Arrays.equals(va, vb)
- else -> false
- }
-
- else -> va == vb
- }
- }
-
- override fun toString() : String {
- val strTagId = String.format("%04X", tagId)
- return "tag id: $strTagId\nifd id: $ifd\ntype: ${convertTypeToString(dataType)}\ncount: $componentCount\noffset: $offset\nvalue: ${forceGetValueAsString()}\n"
- }
-
- /**
- * Gets a string representation of the value.
- */
- private fun forceGetValueAsString() : String {
- when(val v = mValue) {
-
- null -> return ""
-
- is ByteArray -> return when(dataType) {
- TYPE_ASCII -> String(v, US_ASCII)
- else -> Arrays.toString(v)
- }
-
- is LongArray -> return when {
- v.size == 1 -> v[0].toString()
- else -> Arrays.toString(v)
- }
-
- is Array<*> -> return when {
- v.size == 1 -> v[0]?.toString() ?: ""
- else -> Arrays.toString(v)
- }
-
- else -> return v.toString()
- }
- }
-
- companion object {
- /**
- * The BYTE type in the EXIF standard. An 8-bit unsigned integer.
- */
- const val TYPE_UNSIGNED_BYTE : Short = 1
- /**
- * The ASCII type in the EXIF standard. An 8-bit byte containing one 7-bit
- * ASCII code. The final byte is terminated with NULL.
- */
- const val TYPE_ASCII : Short = 2
- /**
- * The SHORT type in the EXIF standard. A 16-bit (2-byte) unsigned integer
- */
- const val TYPE_UNSIGNED_SHORT : Short = 3
- /**
- * The LONG type in the EXIF standard. A 32-bit (4-byte) unsigned integer
- */
- const val TYPE_UNSIGNED_LONG : Short = 4
- /**
- * The RATIONAL type of EXIF standard. It consists of two LONGs. The first
- * one is the numerator and the second one expresses the denominator.
- */
- const val TYPE_UNSIGNED_RATIONAL : Short = 5
- /**
- * The UNDEFINED type in the EXIF standard. An 8-bit byte that can take any
- * value depending on the field definition.
- */
- const val TYPE_UNDEFINED : Short = 7
- /**
- * The SLONG type in the EXIF standard. A 32-bit (4-byte) signed integer
- * (2's complement notation).
- */
- const val TYPE_LONG : Short = 9
- /**
- * The SRATIONAL type of EXIF standard. It consists of two SLONGs. The first
- * one is the numerator and the second one is the denominator.
- */
- const val TYPE_RATIONAL : Short = 10
- internal const val SIZE_UNDEFINED = 0
- private val TYPE_TO_SIZE_MAP = IntArray(11)
- private const val UNSIGNED_SHORT_MAX = 65535
- private const val UNSIGNED_LONG_MAX = 4294967295L
- private const val LONG_MAX = Integer.MAX_VALUE.toLong()
- private const val LONG_MIN = Integer.MIN_VALUE.toLong()
-
- init {
- TYPE_TO_SIZE_MAP[TYPE_UNSIGNED_BYTE.toInt()] = 1
- TYPE_TO_SIZE_MAP[TYPE_ASCII.toInt()] = 1
- TYPE_TO_SIZE_MAP[TYPE_UNSIGNED_SHORT.toInt()] = 2
- TYPE_TO_SIZE_MAP[TYPE_UNSIGNED_LONG.toInt()] = 4
- TYPE_TO_SIZE_MAP[TYPE_UNSIGNED_RATIONAL.toInt()] = 8
- TYPE_TO_SIZE_MAP[TYPE_UNDEFINED.toInt()] = 1
- TYPE_TO_SIZE_MAP[TYPE_LONG.toInt()] = 4
- TYPE_TO_SIZE_MAP[TYPE_RATIONAL.toInt()] = 8
- }
-
- private val TIME_FORMAT = SimpleDateFormat("yyyy:MM:dd kk:mm:ss", Locale.ENGLISH)
- private val US_ASCII = Charset.forName("US-ASCII")
-
- /**
- * Returns true if the given IFD is a valid IFD.
- */
- fun isValidIfd(ifdId : Int) : Boolean =
- when(ifdId) {
- IfdData.TYPE_IFD_0,
- IfdData.TYPE_IFD_1,
- IfdData.TYPE_IFD_EXIF,
- IfdData.TYPE_IFD_INTEROPERABILITY,
- IfdData.TYPE_IFD_GPS -> true
- else -> false
- }
-
- /**
- * Returns true if a given type is a valid tag type.
- */
- fun isValidType(type : Short) : Boolean =
- when(type) {
- TYPE_UNSIGNED_BYTE,
- TYPE_ASCII,
- TYPE_UNSIGNED_SHORT,
- TYPE_UNSIGNED_LONG,
- TYPE_UNSIGNED_RATIONAL,
- TYPE_UNDEFINED,
- TYPE_LONG,
- TYPE_RATIONAL -> true
- else -> false
- }
-
- /**
- * Gets the element size of the given data type in bytes.
- *
- * @see .TYPE_ASCII
- * @see .TYPE_LONG
- * @see .TYPE_RATIONAL
- * @see .TYPE_UNDEFINED
- * @see .TYPE_UNSIGNED_BYTE
- * @see .TYPE_UNSIGNED_LONG
- * @see .TYPE_UNSIGNED_RATIONAL
- * @see .TYPE_UNSIGNED_SHORT
- */
- fun getElementSize(type : Short) : Int = TYPE_TO_SIZE_MAP[type.toInt()]
-
- private fun convertTypeToString(type : Short) : String =
- when(type) {
- TYPE_UNSIGNED_BYTE -> "UNSIGNED_BYTE"
- TYPE_ASCII -> "ASCII"
- TYPE_UNSIGNED_SHORT -> "UNSIGNED_SHORT"
- TYPE_UNSIGNED_LONG -> "UNSIGNED_LONG"
- TYPE_UNSIGNED_RATIONAL -> "UNSIGNED_RATIONAL"
- TYPE_UNDEFINED -> "UNDEFINED"
- TYPE_LONG -> "LONG"
- TYPE_RATIONAL -> "RATIONAL"
- else -> ""
- }
- }
-}
diff --git a/exif/src/main/java/it/sephiroth/android/library/exif2/ExifUtil.kt b/exif/src/main/java/it/sephiroth/android/library/exif2/ExifUtil.kt
deleted file mode 100644
index a4fa589a..00000000
--- a/exif/src/main/java/it/sephiroth/android/library/exif2/ExifUtil.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-package it.sephiroth.android.library.exif2
-
-import java.text.DecimalFormat
-
-/**
- * Created by alessandro on 20/04/14.
- */
-object ExifUtil {
-
- private val formatter = DecimalFormat.getInstance()
-
- fun processLensSpecifications(values : Array) : String {
- val min_focal = values[0]
- val max_focal = values[1]
- val min_f = values[2]
- val max_f = values[3]
-
- formatter.maximumFractionDigits = 1
-
- val sb = StringBuilder()
- sb.append(formatter.format(min_focal.toDouble()))
- sb.append("-")
- sb.append(formatter.format(max_focal.toDouble()))
- sb.append("mm f/")
- sb.append(formatter.format(min_f.toDouble()))
- sb.append("-")
- sb.append(formatter.format(max_f.toDouble()))
-
- return sb.toString()
- }
-
-}
diff --git a/exif/src/main/java/it/sephiroth/android/library/exif2/IfdData.kt b/exif/src/main/java/it/sephiroth/android/library/exif2/IfdData.kt
deleted file mode 100644
index 4e62636a..00000000
--- a/exif/src/main/java/it/sephiroth/android/library/exif2/IfdData.kt
+++ /dev/null
@@ -1,107 +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.util.HashMap
-
-// This class stores all the tags in an IFD.
-// an IfdData with given IFD ID.
-internal class IfdData(
- 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()
-
- // the offset of next IFD.
- var offsetToNextIfd = 0
-
- // the tags count in the IFD.
- val tagCount : Int
- get() = mExifTags.size
-
- // Collection the contains all [ExifTag] in this IFD.
- val allTagsCollection : Collection
- get() = mExifTags.values
-
- // checkCollision
- fun contains(tagId : Short) : Boolean {
- return mExifTags[tagId] != null
- }
-
- // the [ExifTag] with given tag id.
- // null if there is no such tag.
- fun getTag(tagId : Short) : ExifTag? {
- return mExifTags[tagId]
- }
-
- // Adds or replaces a [ExifTag].
- fun setTag(tag : ExifTag) : ExifTag? {
- tag.ifd = id
- return mExifTags.put(tag.tagId, tag)
- }
-
- // Removes the tag of the given ID
- fun removeTag(tagId : Short) {
- mExifTags.remove(tagId)
- }
-
- /**
- * Returns true if all tags in this two IFDs are equal. Note that tags of
- * IFDs offset or thumbnail offset will be ignored.
- */
- override fun equals(other : Any?) : Boolean {
- if(other is IfdData) {
- if(other === this) return true
- if(other.id == id && other.tagCount == tagCount) {
- for(tag in other.allTagsCollection) {
- if(ExifInterface.isOffsetTag(tag.tagId)) continue
- if(tag != mExifTags[tag.tagId]) return false
- }
- return true
- }
- }
- return false
- }
-
- override fun hashCode() : Int {
- var result = id
- result = 31 * result + mExifTags.hashCode()
- result = 31 * result + offsetToNextIfd
- return result
- }
-}
diff --git a/exif/src/main/java/it/sephiroth/android/library/exif2/JpegHeader.kt b/exif/src/main/java/it/sephiroth/android/library/exif2/JpegHeader.kt
deleted file mode 100644
index 60f3a5ea..00000000
--- a/exif/src/main/java/it/sephiroth/android/library/exif2/JpegHeader.kt
+++ /dev/null
@@ -1,123 +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
-
-@Suppress("unused", "MemberVisibilityCanBePrivate")
-object JpegHeader {
- /** Start Of Image */
- const val TAG_SOI = 0xD8
-
- /** JFIF (JPEG File Interchange Format) */
- const val TAG_M_JFIF = 0xE0
-
- /** EXIF table */
- const val TAG_M_EXIF = 0xE1
-
- /** Product Information Comment */
- const val TAG_M_COM = 0xFE
-
- /** Quantization Table */
- const val TAG_M_DQT = 0xDB
-
- /** Start of frame */
- const val TAG_M_SOF0 = 0xC0
- const val TAG_M_SOF1 = 0xC1
- const val TAG_M_SOF2 = 0xC2
- const val TAG_M_SOF3 = 0xC3
- const val TAG_M_DHT = 0xC4
- const val TAG_M_SOF5 = 0xC5
- const val TAG_M_SOF6 = 0xC6
- const val TAG_M_SOF7 = 0xC7
- const val TAG_M_SOF9 = 0xC9
- const val TAG_M_SOF10 = 0xCA
- const val TAG_M_SOF11 = 0xCB
- const val TAG_M_SOF13 = 0xCD
- const val TAG_M_SOF14 = 0xCE
- const val TAG_M_SOF15 = 0xCF
-
- /** Start Of Scan */
- const val TAG_M_SOS = 0xDA
-
- /** End of Image */
- const val TAG_M_EOI = 0xD9
-
- const val TAG_M_IPTC = 0xED
-
- /** default JFIF Header bytes */
- val JFIF_HEADER = byteArrayOf(
- 0xff.toByte(),
- TAG_M_JFIF.toByte(),
- 0x00,
- 0x10,
- 'J'.toByte(),
- 'F'.toByte(),
- 'I'.toByte(),
- 'F'.toByte(),
- 0x00,
- 0x01,
- 0x01,
- 0x01,
- 0x01,
- 0x2C,
- 0x01,
- 0x2C,
- 0x00,
- 0x00
- )
-
- const val SOI = 0xFFD8.toShort()
- const val M_EXIF = 0xFFE1.toShort()
- const val M_JFIF = 0xFFE0.toShort()
- const val M_EOI = 0xFFD9.toShort()
-
- /**
- * SOF (start of frame). All value between M_SOF0 and SOF15 is SOF marker except for M_DHT, JPG,
- * and DAC marker.
- */
- const val M_SOF0 = 0xFFC0.toShort()
- const val M_SOF1 = 0xFFC1.toShort()
- const val M_SOF2 = 0xFFC2.toShort()
- const val M_SOF3 = 0xFFC3.toShort()
- const val M_SOF5 = 0xFFC5.toShort()
- const val M_SOF6 = 0xFFC6.toShort()
- const val M_SOF7 = 0xFFC7.toShort()
- const val M_SOF9 = 0xFFC9.toShort()
- const val M_SOF10 = 0xFFCA.toShort()
- const val M_SOF11 = 0xFFCB.toShort()
- const val M_SOF13 = 0xFFCD.toShort()
- const val M_SOF14 = 0xFFCE.toShort()
- const val M_SOF15 = 0xFFCF.toShort()
- const val M_DHT = 0xFFC4.toShort()
- const val JPG = 0xFFC8.toShort()
- const val DAC = 0xFFCC.toShort()
-
- /** Define quantization table */
- const val M_DQT = 0xFFDB.toShort()
-
- /** IPTC marker */
- const val M_IPTC = 0xFFED.toShort()
-
- /** Start of scan (begins compressed data */
- const val M_SOS = 0xFFDA.toShort()
-
- /** Comment section * */
- const val M_COM = 0xFFFE.toShort() // Comment section
-
- fun isSofMarker(marker : Short) : Boolean {
- return marker >= M_SOF0 && marker <= M_SOF15 && marker != M_DHT && marker != JPG && marker != DAC
- }
-}
diff --git a/exif/src/main/java/it/sephiroth/android/library/exif2/Rational.kt b/exif/src/main/java/it/sephiroth/android/library/exif2/Rational.kt
deleted file mode 100644
index bdede84f..00000000
--- a/exif/src/main/java/it/sephiroth/android/library/exif2/Rational.kt
+++ /dev/null
@@ -1,56 +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
-
-/**
- * The rational data type of EXIF tag. Contains a pair of longs representing the
- * numerator and denominator of a Rational number.
- */
-class Rational(
-
- //the numerator of the rational.
- val numerator : Long = 0,
-
- //the denominator of the rational
- val denominator : Long = 1
-) {
-
- // copy from a Rational.
- @Suppress("unused")
- constructor(r : Rational) : this(
- numerator = r.numerator,
- denominator = r.denominator
- )
-
- override fun equals(other : Any?) : Boolean {
- return when {
- other === null -> false
- other === this -> true
- other is Rational -> numerator == other.numerator && denominator == other.denominator
- else -> false
- }
- }
-
- override fun hashCode() : Int =
- 31 * numerator.hashCode() + denominator.hashCode()
-
- override fun toString() : String = "$numerator/$denominator"
-
- // Gets the rational value as type double.
- // Will cause a divide-by-zero error if the denominator is 0.
- fun toDouble() : Double = numerator.toDouble() / denominator.toDouble()
-}
diff --git a/exif/src/main/java/it/sephiroth/android/library/exif2/utils/CountedDataInputStream.kt b/exif/src/main/java/it/sephiroth/android/library/exif2/utils/CountedDataInputStream.kt
deleted file mode 100644
index b57801c7..00000000
--- a/exif/src/main/java/it/sephiroth/android/library/exif2/utils/CountedDataInputStream.kt
+++ /dev/null
@@ -1,149 +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.utils
-
-import java.io.EOFException
-import java.io.FilterInputStream
-import java.io.IOException
-import java.io.InputStream
-import java.nio.ByteBuffer
-import java.nio.ByteOrder
-import java.nio.charset.Charset
-import java.nio.charset.StandardCharsets
-
-@Suppress("unused")
-internal class CountedDataInputStream constructor(`in` : InputStream) :
- FilterInputStream(`in`) {
-
- // allocate a byte buffer for a long value;
- private val mByteArray = ByteArray(8)
- private val mByteBuffer = ByteBuffer.wrap(mByteArray)
- var readByteCount = 0
- private set
- var end = 0
-
- var byteOrder : ByteOrder
- get() = mByteBuffer.order()
- set(order) {
- mByteBuffer.order(order)
- }
-
- @Throws(IOException::class)
- override fun read(b : ByteArray) : Int {
- val r = `in`.read(b)
- readByteCount += if(r >= 0) r else 0
- return r
- }
-
- @Throws(IOException::class)
- override fun read() : Int {
- val r = `in`.read()
- readByteCount += if(r >= 0) 1 else 0
- return r
- }
-
- @Throws(IOException::class)
- override fun read(b : ByteArray, off : Int, len : Int) : Int {
- val r = `in`.read(b, off, len)
- readByteCount += if(r >= 0) r else 0
- return r
- }
-
- @Throws(IOException::class)
- override fun skip(length : Long) : Long {
- val skip = `in`.skip(length)
- readByteCount += skip.toInt()
- return skip
- }
-
- @Throws(IOException::class)
- fun skipTo(target : Long) {
- val cur = readByteCount.toLong()
- val diff = target - cur
- if(diff < 0) throw IndexOutOfBoundsException("skipTo: negative move")
- skipOrThrow(diff)
- }
-
- @Throws(IOException::class)
- fun skipOrThrow(length : Long) {
- if(skip(length) != length) throw EOFException()
- }
-
- @Throws(IOException::class)
- fun readUnsignedShort() : Int = readShort().toInt() and 0xffff
-
- @Throws(IOException::class)
- fun readShort() : Short {
- readOrThrow(mByteArray, 0, 2)
- mByteBuffer.rewind()
- return mByteBuffer.short
- }
-
- @Throws(IOException::class)
- fun readByte() : Byte {
- readOrThrow(mByteArray, 0, 1)
- mByteBuffer.rewind()
- return mByteBuffer.get()
- }
-
- @Throws(IOException::class)
- fun readUnsignedByte() : Int {
- readOrThrow(mByteArray, 0, 1)
- mByteBuffer.rewind()
- return mByteBuffer.get().toInt() and 0xff
- }
-
- @Throws(IOException::class)
- @JvmOverloads
- fun readOrThrow(b : ByteArray, off : Int = 0, len : Int = b.size) {
- val r = read(b, off, len)
- if(r != len) throw EOFException()
- }
-
- @Throws(IOException::class)
- fun readUnsignedInt() : Long {
- return readInt().toLong() and 0xffffffffL
- }
-
- @Throws(IOException::class)
- fun readInt() : Int {
- readOrThrow(mByteArray, 0, 4)
- mByteBuffer.rewind()
- return mByteBuffer.int
- }
-
- @Throws(IOException::class)
- fun readLong() : Long {
- readOrThrow(mByteArray, 0, 8)
- mByteBuffer.rewind()
- return mByteBuffer.long
- }
-
- @Throws(IOException::class)
- fun readString(n : Int) : String {
- val buf = ByteArray(n)
- readOrThrow(buf)
- return String(buf, StandardCharsets.UTF_8)
- }
-
- @Throws(IOException::class)
- fun readString(n : Int, charset : Charset) : String {
- val buf = ByteArray(n)
- readOrThrow(buf)
- return String(buf, charset)
- }
-}
\ No newline at end of file
diff --git a/exif/src/main/java/it/sephiroth/android/library/exif2/utils/OrderedDataOutputStream.kt b/exif/src/main/java/it/sephiroth/android/library/exif2/utils/OrderedDataOutputStream.kt
deleted file mode 100644
index 104502c4..00000000
--- a/exif/src/main/java/it/sephiroth/android/library/exif2/utils/OrderedDataOutputStream.kt
+++ /dev/null
@@ -1,56 +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.utils
-
-import it.sephiroth.android.library.exif2.Rational
-import java.io.FilterOutputStream
-import java.io.IOException
-import java.io.OutputStream
-import java.nio.ByteBuffer
-import java.nio.ByteOrder
-
-internal class OrderedDataOutputStream(out : OutputStream) : FilterOutputStream(out) {
- private val mByteBuffer = ByteBuffer.allocate(4)
-
- fun setByteOrder(order : ByteOrder) : OrderedDataOutputStream {
- mByteBuffer.order(order)
- return this
- }
-
- @Throws(IOException::class)
- fun writeShort(value : Short) : OrderedDataOutputStream {
- mByteBuffer.rewind()
- mByteBuffer.putShort(value)
- out.write(mByteBuffer.array(), 0, 2)
- return this
- }
-
- @Throws(IOException::class)
- fun writeRational(rational : Rational) : OrderedDataOutputStream {
- writeInt(rational.numerator.toInt())
- writeInt(rational.denominator.toInt())
- return this
- }
-
- @Throws(IOException::class)
- fun writeInt(value : Int) : OrderedDataOutputStream {
- mByteBuffer.rewind()
- mByteBuffer.putInt(value)
- out.write(mByteBuffer.array())
- return this
- }
-}
diff --git a/exif/src/main/java/it/sephiroth/android/library/exif2/utils/Utils.kt b/exif/src/main/java/it/sephiroth/android/library/exif2/utils/Utils.kt
deleted file mode 100644
index 26416c9f..00000000
--- a/exif/src/main/java/it/sephiroth/android/library/exif2/utils/Utils.kt
+++ /dev/null
@@ -1,7 +0,0 @@
-package it.sephiroth.android.library.exif2.utils
-
-internal fun Collection?.notEmpty() : Collection? =
- if(this?.isNotEmpty() == true) this else null
-
-internal fun List?.notEmpty() : List? =
- if(this?.isNotEmpty() == true) this else null
diff --git a/exif/src/test/java/it/sephiroth/android/library/exif2/Test1.kt b/exif/src/test/java/it/sephiroth/android/library/exif2/Test1.kt
deleted file mode 100644
index efd31b11..00000000
--- a/exif/src/test/java/it/sephiroth/android/library/exif2/Test1.kt
+++ /dev/null
@@ -1,115 +0,0 @@
-package it.sephiroth.android.library.exif2
-
-import android.util.Log
-import android.util.SparseIntArray
-import org.junit.Assert.*
-import org.junit.Test
-import java.io.File
-import java.io.FileInputStream
-
-class Test1 {
-
- @Test
- fun testLog() {
- Log.v("TEST", "test")
- assertTrue("using android.util.Log", true)
- }
-
- @Test
- fun testSparseIntArray() {
- val a = SparseIntArray()
- a.put(1, 2)
- assertTrue("get existing value", a[1] == 2)
- assertTrue("fallback to default value ", a.get(0, - 1) == - 1)
- }
-
- // get File object from files in src/test/resources/
- private fun getFile(fileName : String) : File {
- return when(val resource = this.javaClass.classLoader !!.getResource(fileName)) {
- null -> error("missing file $fileName")
- else -> File(resource.path)
- }
- }
-
- private fun getOrientation(fileName : String) : Pair =
- try {
- val o = FileInputStream(getFile(fileName)).use { inStream ->
- ExifInterface()
- .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 =
- try {
- val o = FileInputStream(getFile(fileName)).use { inStream ->
- ExifInterface()
- .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)
- }
-
- @Test
- fun testNotJpeg() {
- fun testNotJpegSub(fileName : String) {
- val (o, ex) = getOrientation(fileName)
- assertTrue("testNotJpegSub", o == null && ex != null)
- }
- testNotJpegSub("test.gif")
- testNotJpegSub("test.png")
- testNotJpegSub("test.webp")
- }
-
- @Test
- fun testJpeg() {
- var fileName : String
- var rvO : Pair
- var rvT : Pair
-
- // this file has orientation 6.
- fileName = "test3.jpg"
- 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
- ) //
-
- rvT = getThumbnailBytes(fileName)
- assertNotNull(
- fileName,
- rvT.second
- ) //
-
- }
-
-}
\ No newline at end of file
diff --git a/exif/src/test/resources/test.gif b/exif/src/test/resources/test.gif
deleted file mode 100644
index 6e42eb2b..00000000
Binary files a/exif/src/test/resources/test.gif and /dev/null differ
diff --git a/exif/src/test/resources/test.png b/exif/src/test/resources/test.png
deleted file mode 100644
index 141e0a16..00000000
Binary files a/exif/src/test/resources/test.png and /dev/null differ
diff --git a/exif/src/test/resources/test.webp b/exif/src/test/resources/test.webp
deleted file mode 100644
index 4d9a81af..00000000
Binary files a/exif/src/test/resources/test.webp and /dev/null differ
diff --git a/exif/src/test/resources/test1.jpg b/exif/src/test/resources/test1.jpg
deleted file mode 100644
index 935bc426..00000000
Binary files a/exif/src/test/resources/test1.jpg and /dev/null differ
diff --git a/exif/src/test/resources/test2.jpg b/exif/src/test/resources/test2.jpg
deleted file mode 100644
index 335f16d5..00000000
Binary files a/exif/src/test/resources/test2.jpg and /dev/null differ
diff --git a/exif/src/test/resources/test3.jpg b/exif/src/test/resources/test3.jpg
deleted file mode 100644
index 61a5591f..00000000
Binary files a/exif/src/test/resources/test3.jpg and /dev/null differ
diff --git a/exif/src/test/resources/test3.jpg_original b/exif/src/test/resources/test3.jpg_original
deleted file mode 100644
index 335f16d5..00000000
Binary files a/exif/src/test/resources/test3.jpg_original and /dev/null differ
diff --git a/sample_apng/build.gradle b/sample_apng/build.gradle
index ef7ed5b7..7d6e9ab4 100644
--- a/sample_apng/build.gradle
+++ b/sample_apng/build.gradle
@@ -2,7 +2,7 @@ apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
android {
- compileSdkVersion target_sdk_version
+ compileSdkVersion compile_sdk_version
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8