Skip to content

Commit

Permalink
Improvements (#21)
Browse files Browse the repository at this point in the history
* Gradle and lib updates

* Removed commented out code

* Migrated to new XmlUtil API

* XMPMetaParser.kt: Tiny code improvements.

* DomParser: Trim everything outside the RDF tags, because so many XMP files have junk there.

* Fixed missing import.

* AGP update

* Report the library under its real name.
  • Loading branch information
StefanOltmann authored Feb 20, 2024
1 parent e5bca0e commit 68ffebb
Show file tree
Hide file tree
Showing 217 changed files with 364 additions and 356 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ It's part of [Ashampoo Photos](https://ashampoo.com/photos).
## Installation

```
implementation("com.ashampoo:xmpcore:1.0.0")
implementation("com.ashampoo:xmpcore:1.0.1")
```

## How to use
Expand Down
13 changes: 2 additions & 11 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl

plugins {
kotlin("multiplatform") version "1.9.22"
id("com.android.library") version "8.0.2"
id("com.android.library") version "8.2.2"
id("maven-publish")
id("signing")
id("io.gitlab.arturbosch.detekt") version "1.23.4"
id("io.gitlab.arturbosch.detekt") version "1.23.5"
id("org.sonarqube") version "4.3.1.3277"
id("org.jetbrains.kotlinx.kover") version "0.6.1"
id("com.asarkar.gradle.build-time-tracker") version "4.3.0"
Expand Down Expand Up @@ -418,12 +418,3 @@ publishing {
}
}
// endregion

//rootProject.the<NodeJsRootExtension>().apply {
// nodeVersion = "21.0.0-v8-canary202309143a48826a08"
// nodeDownloadBaseUrl = "https://nodejs.org/download/v8-canary"
//}
//
//tasks.withType<org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinNpmInstallTask>().configureEach {
// args.add("--ignore-engines")
//}
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
32 changes: 21 additions & 11 deletions src/commonMain/kotlin/com/ashampoo/xmp/DomParser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,41 @@ package com.ashampoo.xmp

import nl.adaptivity.xmlutil.DomWriter
import nl.adaptivity.xmlutil.EventType
import nl.adaptivity.xmlutil.XmlStreaming
import nl.adaptivity.xmlutil.dom.Document
import nl.adaptivity.xmlutil.writeCurrent
import nl.adaptivity.xmlutil.xmlStreaming

object DomParser {

private const val RDF_RDF_END = "</rdf:RDF>"

fun parseDocumentFromString(input: String): Document {

/*
* We encountered a situation where the XMP had NUL characters at the end
* for unknown reasons. This caused an exception in the parser.
* The test images IMG_0001.jpg and IMG_0002.jpg on the iOS simulator
* exhibited this issue, indicating that it could occur in real-world
* scenarios as well. To address this, we now trim all whitespaces and
* ISO control characters from the XMP to ensure its proper parsing.
* We encountered a situation where NUL characters at the end of XMP
* caused an exception in the parser for unknown reasons. This issue was
* observed in the test images IMG_0001.jpg and IMG_0002.jpg on the iOS simulator,
* suggesting the possibility of real-world scenarios.
*
* Additionally, we identified some corrupted files with random junk after the
* first </rdf:RDF>, potentially caused by faulty writers.
*
* Since "xpacket" and "xmpmeta" are skippable at the user's discretion,
* the only required tags are within the RDF part. Therefore, we trim it down to this.
*/
val trimmedInput = input.trim {
it.isWhitespace() || it.isISOControl()
}

val rdfStartPos = input.indexOf("<rdf:RDF")
val rfdEndPos = input.indexOf(RDF_RDF_END)

val trimmedInput = input.substring(
rdfStartPos until rfdEndPos + RDF_RDF_END.length
)

try {

val writer = DomWriter()

val reader = XmlStreaming.newReader(trimmedInput)
val reader = xmlStreaming.newReader(trimmedInput)

do {
val event = reader.next()
Expand Down
78 changes: 40 additions & 38 deletions src/commonMain/kotlin/com/ashampoo/xmp/XMPMetaParser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,12 @@ import nl.adaptivity.xmlutil.dom.localName
import nl.adaptivity.xmlutil.dom.namespaceURI

/**
* This class replaces the `ExpatAdapter.cpp` and does the
* XML-parsing and fixes the prefix. After the parsing several normalisations
* are applied to the XMPTree.
* This class replaces the `ExpatAdapter.cpp` and does the XML-parsing and fixes the prefix.
* After the parsing several normalisations are applied to the XMPTree.
*/
internal object XMPMetaParser {

private val XMP_RDF = Any() // "new Object()" in Java
private val XMP_RDF = Any()

/**
* Parses the input source into an XMP metadata object, including
Expand All @@ -50,17 +49,15 @@ internal object XMPMetaParser {

val xmpmetaRequired = actualOptions.getRequireXMPMeta()

var result: Array<Any?>? = arrayOfNulls(3)

result = findRootNode(document, xmpmetaRequired, result)
val result = findRootNode(document, xmpmetaRequired, arrayOfNulls(3))

return if (result != null && result[1] === XMP_RDF) {

val xmp = XMPRDFParser.parse(result[0] as Node, actualOptions)

xmp.setPacketHeader(result[2] as? String)

// Check if the XMP object shall be normalized
/* Check if the XMP object shall be normalized */
if (!actualOptions.getOmitNormalization())
normalize(xmp, actualOptions)
else
Expand Down Expand Up @@ -100,64 +97,69 @@ internal object XMPMetaParser {
* * [1] - an object that is either XMP_RDF or XMP_PLAIN (the latter is decrecated)
* * [2] - the body text of the xpacket-instruction.
*/
private fun findRootNode(root: Node, xmpmetaRequired: Boolean, result: Array<Any?>?): Array<Any?>? {
private fun findRootNode(root: Node, xmpmetaRequired: Boolean, result: Array<Any?>): Array<Any?>? {

// Look among this parent's content for x:xapmeta or x:xmpmeta.
// The recursion for x:xmpmeta is broader than the strictly defined choice,
// but gives us smaller code.
/*
* Look among this parent's content for x:xapmeta or x:xmpmeta.
* The recursion for x:xmpmeta is broader than the strictly
* defined choice, but gives us smaller code.
*/

@Suppress("LoopWithTooManyJumpStatements")
for (index in 0 until root.childNodes.length) {

val child = root.childNodes.item(index)

requireNotNull(child)

if (child is ProcessingInstruction && XMPConst.XMP_PI == child.getTarget()) {
when {

child is ProcessingInstruction && XMPConst.XMP_PI == child.getTarget() -> {

// Store the processing instructions content
if (result != null)
/* Store the processing instructions content */
result[2] = child.getData()
}

} else if (child is Comment) {
child is Comment -> {

/* We ignore comments. */
continue
/* Ignore comments */
continue
}

} else if (child !is Text && child !is ProcessingInstruction) {
child !is Text && child !is ProcessingInstruction -> {

val childElement = child as Element
val childElement = child as Element

val rootNS = childElement.namespaceURI
val rootNS = childElement.namespaceURI

val rootLocal = childElement.localName
val rootLocal = childElement.localName

if (
(XMPConst.TAG_XMPMETA == rootLocal || XMPConst.TAG_XAPMETA == rootLocal) &&
XMPConst.NS_X == rootNS
) {
if (
(XMPConst.TAG_XMPMETA == rootLocal || XMPConst.TAG_XAPMETA == rootLocal) &&
XMPConst.NS_X == rootNS
) {

// by not passing the RequireXMPMeta-option, the rdf-Node will be valid
return findRootNode(child, false, result)
}
/* by not passing the RequireXMPMeta-option, the rdf-Node will be valid */
return findRootNode(child, false, result)
}

if (!xmpmetaRequired && "RDF" == rootLocal && XMPConst.NS_RDF == rootNS) {
if (!xmpmetaRequired && "RDF" == rootLocal && XMPConst.NS_RDF == rootNS) {

if (result != null) {
result[0] = child
result[1] = XMP_RDF
}

return result
}
return result
}

// continue searching
val newResult = findRootNode(child, xmpmetaRequired, result)
/* continue searching */
val newResult = findRootNode(child, xmpmetaRequired, result)

return newResult ?: continue
return newResult ?: continue
}
}
}

// no appropriate node has been found
/* Return NULL if no appropriate node has been found. */
return null
}
}
2 changes: 1 addition & 1 deletion src/commonMain/kotlin/com/ashampoo/xmp/XMPRDFWriter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ internal object XMPRDFWriter {

writeIndent(sb, level)
sb.append(RDF_XMPMETA_START)
sb.append(XMPVersionInfo.message)
sb.append(XMPVersionInfo.VERSION_MESSAGE)
sb.append("\">")
sb.append(XMP_DEFAULT_NEWLINE)

Expand Down
19 changes: 8 additions & 11 deletions src/commonMain/kotlin/com/ashampoo/xmp/XMPVersionInfo.kt
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
package com.ashampoo.xmp

/**
* We ported from version 1.5.3, which was the final release available on
* https://www.adobe.com/devnet/xmp/library/eula-xmp-library-java.html
* under the BSD license, prior to the webpage being taken down.
* Hence we report this as the used version.
* Since v1.0.1 we report the library no longer as "Adobe XMP Core 5.1.3"
* (which it is based on), but under it's real name and version number.
*/
@Suppress("MagicNumber")
object XMPVersionInfo {

const val VERSION_MESSAGE = "Adobe XMP Core 5.1.3"
const val MAJOR: Int = 1
const val MINOR: Int = 0
const val PATCH: Int = 1

const val major: Int = 5
const val minor: Int = 1
const val micro: Int = 3
const val build: Int = 0
const val isDebug: Boolean = false
const val message: String = VERSION_MESSAGE
const val VERSION_MESSAGE = "Ashampoo XMP Core $MAJOR.$MINOR.$PATCH"

const val DEBUG: Boolean = false

}
4 changes: 3 additions & 1 deletion src/commonTest/kotlin/com/ashampoo/xmp/RewriteXmpTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ class RewriteXmpTest {

val xmpMeta = XMPMetaFactory.parseFromString(originalXmp)

val actualCompactXmp = XMPMetaFactory.serializeToString(xmpMeta, xmpSerializeOptionsCompact)
val actualCompactXmp =
XMPMetaFactory.serializeToString(xmpMeta, xmpSerializeOptionsCompact)

val actualCanonicalXmp =
XMPMetaFactory.serializeToString(xmpMeta, xmpSerializeOptionsCanonical)

Expand Down
18 changes: 18 additions & 0 deletions src/commonTest/resources/com/ashampoo/xmp/README
Original file line number Diff line number Diff line change
@@ -1,6 +1,24 @@
This document makes comments about special XMP samples in the set.
We collect problematic files from real-world images to test for regression.

# sample_2.xmp
Ends with multiple NUL

# sample_3.xmp
Ends with a single NUL

# sample_4.xml
Ends with multiple <?xpacket end='w'?>

# sample_5.xml
Text after </x:xmpmeta>

# sample_6.xml
Text after <?xpacket end='w'?>

# sample_7.xml
Missing <?xpacket begin='' id='W5M0MpCehiHzreSzNTczkc9d'?>

# sample_50.xml
Problem: Doubled child entries

Expand Down
2 changes: 1 addition & 1 deletion src/commonTest/resources/com/ashampoo/xmp/empty.xmp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.1.3">
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Ashampoo XMP Core 1.0.1">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about=""/>
</rdf:RDF>
Expand Down
2 changes: 1 addition & 1 deletion src/commonTest/resources/com/ashampoo/xmp/new.xmp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.1.3">
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Ashampoo XMP Core 1.0.1">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about=""
xmlns:dc="http://purl.org/dc/elements/1.1/"
Expand Down
2 changes: 1 addition & 1 deletion src/commonTest/resources/com/ashampoo/xmp/rating.xmp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.1.3">
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Ashampoo XMP Core 1.0.1">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about=""
xmlns:xmp="http://ns.adobe.com/xap/1.0/"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.1.3">
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Ashampoo XMP Core 1.0.1">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about=""
xmlns:MicrosoftPhoto_1_="http://ns.microsoft.com/photo/1.0"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.1.3">
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Ashampoo XMP Core 1.0.1">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about=""
xmlns:MicrosoftPhoto_1_="http://ns.microsoft.com/photo/1.0"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.1.3">
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Ashampoo XMP Core 1.0.1">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about=""
xmlns:aux="http://ns.adobe.com/exif/1.0/aux/"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.1.3">
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Ashampoo XMP Core 1.0.1">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about=""
xmlns:aux="http://ns.adobe.com/exif/1.0/aux/"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.1.3">
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Ashampoo XMP Core 1.0.1">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about=""
xmlns:aux="http://ns.adobe.com/exif/1.0/aux/"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.1.3">
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Ashampoo XMP Core 1.0.1">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about=""
xmlns:aux="http://ns.adobe.com/exif/1.0/aux/"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.1.3">
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Ashampoo XMP Core 1.0.1">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about=""
xmlns:aux="http://ns.adobe.com/exif/1.0/aux/"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.1.3">
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Ashampoo XMP Core 1.0.1">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about=""
xmlns:aux="http://ns.adobe.com/exif/1.0/aux/"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.1.3">
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Ashampoo XMP Core 1.0.1">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about=""
xmlns:xmp="http://ns.adobe.com/xap/1.0/">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.1.3">
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Ashampoo XMP Core 1.0.1">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about=""
xmlns:xmp="http://ns.adobe.com/xap/1.0/"
Expand Down
Loading

0 comments on commit 68ffebb

Please sign in to comment.