/// <summary>Find or create a schema node if <c>createNodes</c> 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, <c>null</c> otherwise.
 /// Note: If <c>createNodes</c> is <c>true</c>, it is <b>always</b>
 /// returned a valid node.
 /// </returns>
 /// <exception cref="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)
 {
     Debug.Assert(tree.Parent == null);
     // make sure that its the root
     var schemaNode = tree.FindChildByName(namespaceUri);
     if (schemaNode == null && createNodes)
     {
         var po = new PropertyOptions { IsSchemaNode = true };
         schemaNode = new XmpNode(namespaceUri, po) { IsImplicit = true };
         // only previously registered schema namespaces are allowed in the XMP tree.
         var prefix = XmpMetaFactory.SchemaRegistry.GetNamespacePrefix(namespaceUri);
         if (prefix == null)
         {
             if (!string.IsNullOrEmpty(suggestedPrefix))
             {
                 prefix = XmpMetaFactory.SchemaRegistry.RegisterNamespace(namespaceUri, suggestedPrefix);
             }
             else
             {
                 throw new XmpException("Unregistered schema namespace URI", XmpErrorCode.BadSchema);
             }
         }
         schemaNode.Value = 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 &quot;uuid:bac965c4-9d87-11d9-9a30-000d936b79c4&quot;, plus InDesign
 /// 3.0 wrote them like &quot;bac965c4-9d87-11d9-9a30-000d936b79c4&quot;. 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="XmpException">Thrown if tweaking fails.</exception>
 private static void TweakOldXmp(XmpNode tree)
 {
     if (tree.Name != null && tree.Name.Length >= Utils.UuidLength)
     {
         var nameStr = tree.Name.ToLower();
         if (nameStr.StartsWith("uuid:"))
         {
             nameStr = nameStr.Substring (5);
         }
         if (Utils.CheckUuidFormat(nameStr))
         {
             // move UUID to xmpMM:InstanceID and remove it from the root node
             var path = XmpPathParser.ExpandXPath(XmpConstants.NsXmpMm, "InstanceID");
             var idNode = XmpNodeUtils.FindNode(tree, path, true, null);
             if (idNode != null)
             {
                 idNode.Options = null;
                 // Clobber any existing xmpMM:InstanceID.
                 idNode.Value = "uuid:" + nameStr;
                 idNode.RemoveChildren();
                 idNode.RemoveQualifiers();
                 tree.Name = null;
             }
             else
             {
                 throw new XmpException("Failure creating xmpMM:InstanceID", XmpErrorCode.InternalFailure);
             }
         }
     }
 }
 /// <summary>The outermost call is special.</summary>
 /// <remarks>
 /// The outermost call is special. The names almost certainly differ. The
 /// qualifiers (and hence options) will differ for an alias to the x-default
 /// item of a langAlt array.
 /// </remarks>
 /// <param name="aliasNode">the alias node</param>
 /// <param name="baseNode">the base node of the alias</param>
 /// <param name="outerCall">marks the outer call of the recursion</param>
 /// <exception cref="XmpException">Forwards XMP errors</exception>
 private static void CompareAliasedSubtrees(XmpNode aliasNode, XmpNode baseNode, bool outerCall)
 {
     if (!aliasNode.Value.Equals(baseNode.Value) || aliasNode.GetChildrenLength() != baseNode.GetChildrenLength())
     {
         throw new XmpException("Mismatch between alias and base nodes", XmpErrorCode.BadXmp);
     }
     if (!outerCall && (!aliasNode.Name.Equals(baseNode.Name) || !aliasNode.Options.Equals(baseNode.Options) || aliasNode.GetQualifierLength() != baseNode.GetQualifierLength()))
     {
         throw new XmpException("Mismatch between alias and base nodes", XmpErrorCode.BadXmp);
     }
     for (IIterator an = aliasNode.IterateChildren(), bn = baseNode.IterateChildren(); an.HasNext() && bn.HasNext(); )
     {
         var aliasChild = (XmpNode)an.Next();
         var baseChild = (XmpNode)bn.Next();
         CompareAliasedSubtrees(aliasChild, baseChild, false);
     }
     for (IIterator an1 = aliasNode.IterateQualifier(), bn1 = baseNode.IterateQualifier(); an1.HasNext() && bn1.HasNext(); )
     {
         var aliasQual = (XmpNode)an1.Next();
         var baseQual = (XmpNode)bn1.Next();
         CompareAliasedSubtrees(aliasQual, baseQual, false);
     }
 }
 /// <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(IXmpMeta xmp, XmpNode dmCopyright)
 {
     try
     {
         var dcSchema = XmpNodeUtils.FindSchemaNode(((XmpMeta)xmp).GetRoot(), XmpConstants.NsDC, true);
         var dmValue = dmCopyright.Value;
         var doubleLf = "\n\n";
         var 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(XmpConstants.NsDC, "rights", string.Empty, XmpConstants.XDefault, dmValue, null);
         }
         else
         {
             var xdIndex = XmpNodeUtils.LookupLanguageItem(dcRightsArray, XmpConstants.XDefault);
             if (xdIndex < 0)
             {
                 // 2. No x-default item, create from the first item.
                 var firstValue = dcRightsArray.GetChild(1).Value;
                 xmp.SetLocalizedText(XmpConstants.NsDC, "rights", string.Empty, XmpConstants.XDefault, firstValue, null);
                 xdIndex = XmpNodeUtils.LookupLanguageItem(dcRightsArray, XmpConstants.XDefault);
             }
             // 3. Look for a double linefeed in the x-default value.
             var defaultNode = dcRightsArray.GetChild(xdIndex);
             var defaultValue = defaultNode.Value;
             var 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.Value = defaultValue + doubleLf + dmValue;
                 }
             }
             else
             {
                 // 3B. Has double LF, compare the tail.
                 if (!defaultValue.Substring (lfPos + 2).Equals(dmValue))
                 {
                     // 3B2. Replace the x-default tail.
                     defaultNode.Value = defaultValue.Substring (0, lfPos + 2 - 0) + dmValue;
                 }
             }
         }
         // 4. Get rid of the xmpDM:copyright.
         dmCopyright.Parent.RemoveChild(dmCopyright);
     }
     catch (XmpException)
     {
     }
 }
 /// <summary>Write each of the parent's simple unqualified properties as an attribute.</summary>
 /// <remarks>
 /// Write each of the parent's simple unqualified properties as an attribute. Returns true if all
 /// of the properties are written as attributes.
 /// </remarks>
 /// <param name="parentNode">the parent property node</param>
 /// <param name="indent">the current indent level</param>
 /// <returns>Returns true if all properties can be rendered as RDF attribute.</returns>
 /// <exception cref="System.IO.IOException"/>
 private bool SerializeCompactRdfAttrProps(XmpNode parentNode, int indent)
 {
     var allAreAttrs = true;
     for (var it = parentNode.IterateChildren(); it.HasNext(); )
     {
         var prop = (XmpNode)it.Next();
         if (CanBeRdfAttrProp(prop))
         {
             WriteNewline();
             WriteIndent(indent);
             Write(prop.Name);
             Write("=\"");
             AppendNodeValue(prop.Value, true);
             Write('"');
         }
         else
         {
             allAreAttrs = false;
         }
     }
     return allAreAttrs;
 }
        /// <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="XmpException">Forwards XMP errors</exception>
        private static void MoveExplicitAliases(XmpNode tree, ParseOptions options)
        {
            if (!tree.HasAliases)
            {
                return;
            }
            tree.HasAliases = false;
            var strictAliasing = options.StrictAliasing;
            for (var schemaIt = tree.GetUnmodifiableChildren().Iterator(); schemaIt.HasNext(); )
            {
                var currSchema = (XmpNode)schemaIt.Next();
                if (!currSchema.HasAliases)
                {
                    continue;
                }
                for (var propertyIt = currSchema.IterateChildren(); propertyIt.HasNext(); )
                {
                    var currProp = (XmpNode)propertyIt.Next();
                    if (!currProp.IsAlias)
                    {
                        continue;
                    }
                    currProp.IsAlias = false;
                    // Find the base path, look for the base schema and root node.
                    var info = XmpMetaFactory.SchemaRegistry.FindAlias(currProp.Name);
                    if (info != null)
                    {
                        // find or create schema
                        var baseSchema = XmpNodeUtils.FindSchemaNode(tree, info.Namespace, null, true);
                        baseSchema.IsImplicit = false;
                        var baseNode = XmpNodeUtils.FindChildNode(baseSchema, info.Prefix + info.PropName, false);
                        if (baseNode == null)
                        {
                            if (info.AliasForm.IsSimple())
                            {
                                // A top-to-top alias, transplant the property.
                                // change the alias property name to the base name
                                var qname = info.Prefix + info.PropName;
                                currProp.Name = 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.Prefix + info.PropName, info.AliasForm.ToPropertyOptions());
                                baseSchema.AddChild(baseNode);
                                TransplantArrayItemAlias(propertyIt, currProp, baseNode);
                            }
                        }
                        else if (info.AliasForm.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.AliasForm.IsArrayAltText)
                            {
                                var xdIndex = XmpNodeUtils.LookupLanguageItem(baseNode, XmpConstants.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.HasAliases = false;
            }
        }
        /// <summary>Fixes the GPS Timestamp in EXIF.</summary>
        /// <param name="exifSchema">the EXIF schema node</param>
        /// <exception cref="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.
            var gpsDateTime = XmpNodeUtils.FindChildNode(exifSchema, "exif:GPSTimeStamp", false);

            if (gpsDateTime == null)
                return;

            try
            {
                var binGpsStamp = XmpCore.XmpUtils.ConvertToDate(gpsDateTime.Value);
                if (binGpsStamp.Year != 0 || binGpsStamp.Month != 0 || binGpsStamp.Day != 0)
                    return;

                var otherDate = XmpNodeUtils.FindChildNode(exifSchema, "exif:DateTimeOriginal", false)
                    ?? XmpNodeUtils.FindChildNode(exifSchema, "exif:DateTimeDigitized", false);

                var binOtherDate = XmpCore.XmpUtils.ConvertToDate(otherDate.Value);
                var cal = binGpsStamp.Calendar;
                cal.Set(CalendarEnum.Year, binOtherDate.Year);
                cal.Set(CalendarEnum.Month, binOtherDate.Month);
                cal.Set(CalendarEnum.DayOfMonth, binOtherDate.Day);
                binGpsStamp = new XmpDateTime(cal);
                gpsDateTime.Value = XmpCore.XmpUtils.ConvertFromDate(binGpsStamp);
            }
            catch (XmpException)
            {
                // Don't let a missing or bad date stop other things.
            }
        }
Beispiel #8
0
 /// <summary>
 /// The internals for setProperty() and related calls, used after the node is
 /// found or created.
 /// </summary>
 /// <param name="node">the newly created node</param>
 /// <param name="value">the node value, can be <c>null</c></param>
 /// <param name="newOptions">options for the new node, must not be <c>null</c>.</param>
 /// <param name="deleteExisting">flag if the existing value is to be overwritten</param>
 /// <exception cref="XmpException">thrown if options and value do not correspond</exception>
 internal void SetNode(XmpNode node, object value, PropertyOptions newOptions, bool deleteExisting)
 {
     if (deleteExisting)
     {
         node.Clear();
     }
     // its checked by setOptions(), if the merged result is a valid options set
     node.Options.MergeWith(newOptions);
     if (!node.Options.IsCompositeProperty)
     {
         // This is setting the value of a leaf node.
         XmpNodeUtils.SetNodeValue(node, value);
     }
     else
     {
         if (value != null && value.ToString().Length > 0)
         {
             throw new XmpException("Composite nodes can't have values", XmpErrorCode.BadXPath);
         }
         node.RemoveChildren();
     }
 }
 /// <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 <c>xml:lang</c> attribute was dropped from an
 /// <c>alt-text</c> item if the language was <c>x-default</c>.
 /// </remarks>
 /// <param name="dcSchema">the DC schema node</param>
 /// <exception cref="XmpException">Thrown if normalization fails</exception>
 private static void NormalizeDcArrays(XmpNode dcSchema)
 {
     for (var i = 1; i <= dcSchema.GetChildrenLength(); i++)
     {
         var currProp = dcSchema.GetChild(i);
         var arrayForm = (PropertyOptions)_dcArrayForms[currProp.Name];
         if (arrayForm == null)
         {
             continue;
         }
         if (currProp.Options.IsSimple)
         {
             // create a new array and add the current property as child,
             // if it was formerly simple
             var newArray = new XmpNode(currProp.Name, arrayForm);
             currProp.Name = XmpConstants.ArrayItemName;
             newArray.AddChild(currProp);
             dcSchema.ReplaceChild(i, newArray);
             // fix language alternatives
             if (arrayForm.IsArrayAltText && !currProp.Options.HasLanguage)
             {
                 var newLang = new XmpNode(XmpConstants.XmlLang, XmpConstants.XDefault, null);
                 currProp.AddQualifier(newLang);
             }
         }
         else
         {
             // clear array options and add corrected array form if it has been an array before
             currProp.Options.SetOption(PropertyOptions.ArrayFlag | PropertyOptions.ArrayOrderedFlag | PropertyOptions.ArrayAlternateFlag | PropertyOptions.ArrayAltTextFlag, false);
             currProp.Options.MergeWith(arrayForm);
             if (arrayForm.IsArrayAltText)
             {
                 // applying for "dc:description", "dc:rights", "dc:title"
                 RepairAltText(currProp);
             }
         }
     }
 }
Beispiel #10
0
 public XmpPropertyInfo450(XmpNode node, string baseNs, string path, string value)
 {
     _node = node;
     _baseNs = baseNs;
     Path = path;
     Value = value;
 }
Beispiel #11
0
 /// <summary>Constructor for a cloned metadata tree.</summary>
 /// <param name="tree">
 /// an prefilled metadata tree which fulfills all
 /// <c>XMPNode</c> contracts.
 /// </param>
 public XmpMeta(XmpNode tree)
 {
     _tree = tree;
 }
Beispiel #12
0
 /// <param name="currNode">the node that will be added to the path.</param>
 /// <param name="parentPath">the path up to this node.</param>
 /// <param name="currentIndex">the current array index if an array is traversed</param>
 /// <returns>Returns the updated path.</returns>
 protected string AccumulatePath(XmpNode currNode, string parentPath, int currentIndex)
 {
     string separator;
     string segmentName;
     if (currNode.Parent == null || currNode.Options.IsSchemaNode)
     {
         return null;
     }
     if (currNode.Parent.Options.IsArray)
     {
         separator = string.Empty;
         segmentName = "[" + currentIndex + "]";
     }
     else
     {
         separator = "/";
         segmentName = currNode.Name;
     }
     if (string.IsNullOrEmpty(parentPath))
     {
         return segmentName;
     }
     if (_enclosing.Options.IsJustLeafName)
     {
         return !segmentName.StartsWith("?") ? segmentName : segmentName.Substring (1);
     }
     // qualifier
     return parentPath + separator + segmentName;
 }
Beispiel #13
0
 /// <summary>Creates a property info object from an <c>XMPNode</c>.</summary>
 /// <param name="node">an <c>XMPNode</c></param>
 /// <param name="baseNs">the base namespace to report</param>
 /// <param name="path">the full property path</param>
 /// <returns>Returns a <c>XMPProperty</c>-object that serves representation of the node.</returns>
 protected static IXmpPropertyInfo CreatePropertyInfo(XmpNode node, string baseNs, string path)
 {
     var value = node.Options.IsSchemaNode ? null : node.Value;
     return new XmpPropertyInfo450(node, baseNs, path, value);
 }
Beispiel #14
0
 /// <summary>Constructor for the node iterator.</summary>
 /// <param name="enclosing"></param>
 /// <param name="visitedNode">the currently visited node</param>
 /// <param name="parentPath">the accumulated path of the node</param>
 /// <param name="index">the index within the parent node (only for arrays)</param>
 public NodeIterator(XmpIterator enclosing, XmpNode visitedNode, string parentPath, int index)
 {
     _enclosing = enclosing;
     _visitedNode = visitedNode;
     _state = IterateNode;
     if (visitedNode.Options.IsSchemaNode)
     {
         _enclosing.BaseNamespace = visitedNode.Name;
     }
     // for all but the root node and schema nodes
     _path = AccumulatePath(visitedNode, parentPath, index);
 }
 /// <summary>Serializes the general qualifier.</summary>
 /// <param name="node">the root node of the subtree</param>
 /// <param name="indent">the current indent level</param>
 /// <exception cref="System.IO.IOException">Forwards all writer exceptions.</exception>
 /// <exception cref="XmpException">If qualifier and element fields are mixed.</exception>
 private void SerializeCompactRdfGeneralQualifier(int indent, XmpNode node)
 {
     // The node has general qualifiers, ones that can't be
     // attributes on a property element.
     // Emit using the qualified property pseudo-struct form. The
     // value is output by a call
     // to SerializePrettyRDFProperty with emitAsRDFValue set.
     Write(" rdf:parseType=\"Resource\">");
     WriteNewline();
     SerializeCanonicalRdfProperty(node, false, true, indent + 1);
     for (var iq = node.IterateQualifier(); iq.HasNext(); )
     {
         var qualifier = (XmpNode)iq.Next();
         SerializeCanonicalRdfProperty(qualifier, false, false, indent + 1);
     }
 }
 /// <summary>
 /// Recursively handles the "value" for a node that must be written as an RDF
 /// property element.
 /// </summary>
 /// <remarks>
 /// Recursively handles the "value" for a node that must be written as an RDF
 /// property element. It does not matter if it is a top level property, a
 /// field of a struct, or an item of an array. The indent is that for the
 /// property element. The patterns bwlow ignore attribute qualifiers such as
 /// xml:lang, they don't affect the output form.
 /// <blockquote>
 /// <pre>
 /// &lt;ns:UnqualifiedStructProperty-1
 /// ... The fields as attributes, if all are simple and unqualified
 /// /&gt;
 /// &lt;ns:UnqualifiedStructProperty-2 rdf:parseType=&quot;Resource&quot;&gt;
 /// ... The fields as elements, if none are simple and unqualified
 /// &lt;/ns:UnqualifiedStructProperty-2&gt;
 /// &lt;ns:UnqualifiedStructProperty-3&gt;
 /// &lt;rdf:Description
 /// ... The simple and unqualified fields as attributes
 /// &gt;
 /// ... The compound or qualified fields as elements
 /// &lt;/rdf:Description&gt;
 /// &lt;/ns:UnqualifiedStructProperty-3&gt;
 /// &lt;ns:UnqualifiedArrayProperty&gt;
 /// &lt;rdf:Bag&gt; or Seq or Alt
 /// ... Array items as rdf:li elements, same forms as top level properties
 /// &lt;/rdf:Bag&gt;
 /// &lt;/ns:UnqualifiedArrayProperty&gt;
 /// &lt;ns:QualifiedProperty rdf:parseType=&quot;Resource&quot;&gt;
 /// &lt;rdf:value&gt; ... Property &quot;value&quot;
 /// following the unqualified forms ... &lt;/rdf:value&gt;
 /// ... Qualifiers looking like named struct fields
 /// &lt;/ns:QualifiedProperty&gt;
 /// </pre>
 /// </blockquote>
 /// *** Consider numbered array items, but has compatibility problems.
 /// Consider qualified form with rdf:Description and attributes.
 /// </remarks>
 /// <param name="parentNode">the parent node</param>
 /// <param name="indent">the current indent level</param>
 /// <exception cref="System.IO.IOException">Forwards writer exceptions</exception>
 /// <exception cref="XmpException">If qualifier and element fields are mixed.</exception>
 private void SerializeCompactRdfElementProps(XmpNode parentNode, int indent)
 {
     for (var it = parentNode.IterateChildren(); it.HasNext(); )
     {
         var node = (XmpNode)it.Next();
         if (CanBeRdfAttrProp(node))
         {
             continue;
         }
         var emitEndTag = true;
         var indentEndTag = true;
         // Determine the XML element name, write the name part of the start tag. Look over the
         // qualifiers to decide on "normal" versus "rdf:value" form. Emit the attribute
         // qualifiers at the same time.
         var elemName = node.Name;
         if (XmpConstants.ArrayItemName.Equals(elemName))
         {
             elemName = "rdf:li";
         }
         WriteIndent(indent);
         Write('<');
         Write(elemName);
         var hasGeneralQualifiers = false;
         var hasRdfResourceQual = false;
         for (var iq = node.IterateQualifier(); iq.HasNext(); )
         {
             var qualifier = (XmpNode)iq.Next();
             if (!RdfAttrQualifier.Contains(qualifier.Name))
             {
                 hasGeneralQualifiers = true;
             }
             else
             {
                 hasRdfResourceQual = "rdf:resource".Equals(qualifier.Name);
                 Write(' ');
                 Write(qualifier.Name);
                 Write("=\"");
                 AppendNodeValue(qualifier.Value, true);
                 Write('"');
             }
         }
         // Process the property according to the standard patterns.
         if (hasGeneralQualifiers)
         {
             SerializeCompactRdfGeneralQualifier(indent, node);
         }
         else
         {
             // This node has only attribute qualifiers. Emit as a property element.
             if (!node.Options.IsCompositeProperty)
             {
                 var result = SerializeCompactRdfSimpleProp(node);
                 emitEndTag = ((bool)result[0]);
                 indentEndTag = ((bool)result[1]);
             }
             else
             {
                 if (node.Options.IsArray)
                 {
                     SerializeCompactRdfArrayProp(node, indent);
                 }
                 else
                 {
                     emitEndTag = SerializeCompactRdfStructProp(node, indent, hasRdfResourceQual);
                 }
             }
         }
         // Emit the property element end tag.
         if (emitEndTag)
         {
             if (indentEndTag)
             {
                 WriteIndent(indent);
             }
             Write("</");
             Write(elemName);
             Write('>');
             WriteNewline();
         }
     }
 }
Beispiel #17
0
 /// <summary>Constructor for an empty metadata object.</summary>
 public XmpMeta()
 {
     // create root node
     _tree = new XmpNode(null, null, null);
 }
Beispiel #18
0
 /// <summary>Constructor</summary>
 /// <param name="enclosing"></param>
 /// <param name="parentNode">the node which children shall be iterated.</param>
 /// <param name="parentPath">the full path of the former node without the leaf node.</param>
 public NodeIteratorChildren(XmpIterator enclosing, XmpNode parentNode, string parentPath)
     : base(enclosing)
 {
     _enclosing = enclosing;
     if (parentNode.Options.IsSchemaNode)
     {
         _enclosing.BaseNamespace = parentNode.Name;
     }
     _parentPath = AccumulatePath(parentNode, parentPath, 1);
     _childrenIterator = parentNode.IterateChildren();
 }
Beispiel #19
0
 public XmpProperty682(object value, XmpNode propNode)
 {
     _value = value;
     _propNode = propNode;
 }
Beispiel #20
0
        /// <param name="source">The source XMP object.</param>
        /// <param name="destination">The destination XMP object.</param>
        /// <param name="doAllProperties">Do internal properties in addition to external properties.</param>
        /// <param name="replaceOldValues">Replace the values of existing properties.</param>
        /// <param name="deleteEmptyValues">Delete destination values if source property is empty.</param>
        /// <exception cref="XmpException">Forwards the Exceptions from the metadata processing</exception>
        public static void AppendProperties(IXmpMeta source, IXmpMeta destination, bool doAllProperties, bool replaceOldValues, bool deleteEmptyValues)
        {
            ParameterAsserts.AssertImplementation(source);
            ParameterAsserts.AssertImplementation(destination);

            var src = (XmpMeta)source;
            var dest = (XmpMeta)destination;
            for (var it = src.GetRoot().IterateChildren(); it.HasNext(); )
            {
                var sourceSchema = (XmpNode)it.Next();

                // Make sure we have a destination schema node
                var destSchema = XmpNodeUtils.FindSchemaNode(dest.GetRoot(), sourceSchema.Name, false);
                var createdSchema = false;

                if (destSchema == null)
                {
                    destSchema = new XmpNode(sourceSchema.Name, sourceSchema.Value, new PropertyOptions { IsSchemaNode = true });
                    dest.GetRoot().AddChild(destSchema);
                    createdSchema = true;
                }

                // Process the source schema's children.
                for (var ic = sourceSchema.IterateChildren(); ic.HasNext(); )
                {
                    var sourceProp = (XmpNode)ic.Next();
                    if (doAllProperties || !Utils.IsInternalProperty(sourceSchema.Name, sourceProp.Name))
                        AppendSubtree(dest, sourceProp, destSchema, replaceOldValues, deleteEmptyValues);
                }

                if (!destSchema.HasChildren && (createdSchema || deleteEmptyValues))
                {
                    // Don't create an empty schema / remove empty schema.
                    dest.GetRoot().RemoveChild(destSchema);
                }
            }
        }
Beispiel #21
0
 /// <summary>Locate or create the item node and set the value.</summary>
 /// <remarks>
 /// Locate or create the item node and set the value. Note the index
 /// parameter is one-based! The index can be in the range [1..size + 1] or
 /// "last()", normalize it and check the insert flags. The order of the
 /// normalization checks is important. If the array is empty we end up with
 /// an index and location to set item size + 1.
 /// </remarks>
 /// <param name="arrayNode">an array node</param>
 /// <param name="itemIndex">the index where to insert the item</param>
 /// <param name="itemValue">the item value</param>
 /// <param name="itemOptions">the options for the new item</param>
 /// <param name="insert">insert oder overwrite at index position?</param>
 /// <exception cref="XmpException"/>
 private void DoSetArrayItem(XmpNode arrayNode, int itemIndex, string itemValue, PropertyOptions itemOptions, bool insert)
 {
     var itemNode = new XmpNode(XmpConstants.ArrayItemName, null);
     itemOptions = XmpNodeUtils.VerifySetOptions(itemOptions, itemValue);
     // in insert mode the index after the last is allowed,
     // even ARRAY_LAST_ITEM points to the index *after* the last.
     var maxIndex = insert ? arrayNode.GetChildrenLength() + 1 : arrayNode.GetChildrenLength();
     if (itemIndex == XmpConstants.ArrayLastItem)
     {
         itemIndex = maxIndex;
     }
     if (1 <= itemIndex && itemIndex <= maxIndex)
     {
         if (!insert)
         {
             arrayNode.RemoveChild(itemIndex);
         }
         arrayNode.AddChild(itemIndex, itemNode);
         SetNode(itemNode, itemValue, itemOptions, false);
     }
     else
     {
         throw new XmpException("Array index out of bounds", XmpErrorCode.BadIndex);
     }
 }
Beispiel #22
0
 /// <summary>Remove all schema children according to the flag <c>doAllProperties</c>.</summary>
 /// <remarks>Empty schemas are automatically remove by <c>XMPNode</c>.</remarks>
 /// <param name="schemaNode">a schema node</param>
 /// <param name="doAllProperties">flag if all properties or only externals shall be removed.</param>
 /// <returns>Returns true if the schema is empty after the operation.</returns>
 private static bool RemoveSchemaChildren(XmpNode schemaNode, bool doAllProperties)
 {
     for (var it = schemaNode.IterateChildren(); it.HasNext(); )
     {
         var currProp = (XmpNode)it.Next();
         if (doAllProperties || !Utils.IsInternalProperty(schemaNode.Name, currProp.Name))
         {
             it.Remove();
         }
     }
     return !schemaNode.HasChildren;
 }
Beispiel #23
0
        /// <summary>
        /// Evaluates a raw node value to the given value type, apply special
        /// conversions for defined types in XMP.
        /// </summary>
        /// <param name="valueType">an int indicating the value type</param>
        /// <param name="propNode">the node containing the value</param>
        /// <returns>Returns a literal value for the node.</returns>
        /// <exception cref="XmpException"/>
        private static object EvaluateNodeValue(ValueType valueType, XmpNode propNode)
        {
            object value;
            var rawValue = propNode.Value;
            switch (valueType)
            {
                case ValueType.Boolean:
                {
                    value = XmpCore.XmpUtils.ConvertToBoolean(rawValue);
                    break;
                }

                case ValueType.Integer:
                {
                    value = XmpCore.XmpUtils.ConvertToInteger(rawValue);
                    break;
                }

                case ValueType.Long:
                {
                    value = XmpCore.XmpUtils.ConvertToLong(rawValue);
                    break;
                }

                case ValueType.Double:
                {
                    value = XmpCore.XmpUtils.ConvertToDouble(rawValue);
                    break;
                }

                case ValueType.Date:
                {
                    value = XmpCore.XmpUtils.ConvertToDate(rawValue);
                    break;
                }

                case ValueType.Calendar:
                {
                    var dt = XmpCore.XmpUtils.ConvertToDate(rawValue);
                    value = dt.Calendar;
                    break;
                }

                case ValueType.Base64:
                {
                    value = XmpCore.XmpUtils.DecodeBase64(rawValue);
                    break;
                }

                case ValueType.String:
                default:
                {
                    // leaf values return empty string instead of null
                    // for the other cases the converter methods provides a "null"
                    // value.
                    // a default value can only occur if this method is made public.
                    value = rawValue != null || propNode.Options.IsCompositeProperty ? rawValue : string.Empty;
                    break;
                }
            }
            return value;
        }
Beispiel #24
0
        /// <param name="destXmp">The destination XMP object.</param>
        /// <param name="sourceNode">the source node</param>
        /// <param name="destParent">the parent of the destination node</param>
        /// <param name="replaceOldValues">Replace the values of existing properties.</param>
        /// <param name="deleteEmptyValues">flag if properties with empty values should be deleted in the destination object.</param>
        /// <exception cref="XmpException"/>
        private static void AppendSubtree(XmpMeta destXmp, XmpNode sourceNode, XmpNode destParent, bool replaceOldValues, bool deleteEmptyValues)
        {
            var destNode = XmpNodeUtils.FindChildNode(destParent, sourceNode.Name, false);
            var valueIsEmpty = false;

            if (deleteEmptyValues)
                valueIsEmpty = sourceNode.Options.IsSimple ? string.IsNullOrEmpty(sourceNode.Value) : !sourceNode.HasChildren;

            if (deleteEmptyValues && valueIsEmpty)
            {
                if (destNode != null)
                    destParent.RemoveChild(destNode);
            }
            else
            {
                if (destNode == null)
                {
                    // The one easy case, the destination does not exist.
                    destParent.AddChild((XmpNode)sourceNode.Clone());
                }
                else
                {
                    if (replaceOldValues)
                    {
                        // The destination exists and should be replaced.
                        destXmp.SetNode(destNode, sourceNode.Value, sourceNode.Options, true);
                        destParent.RemoveChild(destNode);
                        destNode = (XmpNode)sourceNode.Clone();
                        destParent.AddChild(destNode);
                    }
                    else
                    {
                        // The destination exists and is not totally replaced. Structs and arrays are merged.
                        var sourceForm = sourceNode.Options;
                        var destForm = destNode.Options;
                        if (sourceForm != destForm)
                        {
                            return;
                        }

                        if (sourceForm.IsStruct)
                        {
                            // To merge a struct process the fields recursively. E.g. add simple missing fields.
                            // The recursive call to AppendSubtree will handle deletion for fields with empty
                            // values.
                            for (var it = sourceNode.IterateChildren(); it.HasNext(); )
                            {
                                var sourceField = (XmpNode)it.Next();
                                AppendSubtree(destXmp, sourceField, destNode, replaceOldValues, deleteEmptyValues);
                                if (deleteEmptyValues && !destNode.HasChildren)
                                    destParent.RemoveChild(destNode);
                            }
                        }
                        else if (sourceForm.IsArrayAltText)
                        {
                            // Merge AltText arrays by the "xml:lang" qualifiers. Make sure x-default is first.
                            // Make a special check for deletion of empty values. Meaningful in AltText arrays
                            // because the "xml:lang" qualifier provides unambiguous source/dest correspondence.
                            for (var it = sourceNode.IterateChildren(); it.HasNext(); )
                            {
                                var sourceItem = (XmpNode)it.Next();

                                if (!sourceItem.HasQualifier || !XmpConstants.XmlLang.Equals(sourceItem.GetQualifier(1).Name))
                                    continue;

                                var destIndex = XmpNodeUtils.LookupLanguageItem(destNode, sourceItem.GetQualifier(1).Value);
                                if (deleteEmptyValues && string.IsNullOrEmpty(sourceItem.Value))
                                {
                                    if (destIndex != -1)
                                    {
                                        destNode.RemoveChild(destIndex);
                                        if (!destNode.HasChildren)
                                            destParent.RemoveChild(destNode);
                                    }
                                }
                                else if (destIndex == -1)
                                {
                                    // Not replacing, keep the existing item.
                                    if (!XmpConstants.XDefault.Equals(sourceItem.GetQualifier(1).Value) || !destNode.HasChildren)
                                    {
                                        sourceItem.CloneSubtree(destNode);
                                    }
                                    else
                                    {
                                        var destItem = new XmpNode(sourceItem.Name, sourceItem.Value, sourceItem.Options);
                                        sourceItem.CloneSubtree(destItem);
                                        destNode.AddChild(1, destItem);
                                    }
                                }
                            }
                        }
                        else if (sourceForm.IsArray)
                        {
                            // Merge other arrays by item values. Don't worry about order or duplicates. Source
                            // items with empty values do not cause deletion, that conflicts horribly with
                            // merging.
                            for (var children = sourceNode.IterateChildren(); children.HasNext(); )
                            {
                                var sourceItem = (XmpNode)children.Next();

                                var match = false;
                                for (var id = destNode.IterateChildren(); id.HasNext(); )
                                {
                                    var destItem = (XmpNode)id.Next();
                                    if (ItemValuesMatch(sourceItem, destItem))
                                        match = true;
                                }

                                if (!match)
                                {
                                    destNode = (XmpNode)sourceItem.Clone();
                                    destParent.AddChild(destNode);
                                }
                            }
                        }
                    }
                }
            }
        }
 /// <summary>Make sure that the array is well-formed AltText.</summary>
 /// <remarks>
 /// Make sure that the array is well-formed AltText. Each item must be simple
 /// and have an "xml:lang" qualifier. If repairs are needed, keep simple
 /// non-empty items by adding the "xml:lang" with value "x-repair".
 /// </remarks>
 /// <param name="arrayNode">the property node of the array to repair.</param>
 /// <exception cref="XmpException">Forwards unexpected exceptions.</exception>
 private static void RepairAltText(XmpNode arrayNode)
 {
     if (arrayNode == null || !arrayNode.Options.IsArray)
     {
         // Already OK or not even an array.
         return;
     }
     // fix options
     arrayNode.Options.IsArrayOrdered = true;
     arrayNode.Options.IsArrayAlternate = true;
     arrayNode.Options.IsArrayAltText = true;
     for (var it = arrayNode.IterateChildren(); it.HasNext(); )
     {
         var currChild = (XmpNode)it.Next();
         if (currChild.Options.IsCompositeProperty)
         {
             // Delete non-simple children.
             it.Remove();
         }
         else if (!currChild.Options.HasLanguage)
         {
             if (string.IsNullOrEmpty(currChild.Value))
             {
                 // Delete empty valued children that have no xml:lang.
                 it.Remove();
             }
             else
             {
                 // Add an xml:lang qualifier with the value "x-repair".
                 var repairLang = new XmpNode(XmpConstants.XmlLang, "x-repair", null);
                 currChild.AddQualifier(repairLang);
             }
         }
     }
 }
Beispiel #26
0
        /// <summary>Compares two nodes including its children and qualifier.</summary>
        /// <param name="leftNode">an <c>XMPNode</c></param>
        /// <param name="rightNode">an <c>XMPNode</c></param>
        /// <returns>Returns true if the nodes are equal, false otherwise.</returns>
        /// <exception cref="XmpException">Forwards exceptions to the calling method.</exception>
        private static bool ItemValuesMatch(XmpNode leftNode, XmpNode rightNode)
        {
            var leftForm = leftNode.Options;
            var rightForm = rightNode.Options;

            if (leftForm.Equals(rightForm))
                return false;

            if (leftForm.GetOptions() == 0)
            {
                // Simple nodes, check the values and xml:lang qualifiers.
                if (!leftNode.Value.Equals(rightNode.Value))
                    return false;
                if (leftNode.Options.HasLanguage != rightNode.Options.HasLanguage)
                    return false;
                if (leftNode.Options.HasLanguage && !leftNode.GetQualifier(1).Value.Equals(rightNode.GetQualifier(1).Value))
                    return false;
            }
            else
            {
                if (leftForm.IsStruct)
                {
                    // Struct nodes, see if all fields match, ignoring order.
                    if (leftNode.GetChildrenLength() != rightNode.GetChildrenLength())
                        return false;

                    for (var it = leftNode.IterateChildren(); it.HasNext(); )
                    {
                        var leftField = (XmpNode)it.Next();
                        var rightField = XmpNodeUtils.FindChildNode(rightNode, leftField.Name, false);
                        if (rightField == null || !ItemValuesMatch(leftField, rightField))
                            return false;
                    }
                }
                else
                {
                    // Array nodes, see if the "leftNode" values are present in the
                    // "rightNode", ignoring order, duplicates,
                    // and extra values in the rightNode-> The rightNode is the
                    // destination for AppendProperties.
                    Debug.Assert(leftForm.IsArray);
                    for (var il = leftNode.IterateChildren(); il.HasNext(); )
                    {
                        var leftItem = (XmpNode)il.Next();
                        var match = false;

                        for (var ir = rightNode.IterateChildren(); ir.HasNext(); )
                        {
                            var rightItem = (XmpNode)ir.Next();
                            if (ItemValuesMatch(leftItem, rightItem))
                            {
                                match = true;
                                break;
                            }
                        }

                        if (!match)
                            return false;
                    }
                }
            }
            return true;
        }
 /// <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="XmpException">Forwards XMP errors</exception>
 private static void TransplantArrayItemAlias(IIterator propertyIt, XmpNode childNode, XmpNode baseArray)
 {
     if (baseArray.Options.IsArrayAltText)
     {
         if (childNode.Options.HasLanguage)
         {
             throw new XmpException("Alias to x-default already has a language qualifier", XmpErrorCode.BadXmp);
         }
         var langQual = new XmpNode(XmpConstants.XmlLang, XmpConstants.XDefault, null);
         childNode.AddQualifier(langQual);
     }
     propertyIt.Remove();
     childNode.Name = XmpConstants.ArrayItemName;
     baseArray.AddChild(childNode);
 }
Beispiel #28
0
        static bool HasLangQualifier(XmpNode node, string lang)
        {
            var qualifier = node.GetQualifier(XmpTag.XML_NS, "lang");

            return(qualifier != null && qualifier.Value == lang);
        }
 /// <summary>Remove all empty schemas from the metadata tree that were generated during the rdf parsing.</summary>
 /// <param name="tree">the root of the metadata tree</param>
 private static void DeleteEmptySchemas(XmpNode tree)
 {
     // Delete empty schema nodes. Do this last, other cleanup can make empty
     // schema.
     for (var it = tree.IterateChildren(); it.HasNext(); )
     {
         var schema = (XmpNode)it.Next();
         if (!schema.HasChildren)
         {
             it.Remove();
         }
     }
 }
Beispiel #30
0
 public XmpProperty407(XmpNode itemNode)
 {
     _itemNode = itemNode;
 }
Beispiel #31
0
        static void Main(string[] args)
        {
            if (args.Length != 3)
            {
                System.Console.WriteLine("The correct number of arguments is 3");
                System.Console.WriteLine("Description:");
                System.Console.WriteLine("Metimpdf is the program for [met]adata [im]port into a [pdf] file");
                System.Console.WriteLine("as a part of the Math-Net.Ru Meta Data Creator Tools.");
                System.Console.WriteLine("Use:");
                System.Console.WriteLine("metimpdf <pdf file> <metadata> <output pdf file>");
                System.Console.WriteLine("Example:");
                System.Console.WriteLine("metimpdf source.pdf meta.data output.pdf");
                Console.Write("Press any key to continue . . . ");
                Console.ReadKey(true);
                return;
            }

            if (args.Length == 3)
            {
                //System.Console.WriteLine(args[0]);
                //System.Console.WriteLine(args[1]);
                //System.Console.WriteLine(args[2]);
                try{
                    string SourcePdfFile = args[0];
                    string MetadataFile  = args[1];
                    string OutputPdfFile = args[2];

                    string prismSchema = "http://prismstandard.org/namespaces/basic/2.2/";

                    // Создаем и регистрируем несуществующую в системе схему prism
                    XmpMetaFactory.SchemaRegistry.RegisterNamespace(prismSchema, "prism");

                    /**
                     * Создаём карту для корректной конвертации и вставки значений в metadata
                     * т.к. ключевые слова указанные в .data файле не существуют в пространствах
                     */
                    List <MapObject> map = new List <MapObject>();
                    map.Add(new MapObject(XmpConst.NS_DC, "Title", "title", ValueType.Alt));
                    map.Add(new MapObject(XmpConst.NS_DC, "Author", "creator", ValueType.Seq, false));
                    map.Add(new MapObject(XmpConst.NS_DC, "Keywords", "subject", ValueType.Bag, false));
                    map.Add(new MapObject(XmpConst.NS_DC, "Doi", "identifier", ValueType.Property));
                    map.Add(new MapObject(XmpConst.NS_DC, "Publisher", "publisher", ValueType.Bag));
                    map.Add(new MapObject(XmpConst.NS_DC, "Rights", "rights", ValueType.Alt));
                    map.Add(new MapObject(XmpConst.NS_DC, "Description", "description", ValueType.Alt));

                    map.Add(new MapObject(XmpConst.NS_PDF, "Keywords", ValueType.Property));
                    map.Add(new MapObject(XmpConst.NS_PDF, "Producer", ValueType.Property));

                    map.Add(new MapObject(XmpConst.NS_XMP, "CreatorTool", ValueType.Property));

                    map.Add(new MapObject(XmpConst.NS_XMP_RIGHTS, "Rights", "UsageTerms", ValueType.Alt));
                    map.Add(new MapObject(XmpConst.NS_XMP_RIGHTS, "Marked", "Marked", ValueType.Property));
                    map.Add(new MapObject(XmpConst.NS_XMP_RIGHTS, "RightsUrl", "WebStatement", ValueType.Property));

                    map.Add(new MapObject(prismSchema, "Doi", "doi", ValueType.Property));
                    map.Add(new MapObject(prismSchema, "DoiUrl", "url", ValueType.Property));
                    map.Add(new MapObject(prismSchema, "Issn", "issn", ValueType.Property));
                    map.Add(new MapObject(prismSchema, "eIssn", "eissn", ValueType.Property));
                    map.Add(new MapObject(prismSchema, "Volume", "volume", ValueType.Property));
                    map.Add(new MapObject(prismSchema, "Number", "number", ValueType.Property));
                    map.Add(new MapObject(prismSchema, "CoverDisplayDate", "coverDisplayDate", ValueType.Property));
                    map.Add(new MapObject(prismSchema, "CoverDate", "coverDate", ValueType.Property));
                    map.Add(new MapObject(prismSchema, "IssueName", "issueName", ValueType.Property));
                    map.Add(new MapObject(prismSchema, "PageRange", "pageRange", ValueType.Property));
                    map.Add(new MapObject(prismSchema, "StartingPage", "startingPage", ValueType.Property));
                    map.Add(new MapObject(prismSchema, "EndingPage", "endingPage", ValueType.Property));
                    map.Add(new MapObject(prismSchema, "AggregationType", "aggregationType", ValueType.Property));
                    map.Add(new MapObject(prismSchema, "Platform", "originPlatform", ValueType.Property));
                    map.Add(new MapObject(prismSchema, "PublicationName", "publicationName", ValueType.Property));
                    map.Add(new MapObject(prismSchema, "Edition", "edition", ValueType.Property));
                    map.Add(new MapObject(prismSchema, "Section", "section", ValueType.Property));
                    map.Add(new MapObject(prismSchema, "Subsection1", "subsection1", ValueType.Property));
                    map.Add(new MapObject(prismSchema, "Rights", "copyright", ValueType.Property));


                    // Открываем поток для чтения/записи значений
                    using (FileStream fs = new FileStream("metadata.xmp", FileMode.Create, FileAccess.ReadWrite)){
                        // Считываем все стрроки из .data файла
                        string[] lines = File.ReadAllLines(MetadataFile);

                        // Создаем словарь разбора строк в сопоставлении ключ:значение
                        Dictionary <string, string> info = new Dictionary <string, string>();

                        // цикл перебора строк для разбора в словарь ключ:значение
                        foreach (string line in lines)
                        {
                            string[] splitted = line.Split('=');          // делим строку попалам относительно знака =
                            string   key      = splitted[0].Trim();       // ключ(значение слева от = )
                            string   value    = splitted[1].Trim();       // значение(значение справа от =)
                            value = value.Substring(1, value.Length - 2); // убираем скобки {}
                            info.Add(key, value);
                        }

                        /**
                         * Создаём объект для корректной современной записи Xmp и в качестве значения конструктора
                         * передаём только поток
                         */
                        XmpWriter xmpWriter = new XmpWriter(fs);


                        xmpWriter.XmpMeta.SetProperty(prismSchema, "platform", "print");
                        xmpWriter.XmpMeta.SetProperty(prismSchema, "edition", "My print");
                        xmpWriter.XmpMeta.SetProperty(prismSchema, "section", "My print");
                        xmpWriter.XmpMeta.SetProperty(prismSchema, "subsection1", "My print");


                        // цикл перебора словаря для дальнейшего разбора
                        foreach (string key in info.Keys)
                        {
                            // цикл перебора карты на поиск соответствующих ключевым словам объектов
                            foreach (MapObject p in map)
                            {
                                if (p.Name == key)                 // если имя объекта равно ключевому слову из data то

                                /**
                                 * сохраняем значение в xmpWriter с помощь специального
                                 * упращенного метода сохранения(сам метод создан в конце класса)
                                 */
                                {
                                    SetValue(xmpWriter, p, info[key]);
                                }
                            }
                        }



                        // модификатор языка
                        XmpNode languageQualifier = new XmpNode("xml:lang", "x-default", new PropertyOptions {
                            Qualifier = true
                        });

                        // более низкий уровень взаимодействия с деревом xml для проставки дополнительных атрибутов
                        XmpNode groupNode = (xmpWriter.XmpMeta as XmpMetaImpl).Root.FindChildByName(XmpConst.NS_DC);

                        // находим элементы которым нужно указать языковую конструкцию
                        XmpNode titleDcNode        = groupNode.FindChildByName("dc:title");
                        XmpNode rightsDcNode       = groupNode.FindChildByName("dc:rights");
                        XmpNode descriptyopmDcNode = groupNode.FindChildByName("dc:description");

                        // проставляем модификатор
                        titleDcNode.GetChild(1).AddQualifier(languageQualifier);
                        rightsDcNode.GetChild(1).AddQualifier(languageQualifier);
                        descriptyopmDcNode.GetChild(1).AddQualifier(languageQualifier);

                        groupNode = (xmpWriter.XmpMeta as XmpMetaImpl).Root.FindChildByName(XmpConst.NS_XMP_RIGHTS);
                        XmpNode rightsXmpRightsNode = groupNode.FindChildByName("xmpRights:UsageTerms");
                        rightsXmpRightsNode.GetChild(1).AddQualifier(languageQualifier);

                        // Закрываем xmpWriter
                        xmpWriter.Close();
                    }     // после этой скобочки файл metadata.xmp сохранится и запишется автоматически

                    // Внедрение metadata.xmp в SourcePdfFile и вывод в OutputPdfFile
                    PdfReader  reader  = new PdfReader(SourcePdfFile);
                    PdfStamper stamper = new PdfStamper(reader, new FileStream(OutputPdfFile, FileMode.Create));
                    using (FileStream fs = File.OpenRead("metadata.xmp")){
                        byte[] buffer = new byte[fs.Length];
                        fs.Read(buffer, 0, buffer.Length);
                        stamper.XmpMetadata = buffer;
                    }

                    stamper.Close();
                    reader.Close();
                }
                // Catching iTextSharp.text.DocumentException if any
                catch (DocumentException de) {
                    throw de;
                }
                // Catching System.IO.IOException if any
                catch (IOException ioe) {
                    throw ioe;
                }
            }

            Console.Write("Press any key to continue . . . ");
            Console.ReadKey(true);
        }
 /// <summary>Serializes an array property.</summary>
 /// <param name="node">an XMPNode</param>
 /// <param name="indent">the current indent level</param>
 /// <exception cref="System.IO.IOException">Forwards the writer exceptions.</exception>
 /// <exception cref="XmpException">If qualifier and element fields are mixed.</exception>
 private void SerializeCompactRdfArrayProp(XmpNode node, int indent)
 {
     // This is an array.
     Write('>');
     WriteNewline();
     EmitRdfArrayTag(node, true, indent + 1);
     if (node.Options.IsArrayAltText)
     {
         XmpNodeUtils.NormalizeLangArray(node);
     }
     SerializeCompactRdfElementProps(node, indent + 2);
     EmitRdfArrayTag(node, false, indent + 1);
 }