/// <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>Moves an alias node of array form to another schema into an array</summary> /// <param name="propertyIt">the property iterator of the old schema (used to delete the property)</param> /// <param name="childNode">the node to be moved</param> /// <param name="baseArray">the base array for the array item</param> /// <exception cref="Com.Adobe.Xmp.XMPException">Forwards XMP errors</exception> private static void TransplantArrayItemAlias(Iterator propertyIt, XMPNode childNode, XMPNode baseArray) { if (baseArray.GetOptions().IsArrayAltText()) { if (childNode.GetOptions().GetHasLanguage()) { throw new XMPException("Alias to x-default already has a language qualifier", XMPErrorConstants.Badxmp); } XMPNode langQual = new XMPNode(XMPConstConstants.XmlLang, XMPConstConstants.XDefault, null); childNode.AddQualifier(langQual); } propertyIt.Remove(); childNode.SetName(XMPConstConstants.ArrayItemName); baseArray.AddChild(childNode); }
/// <summary> /// Undo the denormalization performed by the XMP used in Acrobat 5.<br /> /// If a Dublin Core array had only one item, it was serialized as a simple /// property. /// </summary> /// <remarks> /// Undo the denormalization performed by the XMP used in Acrobat 5.<br /> /// If a Dublin Core array had only one item, it was serialized as a simple /// property. <br /> /// The <code>xml:lang</code> attribute was dropped from an /// <code>alt-text</code> item if the language was <code>x-default</code>. /// </remarks> /// <param name="dcSchema">the DC schema node</param> /// <exception cref="Com.Adobe.Xmp.XMPException">Thrown if normalization fails</exception> private static void NormalizeDCArrays(XMPNode dcSchema) { for (int i = 1; i <= dcSchema.GetChildrenLength(); i++) { XMPNode currProp = dcSchema.GetChild(i); PropertyOptions arrayForm = (PropertyOptions)dcArrayForms.Get(currProp.GetName()); if (arrayForm == null) { continue; } else { if (currProp.GetOptions().IsSimple()) { // create a new array and add the current property as child, // if it was formerly simple XMPNode newArray = new XMPNode(currProp.GetName(), arrayForm); currProp.SetName(XMPConstConstants.ArrayItemName); newArray.AddChild(currProp); dcSchema.ReplaceChild(i, newArray); // fix language alternatives if (arrayForm.IsArrayAltText() && !currProp.GetOptions().GetHasLanguage()) { XMPNode newLang = new XMPNode(XMPConstConstants.XmlLang, XMPConstConstants.XDefault, null); currProp.AddQualifier(newLang); } } else { // clear array options and add corrected array form if it has been an array before currProp.GetOptions().SetOption(PropertyOptions.Array | PropertyOptions.ArrayOrdered | PropertyOptions.ArrayAlternate | PropertyOptions.ArrayAltText, false); currProp.GetOptions().MergeWith(arrayForm); if (arrayForm.IsArrayAltText()) { // applying for "dc:description", "dc:rights", "dc:title" RepairAltText(currProp); } } } } }
/// <summary>Visit all of the top level nodes looking for aliases.</summary> /// <remarks> /// Visit all of the top level nodes looking for aliases. If there is /// no base, transplant the alias subtree. If there is a base and strict /// aliasing is on, make sure the alias and base subtrees match. /// </remarks> /// <param name="tree">the root of the metadata tree</param> /// <param name="options">th parsing options</param> /// <exception cref="Com.Adobe.Xmp.XMPException">Forwards XMP errors</exception> private static void MoveExplicitAliases(XMPNode tree, ParseOptions options) { if (!tree.GetHasAliases()) { return; } tree.SetHasAliases(false); bool strictAliasing = options.GetStrictAliasing(); for (Iterator schemaIt = tree.GetUnmodifiableChildren().Iterator(); schemaIt.HasNext();) { XMPNode currSchema = (XMPNode)schemaIt.Next(); if (!currSchema.GetHasAliases()) { continue; } for (Iterator propertyIt = currSchema.IterateChildren(); propertyIt.HasNext();) { XMPNode currProp = (XMPNode)propertyIt.Next(); if (!currProp.IsAlias()) { continue; } currProp.SetAlias(false); // Find the base path, look for the base schema and root node. XMPAliasInfo info = XMPMetaFactory.GetSchemaRegistry().FindAlias(currProp.GetName()); if (info != null) { // find or create schema XMPNode baseSchema = XMPNodeUtils.FindSchemaNode(tree, info.GetNamespace(), null, true); baseSchema.SetImplicit(false); XMPNode baseNode = XMPNodeUtils.FindChildNode(baseSchema, info.GetPrefix() + info.GetPropName(), false); if (baseNode == null) { if (info.GetAliasForm().IsSimple()) { // A top-to-top alias, transplant the property. // change the alias property name to the base name string qname = info.GetPrefix() + info.GetPropName(); currProp.SetName(qname); baseSchema.AddChild(currProp); // remove the alias property propertyIt.Remove(); } else { // An alias to an array item, // create the array and transplant the property. baseNode = new XMPNode(info.GetPrefix() + info.GetPropName(), info.GetAliasForm().ToPropertyOptions()); baseSchema.AddChild(baseNode); TransplantArrayItemAlias(propertyIt, currProp, baseNode); } } else { if (info.GetAliasForm().IsSimple()) { // The base node does exist and this is a top-to-top alias. // Check for conflicts if strict aliasing is on. // Remove and delete the alias subtree. if (strictAliasing) { CompareAliasedSubtrees(currProp, baseNode, true); } propertyIt.Remove(); } else { // This is an alias to an array item and the array exists. // Look for the aliased item. // Then transplant or check & delete as appropriate. XMPNode itemNode = null; if (info.GetAliasForm().IsArrayAltText()) { int xdIndex = XMPNodeUtils.LookupLanguageItem(baseNode, XMPConstConstants.XDefault); if (xdIndex != -1) { itemNode = baseNode.GetChild(xdIndex); } } else { if (baseNode.HasChildren()) { itemNode = baseNode.GetChild(1); } } if (itemNode == null) { TransplantArrayItemAlias(propertyIt, currProp, baseNode); } else { if (strictAliasing) { CompareAliasedSubtrees(currProp, itemNode, true); } propertyIt.Remove(); } } } } } currSchema.SetHasAliases(false); } }
/// <summary> /// 7.2.7 propertyAttributeURIs /// anyURI - ( coreSyntaxTerms | rdf:Description | rdf:li | oldTerms ) /// 7.2.11 nodeElement /// start-element ( URI == nodeElementURIs, /// attributes == set ( ( idAttr | nodeIdAttr | aboutAttr )?, propertyAttr* ) ) /// propertyEltList /// end-element() /// Process the attribute list for an RDF node element. /// </summary> /// <remarks> /// 7.2.7 propertyAttributeURIs /// anyURI - ( coreSyntaxTerms | rdf:Description | rdf:li | oldTerms ) /// 7.2.11 nodeElement /// start-element ( URI == nodeElementURIs, /// attributes == set ( ( idAttr | nodeIdAttr | aboutAttr )?, propertyAttr* ) ) /// propertyEltList /// end-element() /// Process the attribute list for an RDF node element. A property attribute URI is /// anything other than an RDF term. The rdf:ID and rdf:nodeID attributes are simply ignored, /// as are rdf:about attributes on inner nodes. /// </remarks> /// <param name="xmp">the xmp metadata object that is generated</param> /// <param name="xmpParent">the parent xmp node</param> /// <param name="xmlNode">the currently processed XML node</param> /// <param name="isTopLevel">Flag if the node is a top-level node</param> /// <exception cref="Com.Adobe.Xmp.XMPException">thown on parsing errors</exception> private static void Rdf_NodeElementAttrs(XMPMetaImpl xmp, XMPNode xmpParent, XmlNode xmlNode, bool isTopLevel) { // Used to detect attributes that are mutually exclusive. int exclusiveAttrs = 0; for (int i = 0; i < xmlNode.Attributes.Count; i++) { XmlNode attribute = xmlNode.Attributes.Item(i); // quick hack, ns declarations do not appear in C++ // ignore "ID" without namespace if ("xmlns".Equals(attribute.Prefix) || (attribute.Prefix == null && "xmlns".Equals(attribute.Name))) { continue; } int attrTerm = GetRDFTermKind(attribute); switch (attrTerm) { case RdftermId: case RdftermNodeId: case RdftermAbout: { if (exclusiveAttrs > 0) { throw new XMPException("Mutally exclusive about, ID, nodeID attributes", XMPErrorConstants.Badrdf); } exclusiveAttrs++; if (isTopLevel && (attrTerm == RdftermAbout)) { // This is the rdf:about attribute on a top level node. Set // the XMP tree name if // it doesn't have a name yet. Make sure this name matches // the XMP tree name. if (xmpParent.GetName() != null && xmpParent.GetName().Length > 0) { if (!xmpParent.GetName().Equals(attribute.Value)) { throw new XMPException("Mismatched top level rdf:about values", XMPErrorConstants.Badxmp); } } else { xmpParent.SetName(attribute.Value); } } break; } case RdftermOther: { AddChildNode(xmp, xmpParent, attribute, attribute.Value, isTopLevel); break; } default: { throw new XMPException("Invalid nodeElement attribute", XMPErrorConstants.Badrdf); } } } }
/// <summary>Adds a child node.</summary> /// <param name="xmp">the xmp metadata object that is generated</param> /// <param name="xmpParent">the parent xmp node</param> /// <param name="xmlNode">the currently processed XML node</param> /// <param name="value">Node value</param> /// <param name="isTopLevel">Flag if the node is a top-level node</param> /// <returns>Returns the newly created child node.</returns> /// <exception cref="Com.Adobe.Xmp.XMPException">thown on parsing errors</exception> private static XMPNode AddChildNode(XMPMetaImpl xmp, XMPNode xmpParent, XmlNode xmlNode, string value, bool isTopLevel) { XMPSchemaRegistry registry = XMPMetaFactory.GetSchemaRegistry(); string @namespace = xmlNode.NamespaceURI; string childName; if (@namespace != null) { if (XMPConstConstants.NsDcDeprecated.Equals(@namespace)) { // Fix a legacy DC namespace @namespace = XMPConstConstants.NsDc; } string prefix = registry.GetNamespacePrefix(@namespace); if (prefix == null) { prefix = xmlNode.Prefix != null ? xmlNode.Prefix : DefaultPrefix; prefix = registry.RegisterNamespace(@namespace, prefix); } childName = prefix + xmlNode.LocalName; } else { throw new XMPException("XML namespace required for all elements and attributes", XMPErrorConstants.Badrdf); } // create schema node if not already there PropertyOptions childOptions = new PropertyOptions(); bool isAlias = false; if (isTopLevel) { // Lookup the schema node, adjust the XMP parent pointer. // Incoming parent must be the tree root. XMPNode schemaNode = XMPNodeUtils.FindSchemaNode(xmp.GetRoot(), @namespace, DefaultPrefix, true); schemaNode.SetImplicit(false); // Clear the implicit node bit. // need runtime check for proper 32 bit code. xmpParent = schemaNode; // If this is an alias set the alias flag in the node // and the hasAliases flag in the tree. if (registry.FindAlias(childName) != null) { isAlias = true; xmp.GetRoot().SetHasAliases(true); schemaNode.SetHasAliases(true); } } // Make sure that this is not a duplicate of a named node. bool isArrayItem = "rdf:li".Equals(childName); bool isValueNode = "rdf:value".Equals(childName); // Create XMP node and so some checks XMPNode newChild = new XMPNode(childName, value, childOptions); newChild.SetAlias(isAlias); // Add the new child to the XMP parent node, a value node first. if (!isValueNode) { xmpParent.AddChild(newChild); } else { xmpParent.AddChild(1, newChild); } if (isValueNode) { if (isTopLevel || !xmpParent.GetOptions().IsStruct()) { throw new XMPException("Misplaced rdf:value element", XMPErrorConstants.Badrdf); } xmpParent.SetHasValueChild(true); } if (isArrayItem) { if (!xmpParent.GetOptions().IsArray()) { throw new XMPException("Misplaced rdf:li element", XMPErrorConstants.Badrdf); } newChild.SetName(XMPConstConstants.ArrayItemName); } return newChild; }