package de.jeisfeld.augendiagnoselib.util.imagefile; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import android.support.annotation.Nullable; import android.util.Log; import com.adobe.xmp.XMPDateTime; import com.adobe.xmp.XMPDateTimeFactory; import com.adobe.xmp.XMPException; import com.adobe.xmp.XMPMeta; import com.adobe.xmp.XMPMetaFactory; import com.adobe.xmp.XMPPathFactory; import com.adobe.xmp.XMPSchemaRegistry; import com.adobe.xmp.options.PropertyOptions; import de.jeisfeld.augendiagnoselib.Application; /** * Helper class to handle XML data in a JPEG file. */ public class XmpHandler { // JAVADOC:OFF private static final String USER_COMMENT = "UserComment"; // Standard namespaces private static final String NS_DC = "http://purl.org/dc/elements/1.1/"; private static final String NS_MP1 = "http://ns.microsoft.com/photo/1.0/"; private static final String NS_MP2 = "http://ns.microsoft.com/photo/1.2/"; private static final String NS_MPRI = "http://ns.microsoft.com/photo/1.2/t/RegionInfo#"; private static final String NS_MPREG = "http://ns.microsoft.com/photo/1.2/t/Region#"; private static final String NS_EXIF = "http://ns.adobe.com/exif/1.0/"; // The custom namespace private static final String NS_JE = "http://ns.jeisfeld.de/augenfotos/1.0/"; // Items from the custom namespace public static final String ITEM_TITLE = "title"; public static final String ITEM_DESCRIPTION = "description"; public static final String ITEM_SUBJECT = "subject"; public static final String ITEM_COMMENT = "comment"; public static final String ITEM_PERSON = "person"; public static final String ITEM_X_CENTER = "xCenter"; public static final String ITEM_Y_CENTER = "yCenter"; public static final String ITEM_OVERLAY_SCALE_FACTOR = "overlayScaleFactor"; public static final String ITEM_X_POSITION = "xPosition"; public static final String ITEM_Y_POSITION = "yPosition"; public static final String ITEM_ZOOM_FACTOR = "zoomFactor"; public static final String ITEM_ORGANIZE_DATE = "organizeDate"; public static final String ITEM_RIGHT_LEFT = "rightLeft"; public static final String ITEM_BRIGHTNESS = "brightness"; public static final String ITEM_CONTRAST = "contrast"; public static final String ITEM_SATURATION = "saturation"; public static final String ITEM_COLOR_TEMPERATURE = "colorTemperature"; public static final String ITEM_PUPIL_SIZE = "pupilSize"; public static final String ITEM_PUPIL_X_OFFSET = "pupilXOffset"; public static final String ITEM_PUPIL_Y_OFFSET = "pupilYOffset"; public static final String ITEM_OVERLAY_COLOR = "overlayColor"; public static final String ITEM_FLAGS = "flags"; // JAVADOC:ON /** * Store if the registry is prepared via prepareRegistry. */ private static boolean mIsPrepared = false; /** * The XMP JpegMetadata stored in the handler. */ private XMPMeta mXmpMeta; /** * Create an XmpHandler from an XMP String. * * @param xmpString the XMP String. */ public XmpHandler(@Nullable final String xmpString) { prepareRegistry(); if (xmpString == null) { Log.w(Application.TAG, "xmpString is null "); mXmpMeta = XMPMetaFactory.create(); return; } try { String updatedXmpString = xmpString.trim(); int i = updatedXmpString.lastIndexOf('<'); if (i > 0 && updatedXmpString.substring(i).startsWith("<?xpacket end")) { updatedXmpString = updatedXmpString.substring(0, i); } mXmpMeta = XMPMetaFactory.parseFromString(updatedXmpString); } catch (Exception e) { Log.w(Application.TAG, "Error when parsing XMP Data ", e); mXmpMeta = XMPMetaFactory.create(); } } /** * Add namespaces to the registry. */ private static void prepareRegistry() { if (!mIsPrepared) { XMPSchemaRegistry registry = XMPMetaFactory.getSchemaRegistry(); try { registry.registerNamespace(NS_JE, "je:"); registry.registerNamespace(NS_MP1, "MicrosoftPhoto:"); registry.registerNamespace(NS_MP2, "MP:"); registry.registerNamespace(NS_MPRI, "MPRI:"); registry.registerNamespace(NS_MPREG, "MPReg:"); registry.registerNamespace(NS_DC, "dc:"); registry.registerNamespace(NS_EXIF, "exif:"); mIsPrepared = true; } catch (XMPException e) { Log.e(Application.TAG, "Exception while preparing XMP registry", e); } } } /** * Get an item from the custom namespace. * * @param item the name of the item. * @return the value of the item. */ public final String getJeItem(final String item) { try { return mXmpMeta.getPropertyString(NS_JE, item); } catch (Exception e) { return null; } } /** * Get an int item from the custom namespace. * * @param item the name of the item. * @return the value of the item. */ public final int getJeInt(final String item) { try { return mXmpMeta.getPropertyInteger(NS_JE, item); } catch (Exception e) { return 0; } } /** * Get a date item from the custom namespace. * * @param item the name of the item. * @return the value of the item. */ public final Date getJeDate(final String item) { try { XMPDateTime dateTime = mXmpMeta.getPropertyDate(NS_JE, item); return dateTime.getCalendar().getTime(); } catch (Exception e) { return null; } } /** * Get an item from the DC namespace. * * @param item the name of the item. * @return the value of the item. */ private String getDcItem(final String item) { try { return mXmpMeta.getArrayItem(NS_DC, item, 1).getValue(); } catch (Exception e) { return null; } } /** * Retrieve the image title. * * @return the image title. */ @Nullable public final String getDcTitle() { return getDcItem("title"); } /** * Retrieve the image description. * * @return the image description. */ @Nullable public final String getDcDescription() { return getDcItem("description"); } /** * Retrieve the image subject. * * @return the image subject. */ @Nullable public final String getDcSubject() { return getDcItem("subject"); } /** * Retrieve the user comment. * * @return the user comment. */ public final String getUserComment() { try { return mXmpMeta.getArrayItem(NS_EXIF, USER_COMMENT, 1).getValue(); } catch (Exception e) { return null; } } /** * Retrieve the image person name, from Microsoft namespace^. * * @return the image person name. */ public final String getMicrosoftPerson() { try { String path = "RegionInfo" + XMPPathFactory.composeArrayItemPath(XMPPathFactory.composeStructFieldPath(NS_MPRI, "Regions"), 1) + XMPPathFactory.composeStructFieldPath(NS_MPREG, "PersonDisplayName"); // String path = "RegionInfo" // + XMPPathFactory.composeArrayItemPath(XMPPathFactory.composeStructFieldPath(NS_MPRI, "Regions"), 1); return mXmpMeta.getPropertyString(NS_MP2, path); } catch (Exception e) { return null; } } /** * Dump the complete XMP object. * * @return a dump of the XMP object. */ public final String dumpObject() { return mXmpMeta.dumpObject(); } /** * Set an entry in the custom namespace. * * @param item the name of the entry. * @param value the value of the entry. * @throws XMPException thrown in case of issues with XMP handling. */ public final void setJeItem(final String item, @Nullable final String value) throws XMPException { if (value != null) { mXmpMeta.setProperty(NS_JE, item, value); } else { removeJeItem(item); } } /** * Set an int entry in the custom namespace. * * @param item the name of the entry. * @param value the value of the entry. * @throws XMPException thrown in case of issues with XMP handling. */ public final void setJeInt(final String item, final int value) throws XMPException { mXmpMeta.setProperty(NS_JE, item, value); } /** * Set a date entry in the custom namespace. * * @param item the name of the entry. * @param date the value of the entry. * @throws XMPException thrown in case of issues with XMP handling. */ public final void setJeDate(final String item, @Nullable final Date date) throws XMPException { if (date != null) { Calendar calendar = new GregorianCalendar(); calendar.setTime(date); XMPDateTime xmpDate = XMPDateTimeFactory.createFromCalendar(calendar); mXmpMeta.setPropertyDate(NS_JE, item, xmpDate); } } /** * Delete an entry from the custom namespace. * * @param item the name of the entry. */ private void removeJeItem(final String item) { mXmpMeta.deleteProperty(NS_JE, item); } /** * Set an entry in the DC namespace. * * @param item the name of the entry. * @param value the value of the entry. * @throws XMPException thrown in case of issues with XMP handling. */ private void setDcItem(final String item, @Nullable final String value) throws XMPException { if (value != null) { if (mXmpMeta.doesArrayItemExist(NS_DC, item, 1)) { mXmpMeta.setArrayItem(NS_DC, item, 1, value); } else { mXmpMeta.appendArrayItem(NS_DC, item, new PropertyOptions().setArray(true), value, null); } } } /** * Set the image title. * * @param title the image title. * @throws XMPException thrown in case of issues with XMP handling. */ public final void setDcTitle(final String title) throws XMPException { setDcItem("title", title); } /** * Set the image description. * * @param description the image description. * @throws XMPException thrown in case of issues with XMP handling. */ public final void setDcDescription(final String description) throws XMPException { setDcItem("description", description); } /** * Set the image subject. * * @param subject the image subject. * @throws XMPException thrown in case of issues with XMP handling. */ public final void setDcSubject(final String subject) throws XMPException { setDcItem("subject", subject); } /** * Set the User Comment. * * @param userComment the user comment. * @throws XMPException thrown in case of issues with XMP handling. */ public final void setUserComment(@Nullable final String userComment) throws XMPException { if (userComment != null) { if (mXmpMeta.doesArrayItemExist(NS_EXIF, USER_COMMENT, 1)) { mXmpMeta.setArrayItem(NS_EXIF, USER_COMMENT, 1, userComment); } else { mXmpMeta.appendArrayItem(NS_EXIF, USER_COMMENT, new PropertyOptions().setArray(true), userComment, null); } } } /** * Set the image person name. * * @param name the image person name. * @throws XMPException thrown in case of issues with XMP handling. */ public final void setMicrosoftPerson(@Nullable final String name) throws XMPException { if (name != null) { String path = "RegionInfo" + XMPPathFactory.composeArrayItemPath(XMPPathFactory.composeStructFieldPath(NS_MPRI, "Regions"), 1) + XMPPathFactory.composeStructFieldPath(NS_MPREG, "PersonDisplayName"); String path1 = "RegionInfo" + XMPPathFactory.composeStructFieldPath(NS_MPRI, "Regions"); if (!mXmpMeta.doesArrayItemExist(NS_MP2, path1, 1)) { mXmpMeta.appendArrayItem(NS_MP2, path1, new PropertyOptions().setArray(true), null, new PropertyOptions().setStruct(true)); } mXmpMeta.setProperty(NS_MP2, path, name); } } /** * Get the XMP String. * * @return the XMP String. * @throws XMPException thrown in case of issues with XMP handling. */ public final String getXmpString() throws XMPException { return XMPMetaFactory.serializeToString(mXmpMeta, null); } }