/// <summary> /// The initial support for WAV files mapped a legacy ID3 audio copyright /// into a new xmpDM:copyright property. /// </summary> /// <remarks> /// The initial support for WAV files mapped a legacy ID3 audio copyright /// into a new xmpDM:copyright property. This is special case code to migrate /// that into dc:rights['x-default']. The rules: /// <pre> /// 1. If there is no dc:rights array, or an empty array - /// Create one with dc:rights['x-default'] set from double linefeed and xmpDM:copyright. /// 2. If there is a dc:rights array but it has no x-default item - /// Create an x-default item as a copy of the first item then apply rule #3. /// 3. If there is a dc:rights array with an x-default item, /// Look for a double linefeed in the value. /// A. If no double linefeed, compare the x-default value to the xmpDM:copyright value. /// A1. If they match then leave the x-default value alone. /// A2. Otherwise, append a double linefeed and /// the xmpDM:copyright value to the x-default value. /// B. If there is a double linefeed, compare the trailing text to the xmpDM:copyright value. /// B1. If they match then leave the x-default value alone. /// B2. Otherwise, replace the trailing x-default text with the xmpDM:copyright value. /// 4. In all cases, delete the xmpDM:copyright property. /// </pre> /// </remarks> /// <param name="xmp">the metadata object</param> /// <param name="dmCopyright">the "dm:copyright"-property</param> private static void MigrateAudioCopyright(XMPMeta xmp, XMPNode dmCopyright) { try { XMPNode dcSchema = XMPNodeUtils.FindSchemaNode(((XMPMetaImpl)xmp).GetRoot(), XMPConstConstants.NsDc, true); string dmValue = dmCopyright.GetValue(); string doubleLF = "\n\n"; XMPNode dcRightsArray = XMPNodeUtils.FindChildNode(dcSchema, "dc:rights", false); if (dcRightsArray == null || !dcRightsArray.HasChildren()) { // 1. No dc:rights array, create from double linefeed and xmpDM:copyright. dmValue = doubleLF + dmValue; xmp.SetLocalizedText(XMPConstConstants.NsDc, "rights", string.Empty, XMPConstConstants.XDefault, dmValue, null); } else { int xdIndex = XMPNodeUtils.LookupLanguageItem(dcRightsArray, XMPConstConstants.XDefault); if (xdIndex < 0) { // 2. No x-default item, create from the first item. string firstValue = dcRightsArray.GetChild(1).GetValue(); xmp.SetLocalizedText(XMPConstConstants.NsDc, "rights", string.Empty, XMPConstConstants.XDefault, firstValue, null); xdIndex = XMPNodeUtils.LookupLanguageItem(dcRightsArray, XMPConstConstants.XDefault); } // 3. Look for a double linefeed in the x-default value. XMPNode defaultNode = dcRightsArray.GetChild(xdIndex); string defaultValue = defaultNode.GetValue(); int lfPos = defaultValue.IndexOf(doubleLF); if (lfPos < 0) { // 3A. No double LF, compare whole values. if (!dmValue.Equals(defaultValue)) { // 3A2. Append the xmpDM:copyright to the x-default // item. defaultNode.SetValue(defaultValue + doubleLF + dmValue); } } else { // 3B. Has double LF, compare the tail. if (!Sharpen.Runtime.Substring(defaultValue, lfPos + 2).Equals(dmValue)) { // 3B2. Replace the x-default tail. defaultNode.SetValue(Sharpen.Runtime.Substring(defaultValue, 0, lfPos + 2) + dmValue); } } } // 4. Get rid of the xmpDM:copyright. dmCopyright.GetParent().RemoveChild(dmCopyright); } catch (XMPException) { } }
/// <summary>This is setting the value of a leaf node.</summary> /// <param name="node">an XMPNode</param> /// <param name="value">a value</param> internal static void SetNodeValue(XMPNode node, object value) { string strValue = SerializeNodeValue(value); if (!(node.GetOptions().IsQualifier() && XMPConstConstants.XmlLang.Equals(node.GetName()))) { node.SetValue(strValue); } else { node.SetValue(Utils.NormalizeLangValue(strValue)); } }
/// <summary>Find or create a schema node if <code>createNodes</code> is true.</summary> /// <param name="tree">the root of the xmp tree.</param> /// <param name="namespaceURI">a namespace</param> /// <param name="suggestedPrefix">If a prefix is suggested, the namespace is allowed to be registered.</param> /// <param name="createNodes"> /// a flag indicating if the node shall be created if not found. /// <em>Note:</em> The namespace must be registered prior to this call. /// </param> /// <returns> /// Returns the schema node if found, <code>null</code> otherwise. /// Note: If <code>createNodes</code> is <code>true</code>, it is <b>always</b> /// returned a valid node. /// </returns> /// <exception cref="Com.Adobe.Xmp.XMPException"> /// An exception is only thrown if an error occurred, not if a /// node was not found. /// </exception> internal static XMPNode FindSchemaNode(XMPNode tree, string namespaceURI, string suggestedPrefix, bool createNodes) { System.Diagnostics.Debug.Assert(tree.GetParent() == null); // make sure that its the root XMPNode schemaNode = tree.FindChildByName(namespaceURI); if (schemaNode == null && createNodes) { schemaNode = new XMPNode(namespaceURI, new PropertyOptions().SetSchemaNode(true)); schemaNode.SetImplicit(true); // only previously registered schema namespaces are allowed in the XMP tree. string prefix = XMPMetaFactory.GetSchemaRegistry().GetNamespacePrefix(namespaceURI); if (prefix == null) { if (suggestedPrefix != null && suggestedPrefix.Length != 0) { prefix = XMPMetaFactory.GetSchemaRegistry().RegisterNamespace(namespaceURI, suggestedPrefix); } else { throw new XMPException("Unregistered schema namespace URI", XMPErrorConstants.Badschema); } } schemaNode.SetValue(prefix); tree.AddChild(schemaNode); } return(schemaNode); }
/// <summary> /// Tweak old XMP: Move an instance ID from rdf:about to the /// <em>xmpMM:InstanceID</em> property. /// </summary> /// <remarks> /// Tweak old XMP: Move an instance ID from rdf:about to the /// <em>xmpMM:InstanceID</em> property. An old instance ID usually looks /// like "uuid:bac965c4-9d87-11d9-9a30-000d936b79c4", plus InDesign /// 3.0 wrote them like "bac965c4-9d87-11d9-9a30-000d936b79c4". If /// the name looks like a UUID simply move it to <em>xmpMM:InstanceID</em>, /// don't worry about any existing <em>xmpMM:InstanceID</em>. Both will /// only be present when a newer file with the <em>xmpMM:InstanceID</em> /// property is updated by an old app that uses <em>rdf:about</em>. /// </remarks> /// <param name="tree">the root of the metadata tree</param> /// <exception cref="Com.Adobe.Xmp.XMPException">Thrown if tweaking fails.</exception> private static void TweakOldXMP(XMPNode tree) { if (tree.GetName() != null && tree.GetName().Length >= Utils.UuidLength) { string nameStr = tree.GetName().ToLower(); if (nameStr.StartsWith("uuid:")) { nameStr = Sharpen.Runtime.Substring(nameStr, 5); } if (Utils.CheckUUIDFormat(nameStr)) { // move UUID to xmpMM:InstanceID and remove it from the root node XMPPath path = XMPPathParser.ExpandXPath(XMPConstConstants.NsXmpMm, "InstanceID"); XMPNode idNode = XMPNodeUtils.FindNode(tree, path, true, null); if (idNode != null) { idNode.SetOptions(null); // Clobber any existing xmpMM:InstanceID. idNode.SetValue("uuid:" + nameStr); idNode.RemoveChildren(); idNode.RemoveQualifiers(); tree.SetName(null); } else { throw new XMPException("Failure creating xmpMM:InstanceID", XMPErrorConstants.Internalfailure); } } } }
/// <summary>Fixes the GPS Timestamp in EXIF.</summary> /// <param name="exifSchema">the EXIF schema node</param> /// <exception cref="Com.Adobe.Xmp.XMPException">Thrown if the date conversion fails.</exception> private static void FixGPSTimeStamp(XMPNode exifSchema) { // Note: if dates are not found the convert-methods throws an exceptions, // and this methods returns. XMPNode gpsDateTime = XMPNodeUtils.FindChildNode(exifSchema, "exif:GPSTimeStamp", false); if (gpsDateTime == null) { return; } try { XMPDateTime binGPSStamp; XMPDateTime binOtherDate; binGPSStamp = XMPUtils.ConvertToDate(gpsDateTime.GetValue()); if (binGPSStamp.GetYear() != 0 || binGPSStamp.GetMonth() != 0 || binGPSStamp.GetDay() != 0) { return; } XMPNode otherDate = XMPNodeUtils.FindChildNode(exifSchema, "exif:DateTimeOriginal", false); if (otherDate == null) { otherDate = XMPNodeUtils.FindChildNode(exifSchema, "exif:DateTimeDigitized", false); } binOtherDate = XMPUtils.ConvertToDate(otherDate.GetValue()); Sharpen.Calendar cal = binGPSStamp.GetCalendar(); cal.Set(Sharpen.CalendarEnum.Year, binOtherDate.GetYear()); cal.Set(Sharpen.CalendarEnum.Month, binOtherDate.GetMonth()); cal.Set(Sharpen.CalendarEnum.DayOfMonth, binOtherDate.GetDay()); binGPSStamp = new XMPDateTimeImpl(cal); gpsDateTime.SetValue(XMPUtils.ConvertFromDate(binGPSStamp)); } catch (XMPException) { // Don't let a missing or bad date stop other things. return; } }
/// <summary>This is setting the value of a leaf node.</summary> /// <param name="node">an XMPNode</param> /// <param name="value">a value</param> internal static void SetNodeValue(XMPNode node, object value) { string strValue = SerializeNodeValue(value); if (!(node.GetOptions().IsQualifier() && XMPConstConstants.XmlLang.Equals(node.GetName()))) { node.SetValue(strValue); } else { node.SetValue(Utils.NormalizeLangValue(strValue)); } }
/// <summary>Find or create a schema node if <code>createNodes</code> is true.</summary> /// <param name="tree">the root of the xmp tree.</param> /// <param name="namespaceURI">a namespace</param> /// <param name="suggestedPrefix">If a prefix is suggested, the namespace is allowed to be registered.</param> /// <param name="createNodes"> /// a flag indicating if the node shall be created if not found. /// <em>Note:</em> The namespace must be registered prior to this call. /// </param> /// <returns> /// Returns the schema node if found, <code>null</code> otherwise. /// Note: If <code>createNodes</code> is <code>true</code>, it is <b>always</b> /// returned a valid node. /// </returns> /// <exception cref="Com.Adobe.Xmp.XMPException"> /// An exception is only thrown if an error occurred, not if a /// node was not found. /// </exception> internal static XMPNode FindSchemaNode(XMPNode tree, string namespaceURI, string suggestedPrefix, bool createNodes) { System.Diagnostics.Debug.Assert(tree.GetParent() == null); // make sure that its the root XMPNode schemaNode = tree.FindChildByName(namespaceURI); if (schemaNode == null && createNodes) { schemaNode = new XMPNode(namespaceURI, new PropertyOptions().SetSchemaNode(true)); schemaNode.SetImplicit(true); // only previously registered schema namespaces are allowed in the XMP tree. string prefix = XMPMetaFactory.GetSchemaRegistry().GetNamespacePrefix(namespaceURI); if (prefix == null) { if (suggestedPrefix != null && suggestedPrefix.Length != 0) { prefix = XMPMetaFactory.GetSchemaRegistry().RegisterNamespace(namespaceURI, suggestedPrefix); } else { throw new XMPException("Unregistered schema namespace URI", XMPErrorConstants.Badschema); } } schemaNode.SetValue(prefix); tree.AddChild(schemaNode); } return schemaNode; }
/// <summary>The parent is an RDF pseudo-struct containing an rdf:value field.</summary> /// <remarks> /// The parent is an RDF pseudo-struct containing an rdf:value field. Fix the /// XMP data model. The rdf:value node must be the first child, the other /// children are qualifiers. The form, value, and children of the rdf:value /// node are the real ones. The rdf:value node's qualifiers must be added to /// the others. /// </remarks> /// <param name="xmpParent">the parent xmp node</param> /// <exception cref="Com.Adobe.Xmp.XMPException">thown on parsing errors</exception> private static void FixupQualifiedNode(XMPNode xmpParent) { System.Diagnostics.Debug.Assert(xmpParent.GetOptions().IsStruct() && xmpParent.HasChildren()); XMPNode valueNode = xmpParent.GetChild(1); System.Diagnostics.Debug.Assert("rdf:value".Equals(valueNode.GetName())); // Move the qualifiers on the value node to the parent. // Make sure an xml:lang qualifier stays at the front. // Check for duplicate names between the value node's qualifiers and the parent's children. // The parent's children are about to become qualifiers. Check here, between the groups. // Intra-group duplicates are caught by XMPNode#addChild(...). if (valueNode.GetOptions().GetHasLanguage()) { if (xmpParent.GetOptions().GetHasLanguage()) { throw new XMPException("Redundant xml:lang for rdf:value element", XMPErrorConstants.Badxmp); } XMPNode langQual = valueNode.GetQualifier(1); valueNode.RemoveQualifier(langQual); xmpParent.AddQualifier(langQual); } // Start the remaining copy after the xml:lang qualifier. for (int i = 1; i <= valueNode.GetQualifierLength(); i++) { XMPNode qualifier = valueNode.GetQualifier(i); xmpParent.AddQualifier(qualifier); } // Change the parent's other children into qualifiers. // This loop starts at 1, child 0 is the rdf:value node. for (int i_1 = 2; i_1 <= xmpParent.GetChildrenLength(); i_1++) { XMPNode qualifier = xmpParent.GetChild(i_1); xmpParent.AddQualifier(qualifier); } // Move the options and value last, other checks need the parent's original options. // Move the value node's children to be the parent's children. System.Diagnostics.Debug.Assert(xmpParent.GetOptions().IsStruct() || xmpParent.GetHasValueChild()); xmpParent.SetHasValueChild(false); xmpParent.GetOptions().SetStruct(false); xmpParent.GetOptions().MergeWith(valueNode.GetOptions()); xmpParent.SetValue(valueNode.GetValue()); xmpParent.RemoveChildren(); for (Iterator it = valueNode.IterateChildren(); it.HasNext(); ) { XMPNode child = (XMPNode)it.Next(); xmpParent.AddChild(child); } }