Mercurial > cgi-bin > hgweb.cgi > JpegWasher
changeset 0:db63d01a23c6
JNI calls and test case (finally!) seem to work.
author | David Barts <n5jrn@me.com> |
---|---|
date | Tue, 31 Mar 2020 13:24:48 -0700 |
parents | |
children | 42277ce58ace |
files | .hgignore Makefile.mac build.xml setup.sh src/name/blackcap/exifwasher/Files.kt src/name/blackcap/exifwasher/Test.kt src/name/blackcap/exifwasher/default.properties src/name/blackcap/exifwasher/exiv2/ExifData.kt src/name/blackcap/exifwasher/exiv2/Exiv2Exception.kt src/name/blackcap/exifwasher/exiv2/Image.kt src/name/blackcap/exifwasher/exiv2/Initialize.kt src/name/blackcap/exifwasher/exiv2/native.cpp src/name/blackcap/exifwasher/exiv2/native.hpp |
diffstat | 13 files changed, 701 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hgignore Tue Mar 31 13:24:48 2020 -0700 @@ -0,0 +1,7 @@ +~$ +\.bak$ +\.class$ +\.dylib$ +\.o$ +^bundles/ +^work/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile.mac Tue Mar 31 13:24:48 2020 -0700 @@ -0,0 +1,23 @@ +CXX = g++ +CXXFLAGS = -I$(JRE_HOME)/include -I$(JRE_HOME)/include/darwin \ + -I$(EXIV2_HOME)/include -I$(EXIV2_HOME)/build +NDIR = src/name/blackcap/exifwasher/exiv2 +BDIR = src/name/blackcap/exifwasher/binaries/mac + +.PHONY: all checkenv + +all: checkenv $(BDIR)/libjni.dylib $(BDIR)/libexiv2.dylib + +checkenv: + @if [ -z "$(JRE_HOME)" -o -z "$(EXIV2_HOME)" ]; then \ + 1>&2 echo "JRE_HOME or EXIV2_HOME not set"; \ + exit 1; \ + fi + +$(NDIR)/native.o: $(NDIR)/native.cpp + +$(BDIR)/libjni.dylib: $(NDIR)/native.o + $(CXX) -dynamiclib -o $@ $< -L$(EXIV2_HOME)/build/lib -lexiv2 + +$(BDIR)/libexiv2.dylib: $(EXIV2_HOME)/build/lib/libexiv2.dylib + cp -fp $< $@
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/build.xml Tue Mar 31 13:24:48 2020 -0700 @@ -0,0 +1,146 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project name="ExifWasher" default="help" basedir="." xmlns:fx="javafx:com.sun.javafx.tools.ant"> + <!-- import all environment variables as env.* --> + <property environment="env"/> + + <!-- ensure required environment variables are set --> + <macrodef name="env-require"> + <attribute name="name"/> + <sequential> + <fail message="Environment variable @{name} not set!"> + <condition> + <not><isset property="env.@{name}"/></not> + </condition> + </fail> + </sequential> + </macrodef> + <env-require name="JRE_HOME"/> + <env-require name="KOTLIN_HOME"/> + + <!-- define the kotlin task --> + <property name="kotlin.lib" value="${env.KOTLIN_HOME}/libexec/lib"/> + <typedef resource="org/jetbrains/kotlin/ant/antlib.xml" + classpath="${kotlin.lib}/kotlin-ant.jar"/> + + <!-- define the package-building tasks --> + <taskdef resource="com/sun/javafx/tools/ant/antlib.xml" + uri="javafx:com.sun.javafx.tools.ant" + classpath="${env.JRE_HOME}/lib/ant-javafx.jar"/> + + <!-- cribbed from https://stackoverflow.com/questions/7129672/uppercase-lowercase-capitalize-an-ant-property --> + <scriptdef language="javascript" name="toLowerCase"> + <attribute name="value" /> + <attribute name="target" /> + <![CDATA[ + project.setProperty( attributes.get( "target" ), + attributes.get( "value" ).toLowerCase() ); + ]]> + </scriptdef> + + <!-- Define the properties used by the build --> + <property name="app.name" value="${ant.project.name}"/> + <property name="app.entry" value="name.blackcap.exifwasher.MainKt"/> + <toLowerCase target="lc.app.name" value="${app.name}"/> + <property name="jar.name" value="${basedir}/${lc.app.name}.jar"/> + <property name="work.jar" value="${basedir}/work.jar"/> + <property name="lib.home" value="${basedir}/lib"/> + <property name="src.home" value="${basedir}/src"/> + <property name="nat.dir" value="${src.home}/name/blackcap/exifwasher/exiv2"/> + <property name="bin.dir" value="${src.home}/name/blackcap/exifwasher/binaries"/> + + <!-- help message --> + <target name="help"> + <echo>You can use the following targets:</echo> + <echo> </echo> + <echo> help : (default) Prints this message </echo> + <echo> all : Cleans, compiles, and stages application</echo> + <echo> clean : Deletes work directories</echo> + <echo> compile : Compiles servlets into class files</echo> + <echo> jar : Make JAR file.</echo> + <echo> </echo> + <echo>For example, to clean, compile, and package all at once, run:</echo> + <echo>prompt> ant all </echo> + </target> + + <!-- Define the CLASSPATH --> + <target name="classpath"> + <path id="std.classpath"> + <fileset dir="${lib.home}"> + <include name="*.jar"/> + </fileset> + </path> + <path id="compile.classpath"> + <path refid="std.classpath"/> + <pathelement location="${src.home}"/> + </path> + <path id="test.classpath"> + <path refid="std.classpath"/> + <pathelement location="${work.home}"/> + </path> + </target> + + <!-- do everything but install --> + <target name="all" depends="jar" + description="Clean work dirs, compile, make JAR."/> + + <!-- compile *.kt to *.class --> + <target name="compile" depends="classpath" + description="Compile Java sources to ${work.home}"> + <kotlinc src="${src.home}" output="${work.jar}" + classpathref="compile.classpath"/> + </target> + + <!-- make .jar file --> + <target name="jar" depends="compile" description="Create JAR file."> + <jar destfile="${jar.name}"> + <manifest> + <attribute name="Main-Class" value="${app.entry}"/> + </manifest> + <zipgroupfileset dir="${lib.home}" includes="*.jar"/> + <zipfileset src="${work.jar}"/> + <zipfileset dir="${src.home}" includes="**/*.properties,**/*.dll,**/*.so,**/*.dylib"/> + </jar> + </target> + + <!-- for making bundled apps --> + <macrodef name="bundle"> + <attribute name="type"/> + <element name="args"/> + <sequential> + <fx:deploy nativeBundles="@{type}" outdir="${basedir}" outfile="${app.name}" + signBundle="false"> + <fx:application mainClass="${app.entry}" name="${app.name}" toolkit="swing" + version="1.0"/> + <fx:info description="ExifWasher" title="${app.name}" + vendor="David Barts <n5jrn@me.com>" + copyright="© MMXX, David W. Barts"/> + <fx:resources> + <fx:fileset dir="${basedir}" includes="${lc.app.name}.jar"/> + </fx:resources> + <fx:bundleArgument arg="runtime" value="${env.JRE_HOME}"/> + <args/> + </fx:deploy> + </sequential> + </macrodef> + + <target name="macapp" depends="jar" description="Create MacOS app bundle."> + <bundle type="image"> + <args> + <fx:bundleArgument arg="jvmOptions" value="-Xdock:name=${app.name}"/> + </args> + </bundle> + </target> + + <target name="app" depends="jar" description="Create app bundle."> + <bundle type="image"/> + </target> + + <target name="rpm" depends="jar" description="Create RPM package."> + <bundle type="rpm"/> + </target> + + <target name="deb" depends="jar" description="Create Debian package."> + <bundle type="deb"/> + </target> + +</project>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/setup.sh Tue Mar 31 13:24:48 2020 -0700 @@ -0,0 +1,11 @@ +#!/bin/bash + +export JRE_HOME="$(/usr/libexec/java_home)" +export KOTLIN_HOME="/usr/local/Cellar/kotlin/1.3.71" +export EXIV2_HOME="$HOME/temp/exiv2/exiv2-0.27.2-Source" + +export ANT_HOME="$HOME/java/apache-ant-1.10.1" +if [[ "$PATH" != *$ANT_HOME/bin* ]] +then + export PATH="$ANT_HOME/bin:$PATH" +fi
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/name/blackcap/exifwasher/Files.kt Tue Mar 31 13:24:48 2020 -0700 @@ -0,0 +1,85 @@ +/* + * For dealing with files. + */ +package name.blackcap.exifwasher + +import java.io.BufferedReader +import java.io.File +import java.io.FileInputStream +import java.io.InputStreamReader +import java.util.Properties +import java.util.logging.FileHandler +import java.util.logging.Level +import java.util.logging.Logger +import java.util.logging.SimpleFormatter + +/* OS Type */ + +enum class OS { + MAC, UNIX, WINDOWS, OTHER; + companion object { + private val rawType = System.getProperty("os.name")?.toLowerCase() + val type = if (rawType == null) { + OTHER + } else if (rawType.contains("win")) { + WINDOWS + } else if (rawType.contains("mac")) { + MAC + } else if (rawType.contains("nix") || rawType.contains("nux") || rawType.contains("aix") || rawType.contains("sunos")) { + UNIX + } else { + OTHER + } + } +} + +/* joins path name components to java.io.File */ + +fun joinPath(base: String, vararg rest: String) = rest.fold(File(base), ::File) + +/* file names */ + +private val SHORTNAME = "exifwasher" +private val LONGNAME = "name.blackcap." + SHORTNAME +private val HOME = System.getenv("HOME") +private val APPDATA = System.getenv("APPDATA") +val PF_DIR = when (OS.type) { + OS.MAC -> joinPath(HOME, "Library", "Application Support", LONGNAME) + OS.WINDOWS -> joinPath(APPDATA, "Roaming", LONGNAME) + else -> joinPath(HOME, "." + SHORTNAME) +} +val LF_DIR = when (OS.type) { + OS.MAC -> joinPath(HOME, "Library", "Application Support", LONGNAME) + OS.WINDOWS -> joinPath(APPDATA, "Local", LONGNAME) + else -> joinPath(HOME, "." + SHORTNAME) +} +val PROP_FILE = File(PF_DIR, SHORTNAME + ".properties") +val LOG_FILE = File(LF_DIR, SHORTNAME + ".log") + +/* make some needed directories */ + +private fun File.makeIfNeeded() = if (exists()) { true } else { mkdirs() } + +/* make some usable objects */ + +val DPROPERTIES = Properties().apply { + OS::class.java.getResourceAsStream("default.properties").use { load(it) } +} + +val PROPERTIES = Properties(DPROPERTIES).apply { + PF_DIR.makeIfNeeded() + PROP_FILE.createNewFile() + BufferedReader(InputStreamReader(FileInputStream(PROP_FILE), "UTF-8")).use { + load(it) + } +} + +val LOGGER = run { + LF_DIR.makeIfNeeded() + Logger.getLogger(LONGNAME).apply { + addHandler(FileHandler(LOG_FILE.toString()).apply { + formatter = SimpleFormatter() }) + level = Level.CONFIG + useParentHandlers = false + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/name/blackcap/exifwasher/Test.kt Tue Mar 31 13:24:48 2020 -0700 @@ -0,0 +1,24 @@ +/* + * A basic test of the library: try to use it to print out the EXIF + * data. + */ +package name.blackcap.exifwasher + +import name.blackcap.exifwasher.exiv2.* + +/* entry point */ +fun main(args: Array<String>) { + println("java.class.path = " + System.getProperty("java.class.path")) + if (args.size != 1) { + System.err.println("expecting a file name") + System.exit(1) + } + val image = Image(args[0]) + val meta = image.metadata + val keys = meta.keys + keys.sort() + keys.forEach { + val v = meta[it] + println("${it}: ${v.type} = ${v.value}") + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/name/blackcap/exifwasher/default.properties Tue Mar 31 13:24:48 2020 -0700 @@ -0,0 +1,1 @@ +# A placeholder, because we currently don't use properties.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/name/blackcap/exifwasher/exiv2/ExifData.kt Tue Mar 31 13:24:48 2020 -0700 @@ -0,0 +1,30 @@ +/* + * Some image EXIF metadata. + */ +package name.blackcap.exifwasher.exiv2 + +import kotlin.collections.Iterable +import kotlin.collections.Iterator + +public class ExifData(ptr: Long) { + init { + Initialize.libraries() + } + + private external fun _erase(key: String): Unit + private external fun _value(key: String): Value + private external fun _keys(): Array<String> + + private val pointer = ptr + + val keys: Array<String> + get() { + return _keys() + } + + fun erase(key: String): Unit = _erase(key) + + public data class Value(val type: String, val value: String) + + operator fun get(key: String): Value = _value(key) +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/name/blackcap/exifwasher/exiv2/Exiv2Exception.kt Tue Mar 31 13:24:48 2020 -0700 @@ -0,0 +1,6 @@ +/* + * Exception thrown when something goes wrong in exiv2. + */ +package name.blackcap.exifwasher.exiv2 + +class Exiv2Exception(message: String): Exception(message) { }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/name/blackcap/exifwasher/exiv2/Image.kt Tue Mar 31 13:24:48 2020 -0700 @@ -0,0 +1,29 @@ +/* + * Represents an image file we're examining and manipulating the metadata of. + */ +package name.blackcap.exifwasher.exiv2 + +public class Image(path: String) { + init { + Initialize.libraries() + } + + private external fun _ctor(path: String): Long + private external fun _writeMetadata() + private external fun _getMetadata(): Long + private external fun _dtor() + + private val pointer: Long + init { + pointer = _ctor(path) + } + + val metadata: ExifData + get() { + return ExifData(_getMetadata()) + } + + fun store() = _writeMetadata() + + protected fun finalize() = _dtor() +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/name/blackcap/exifwasher/exiv2/Initialize.kt Tue Mar 31 13:24:48 2020 -0700 @@ -0,0 +1,81 @@ +/* + * Logic common to initializing all external (JNI) classes goes here. + */ +package name.blackcap.exifwasher.exiv2 + +import java.io.File +import java.io.FileOutputStream +import java.io.IOException +import java.util.jar.JarEntry +import java.util.jar.JarFile +import name.blackcap.exifwasher.LF_DIR +import name.blackcap.exifwasher.OS + +object Initialize { + private var initialized = false + + public fun libraries() { + /* no-op if already initialized */ + if (initialized) { + return + } + + /* use the appropriate binary for the system we're on */ + val subdir = when(OS.type) { + OS.UNIX -> "linux" + OS.MAC -> "mac" + OS.WINDOWS -> "windows" + OS.OTHER -> throw RuntimeException("unsupported OS!") + } + val ext = when(OS.type) { + OS.UNIX -> "so" + OS.MAC -> "dylib" + OS.WINDOWS -> "dll" + OS.OTHER -> throw RuntimeException("unsupported OS!") + } + + /* extract to scratch files, if needed, then load */ + val klass = Initialize::class.java + var myJar = JarFile(File(klass.getProtectionDomain().getCodeSource().getLocation().toURI())) + for (base in arrayOf("libexiv2", "libjni")) { + val eBase = "${base}.${ext}" + val sPath = "name/blackcap/exifwasher/binaries/${subdir}/${eBase}" + val source = myJar.getJarEntry(sPath) + if (source == null) { + die("${sPath} not found in jar") + } + val target = File(LF_DIR, eBase) + val tPath = target.toString() + try { + if (!target.exists() || target.lastModified() < source.getTime()) { + myJar.getInputStream(source).use { sfp -> + FileOutputStream(target).use { tfp -> + sfp.copyTo(tfp) + } + } + target.setExecutable(true) + target.setLastModified(source.getTime()) + } + } catch (e: IOException) { + val message = e.message ?: "I/O error" + die("unable to create ${tPath}: ${message}") + } + try { + println("loading: ${tPath}") /* debug */ + System.load(tPath) + } catch (e: UnsatisfiedLinkError) { + val message = e.message ?: "unsatisfied link" + die("unable to link ${tPath}: ${message}") + } + } + + initialized = true + System.err.println("libraries loaded") /* debug */ + } + + private fun die(message: String) { + /* should open a dialog */ + System.err.println(message) + System.exit(1) + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/name/blackcap/exifwasher/exiv2/native.cpp Tue Mar 31 13:24:48 2020 -0700 @@ -0,0 +1,167 @@ +#include <jni.h> +#include <exiv2/exiv2.hpp> +#include <iostream> +#include <iomanip> +#include <cassert> + +/* Functions for class name_blackcap_exifwasher_exiv2_Image */ + +#ifndef _Included_name_blackcap_exifwasher_exiv2_Image +#define _Included_name_blackcap_exifwasher_exiv2_Image +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Utility function to get pointer field. + */ +jlong getPointer(JNIEnv *jEnv, jobject jThis) { + return jEnv->GetLongField(jThis, jEnv->GetFieldID(jEnv->GetObjectClass(jThis), "pointer", "J")); +} + +/* + * Class: name_blackcap_exifwasher_exiv2_Image + * Method: _ctor + * Signature: (Ljava/lang/String;)J + */ +JNIEXPORT jlong JNICALL Java_name_blackcap_exifwasher_exiv2_Image__1ctor + (JNIEnv *jEnv, jobject jThis, jstring path) { + const char *cPath = jEnv->GetStringUTFChars(path, NULL); + jlong ret = 0; + try { + ret = reinterpret_cast<jlong> (Exiv2::ImageFactory::open(cPath).release()); + } catch (...) { + jclass ex = jEnv->FindClass("name/blackcap/exifwasher/exiv2/Exiv2Exception"); + const char *pfx = "unable to open "; + char *message = (char *) malloc(strlen(cPath) + strlen(pfx) + 1); + strcpy(message, pfx); + strcat(message, cPath); + jEnv->ThrowNew(ex, message); + free(message); + } + jEnv->ReleaseStringUTFChars(path, cPath); + return ret; + } + +/* + * Class: name_blackcap_exifwasher_exiv2_Image + * Method: _writeMetadata + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_name_blackcap_exifwasher_exiv2_Image__1writeMetadata + (JNIEnv *jEnv, jobject jThis) { + Exiv2::Image *image = reinterpret_cast<Exiv2::Image *> (getPointer(jEnv, jThis)); + try { + image->writeMetadata(); + } catch (...) { + jclass ex = jEnv->FindClass("name/blackcap/exifwasher/exiv2/Exiv2Exception"); + jEnv->ThrowNew(ex, "unable to write metadata"); + } + } + +/* + * Class: name_blackcap_exifwasher_exiv2_Image + * Method: _getMetadata + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_name_blackcap_exifwasher_exiv2_Image__1getMetadata + (JNIEnv *jEnv, jobject jThis) { + Exiv2::Image *image = reinterpret_cast<Exiv2::Image *> (getPointer(jEnv, jThis)); + try { + image->readMetadata(); + } catch (...) { + jclass ex = jEnv->FindClass("name/blackcap/exifwasher/exiv2/Exiv2Exception"); + jEnv->ThrowNew(ex, "unable to read metadata"); + } + return reinterpret_cast<jlong> (&(image->exifData())); + } + +/* + * Class: name_blackcap_exifwasher_exiv2_Image + * Method: _dtor + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_name_blackcap_exifwasher_exiv2_Image__1dtor + (JNIEnv *jEnv, jobject jThis) { + Exiv2::Image *image = reinterpret_cast<Exiv2::Image *> (getPointer(jEnv, jThis)); + delete image; + } + +#ifdef __cplusplus +} +#endif +#endif +/* Header for class name_blackcap_exifwasher_exiv2_ExifData */ + +#ifndef _Included_name_blackcap_exifwasher_exiv2_ExifData +#define _Included_name_blackcap_exifwasher_exiv2_ExifData +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: name_blackcap_exifwasher_exiv2_ExifData + * Method: _erase + * Signature: (Ljava/lang/String;)V + */ +JNIEXPORT void JNICALL Java_name_blackcap_exifwasher_exiv2_ExifData__1erase + (JNIEnv *jEnv, jobject jThis, jstring key) { + Exiv2::ExifData *data = reinterpret_cast<Exiv2::ExifData *> (getPointer(jEnv, jThis)); + const char *cKey = jEnv->GetStringUTFChars(key, NULL); + Exiv2::ExifData::iterator found = data->findKey(Exiv2::ExifKey(std::string(cKey))); + try { + data->erase(found); + } catch (...) { + jclass ex = jEnv->FindClass("name/blackcap/exifwasher/exiv2/Exiv2Exception"); + const char *pfx = "unable to delete "; + char *message = (char *) malloc(strlen(cKey) + strlen(pfx) + 1); + strcpy(message, pfx); + strcat(message, cKey); + jEnv->ThrowNew(ex, message); + free(message); + } + jEnv->ReleaseStringUTFChars(key, cKey); + } + +/* + * Class: name_blackcap_exifwasher_exiv2_ExifData + * Method: _value + * Signature: (Ljava/lang/String;)Lname/blackcap/exifwasher/exiv2/ExifData/Value; + */ +JNIEXPORT jobject JNICALL Java_name_blackcap_exifwasher_exiv2_ExifData__1value + (JNIEnv *jEnv, jobject jThis, jstring key) { + Exiv2::ExifData *data = reinterpret_cast<Exiv2::ExifData *> (getPointer(jEnv, jThis)); + const char *cKey = jEnv->GetStringUTFChars(key, NULL); + Exiv2::ExifData::const_iterator found = data->findKey(Exiv2::ExifKey(std::string(cKey))); + jEnv->ReleaseStringUTFChars(key, cKey); + if (found == data->end()) { + return NULL; + } + jclass klass = jEnv->FindClass("name/blackcap/exifwasher/exiv2/ExifData$Value"); + jstring type = jEnv->NewStringUTF(found->typeName()); + jstring value = jEnv->NewStringUTF(found->toString().c_str()); + jmethodID method = jEnv->GetMethodID(klass, "<init>", "(Ljava/lang/String;Ljava/lang/String;)V"); + return jEnv->NewObject(klass, method, type, value); + } + +/* + * Class: name_blackcap_exifwasher_exiv2_ExifData + * Method: _keys + * Signature: ()[Ljava/lang/String; + */ +JNIEXPORT jobjectArray JNICALL Java_name_blackcap_exifwasher_exiv2_ExifData__1keys + (JNIEnv *jEnv, jobject jThis) { + Exiv2::ExifData *data = reinterpret_cast<Exiv2::ExifData *> (getPointer(jEnv, jThis)); + jclass klass = jEnv->FindClass("java/lang/String"); + jobjectArray ret = jEnv->NewObjectArray(data->count(), klass, NULL); + Exiv2::ExifData::const_iterator end = data->end(); + jsize j = 0; + for (Exiv2::ExifData::const_iterator i = data->begin(); i != end; ++i) { + jEnv->SetObjectArrayElement(ret, j++, jEnv->NewStringUTF(i->key().c_str())); + } + return ret; + } + +#ifdef __cplusplus +} +#endif +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/name/blackcap/exifwasher/exiv2/native.hpp Tue Mar 31 13:24:48 2020 -0700 @@ -0,0 +1,91 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include <jni.h> +/* Header for class name_blackcap_exifwasher_exiv2_Image */ + +#ifndef _Included_name_blackcap_exifwasher_exiv2_Image +#define _Included_name_blackcap_exifwasher_exiv2_Image +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: name_blackcap_exifwasher_exiv2_Image + * Method: _ctor + * Signature: (Ljava/lang/String;)J + */ +JNIEXPORT jlong JNICALL Java_name_blackcap_exifwasher_exiv2_Image__1ctor + (JNIEnv *, jobject, jstring); + +/* + * Class: name_blackcap_exifwasher_exiv2_Image + * Method: _writeMetadata + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_name_blackcap_exifwasher_exiv2_Image__1writeMetadata + (JNIEnv *, jobject); + +/* + * Class: name_blackcap_exifwasher_exiv2_Image + * Method: _getMetadata + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_name_blackcap_exifwasher_exiv2_Image__1getMetadata + (JNIEnv *, jobject); + +/* + * Class: name_blackcap_exifwasher_exiv2_Image + * Method: _dtor + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_name_blackcap_exifwasher_exiv2_Image__1dtor + (JNIEnv *, jobject); + +#ifdef __cplusplus +} +#endif +#endif +/* Header for class name_blackcap_exifwasher_exiv2_ExifData */ + +#ifndef _Included_name_blackcap_exifwasher_exiv2_ExifData +#define _Included_name_blackcap_exifwasher_exiv2_ExifData +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: name_blackcap_exifwasher_exiv2_ExifData + * Method: _erase + * Signature: (Ljava/lang/String;)V + */ +JNIEXPORT void JNICALL Java_name_blackcap_exifwasher_exiv2_ExifData__1erase + (JNIEnv *, jobject, jstring); + +/* + * Class: name_blackcap_exifwasher_exiv2_ExifData + * Method: _value + * Signature: (Ljava/lang/String;)Lname/blackcap/exifwasher/exiv2/ExifData/Value; + */ +JNIEXPORT jobject JNICALL Java_name_blackcap_exifwasher_exiv2_ExifData__1value + (JNIEnv *, jobject, jstring); + +/* + * Class: name_blackcap_exifwasher_exiv2_ExifData + * Method: _keys + * Signature: ()[Ljava/lang/String; + */ +JNIEXPORT jobjectArray JNICALL Java_name_blackcap_exifwasher_exiv2_ExifData__1keys + (JNIEnv *, jobject); + +#ifdef __cplusplus +} +#endif +#endif +/* Header for class name_blackcap_exifwasher_exiv2_ExifData_Value */ + +#ifndef _Included_name_blackcap_exifwasher_exiv2_ExifData_Value +#define _Included_name_blackcap_exifwasher_exiv2_ExifData_Value +#ifdef __cplusplus +extern "C" { +#endif +#ifdef __cplusplus +} +#endif +#endif