Esempio n. 1
0
 /// <summary>Make sure the x-default item is first.</summary>
 /// <remarks>
 /// Make sure the x-default item is first. Touch up &quot;single value&quot;
 /// arrays that have a default plus one real language. This case should have
 /// the same value for both items. Older Adobe apps were hardwired to only
 /// use the &quot;x-default&quot; item, so we copy that value to the other
 /// item.
 /// </remarks>
 /// <param name="arrayNode">an alt text array node</param>
 internal static void NormalizeLangArray(XMPNode arrayNode)
 {
     if (!arrayNode.GetOptions().IsArrayAltText())
     {
         return;
     }
     // check if node with x-default qual is first place
     for (int i = 2; i <= arrayNode.GetChildrenLength(); i++)
     {
         XMPNode child = arrayNode.GetChild(i);
         if (child.HasQualifier() && X_DEFAULT.Equals(child.GetQualifier(1).GetValue()))
         {
             // move node to first place
             try
             {
                 arrayNode.RemoveChild(i);
                 arrayNode.AddChild(1, child);
             }
             catch (XMPException)
             {
                 // cannot occur, because same child is removed before
                 System.Diagnostics.Debug.Assert(false);
             }
             if (i == 2)
             {
                 arrayNode.GetChild(2).SetValue(child.GetValue());
             }
             break;
         }
     }
 }
Esempio n. 2
0
        /// <summary>
        /// Make sure the x-default item is first. Touch up &quot;single value&quot;
        /// arrays that have a default plus one real language. This case should have
        /// the same value for both items. Older Adobe apps were hardwired to only
        /// use the &quot;x-default&quot; item, so we copy that value to the other
        /// item.
        /// </summary>
        /// <param name="arrayNode">
        ///            an alt text array node </param>
        internal static void NormalizeLangArray(XmpNode arrayNode)
        {
            if (!arrayNode.Options.ArrayAltText)
            {
                return;
            }

            // check if node with x-default qual is first place
            for (int i = 2; i <= arrayNode.ChildrenLength; i++)
            {
                XmpNode child = arrayNode.GetChild(i);
                if (child.HasQualifier() && X_DEFAULT.Equals(child.GetQualifier(1).Value))
                {
                    // move node to first place
                    try {
                        arrayNode.RemoveChild(i);
                        arrayNode.AddChild(1, child);
                    }
                    catch (XmpException) {
                        // cannot occur, because same child is removed before
                        Debug.Assert(false);
                    }

                    if (i == 2)
                    {
                        arrayNode.GetChild(2).Value = child.Value;
                    }
                    break;
                }
            }
        }
Esempio n. 3
0
        /// <summary>
        /// Normalize an xml:lang value so that comparisons are effectively case
        /// insensitive as required by RFC 3066 (which superceeds RFC 1766). The
        /// normalization rules:
        /// <ul>
        /// <li> The primary subtag is lower case, the suggested practice of ISO 639.
        /// <li> All 2 letter secondary subtags are upper case, the suggested
        /// practice of ISO 3166.
        /// <li> All other subtags are lower case.
        /// </ul>
        /// </summary>
        /// <param name="value">
        ///            raw value </param>
        /// <returns> Returns the normalized value. </returns>
        public static string NormalizeLangValue(string value)
        {
            // don't normalize x-default
            if (X_DEFAULT.Equals(value))
            {
                return(value);
            }

            int           subTag = 1;
            StringBuilder buffer = new StringBuilder();

            for (int i = 0; i < value.Length; i++)
            {
                switch (value[i])
                {
                case '-':
                case '_':
                    // move to next subtag and convert underscore to hyphen
                    buffer.Append('-');
                    subTag++;
                    break;

                case ' ':
                    // remove spaces
                    break;

                default:
                    // convert second subtag to uppercase, all other to lowercase
                    buffer.Append(subTag != 2 ? char.ToLower(value[i]) : char.ToUpper(value[i]));
                    break;
                }
            }
            return(buffer.ToString());
        }
Esempio n. 4
0
        /// <summary>Appends a language item to an alt text array.</summary>
        /// <param name="arrayNode">the language array</param>
        /// <param name="itemLang">the language of the item</param>
        /// <param name="itemValue">the content of the item</param>
        /// <exception cref="iText.Kernel.XMP.XMPException">Thrown if a duplicate property is added
        ///     </exception>
        internal static void AppendLangItem(XMPNode arrayNode, String itemLang, String itemValue
                                            )
        {
            XMPNode newItem  = new XMPNode(ARRAY_ITEM_NAME, itemValue, null);
            XMPNode langQual = new XMPNode(XML_LANG, itemLang, null);

            newItem.AddQualifier(langQual);
            if (!X_DEFAULT.Equals(langQual.GetValue()))
            {
                arrayNode.AddChild(newItem);
            }
            else
            {
                arrayNode.AddChild(1, newItem);
            }
        }
Esempio n. 5
0
        /// <summary>
        /// <ol>
        /// <li>Look for an exact match with the specific language.
        /// </summary>
        /// <remarks>
        /// <ol>
        /// <li>Look for an exact match with the specific language.
        /// <li>If a generic language is given, look for partial matches.
        /// <li>Look for an "x-default"-item.
        /// <li>Choose the first item.
        /// </ol>
        /// </remarks>
        /// <param name="arrayNode">the alt text array node</param>
        /// <param name="genericLang">the generic language</param>
        /// <param name="specificLang">the specific language</param>
        /// <returns>
        /// Returns the kind of match as an Integer and the found node in an
        /// array.
        /// </returns>
        /// <exception cref="iText.Kernel.XMP.XMPException"/>
        internal static Object[] ChooseLocalizedText(XMPNode arrayNode, String genericLang
                                                     , String specificLang)
        {
            // See if the array has the right form. Allow empty alt arrays,
            // that is what parsing returns.
            if (!arrayNode.GetOptions().IsArrayAltText())
            {
                throw new XMPException("Localized text array is not alt-text", XMPError.BADXPATH);
            }
            else
            {
                if (!arrayNode.HasChildren())
                {
                    return(new Object[] { XMPNodeUtils.CLT_NO_VALUES, null });
                }
            }
            int     foundGenericMatches = 0;
            XMPNode resultNode          = null;
            XMPNode xDefault            = null;

            // Look for the first partial match with the generic language.
            for (IEnumerator it = arrayNode.IterateChildren(); it.MoveNext();)
            {
                XMPNode currItem = (XMPNode)it.Current;
                // perform some checks on the current item
                if (currItem.GetOptions().IsCompositeProperty())
                {
                    throw new XMPException("Alt-text array item is not simple", XMPError.BADXPATH);
                }
                else
                {
                    if (!currItem.HasQualifier() || !XML_LANG.Equals(currItem.GetQualifier(1).GetName
                                                                         ()))
                    {
                        throw new XMPException("Alt-text array item has no language qualifier", XMPError.
                                               BADXPATH);
                    }
                }
                String currLang = currItem.GetQualifier(1).GetValue();
                // Look for an exact match with the specific language.
                if (specificLang.Equals(currLang))
                {
                    return(new Object[] { XMPNodeUtils.CLT_SPECIFIC_MATCH, currItem });
                }
                else
                {
                    if (genericLang != null && currLang.StartsWith(genericLang))
                    {
                        if (resultNode == null)
                        {
                            resultNode = currItem;
                        }
                        // ! Don't return/break, need to look for other matches.
                        foundGenericMatches++;
                    }
                    else
                    {
                        if (X_DEFAULT.Equals(currLang))
                        {
                            xDefault = currItem;
                        }
                    }
                }
            }
            // evaluate loop
            if (foundGenericMatches == 1)
            {
                return(new Object[] { XMPNodeUtils.CLT_SINGLE_GENERIC, resultNode });
            }
            else
            {
                if (foundGenericMatches > 1)
                {
                    return(new Object[] { XMPNodeUtils.CLT_MULTIPLE_GENERIC, resultNode });
                }
                else
                {
                    if (xDefault != null)
                    {
                        return(new Object[] { XMPNodeUtils.CLT_XDEFAULT, xDefault });
                    }
                    else
                    {
                        // Everything failed, choose the first item.
                        return(new Object[] { XMPNodeUtils.CLT_FIRST_ITEM, arrayNode.GetChild(1) });
                    }
                }
            }
        }
Esempio n. 6
0
        /// <seealso cref= XMPMeta#setLocalizedText(String, String, String, String, String,
        ///      PropertyOptions) </seealso>
        public virtual void SetLocalizedText(string schemaNs, string altTextName, string genericLang,
                                             string specificLang, string itemValue, PropertyOptions options)
        {
            ParameterAsserts.AssertSchemaNs(schemaNs);
            ParameterAsserts.AssertArrayName(altTextName);
            ParameterAsserts.AssertSpecificLang(specificLang);

            genericLang = genericLang != null?Utils.NormalizeLangValue(genericLang) : null;

            specificLang = Utils.NormalizeLangValue(specificLang);

            XmpPath arrayPath = XmpPathParser.ExpandXPath(schemaNs, altTextName);

            // Find the array node and set the options if it was just created.
            XmpNode arrayNode = XmpNodeUtils.FindNode(_tree, arrayPath, true,
                                                      new PropertyOptions(PropertyOptions.ARRAY |
                                                                          PropertyOptions.ARRAY_ORDERED |
                                                                          PropertyOptions.ARRAY_ALTERNATE |
                                                                          PropertyOptions.ARRAY_ALT_TEXT));

            if (arrayNode == null)
            {
                throw new XmpException("Failed to find or create array node", XmpError.BADXPATH);
            }
            if (!arrayNode.Options.ArrayAltText)
            {
                if (!arrayNode.HasChildren() && arrayNode.Options.ArrayAlternate)
                {
                    arrayNode.Options.ArrayAltText = true;
                }
                else
                {
                    throw new XmpException("Specified property is no alt-text array", XmpError.BADXPATH);
                }
            }

            // Make sure the x-default item, if any, is first.
            bool    haveXDefault = false;
            XmpNode xdItem       = null;

            foreach (XmpNode currItem in arrayNode.Children)
            {
                if (!currItem.HasQualifier() || !XML_LANG.Equals(currItem.GetQualifier(1).Name))
                {
                    throw new XmpException("Language qualifier must be first", XmpError.BADXPATH);
                }
                if (X_DEFAULT.Equals(currItem.GetQualifier(1).Value))
                {
                    xdItem       = currItem;
                    haveXDefault = true;
                    break;
                }
            }

            // Moves x-default to the beginning of the array
            if (xdItem != null && arrayNode.ChildrenLength > 1)
            {
                arrayNode.RemoveChild(xdItem);
                arrayNode.AddChild(1, xdItem);
            }

            // Find the appropriate item.
            // chooseLocalizedText will make sure the array is a language
            // alternative.
            object[] result   = XmpNodeUtils.ChooseLocalizedText(arrayNode, genericLang, specificLang);
            int      match    = (int)((int?)result[0]);
            XmpNode  itemNode = (XmpNode)result[1];

            bool specificXDefault = X_DEFAULT.Equals(specificLang);

            switch (match)
            {
            case XmpNodeUtils.CLT_NO_VALUES:

                // Create the array items for the specificLang and x-default, with
                // x-default first.
                XmpNodeUtils.AppendLangItem(arrayNode, X_DEFAULT, itemValue);
                haveXDefault = true;
                if (!specificXDefault)
                {
                    XmpNodeUtils.AppendLangItem(arrayNode, specificLang, itemValue);
                }
                break;

            case XmpNodeUtils.CLT_SPECIFIC_MATCH:

                if (!specificXDefault)
                {
                    // Update the specific item, update x-default if it matches the
                    // old value.
                    if (haveXDefault && xdItem != itemNode && xdItem != null && xdItem.Value.Equals(itemNode.Value))
                    {
                        xdItem.Value = itemValue;
                    }
                    // ! Do this after the x-default check!
                    itemNode.Value = itemValue;
                }
                else
                {
                    // Update all items whose values match the old x-default value.
                    Debug.Assert(haveXDefault && xdItem == itemNode);
                    foreach (XmpNode currItem in arrayNode.Children)
                    {
                        if (currItem == xdItem || !currItem.Value.Equals(xdItem != null ? xdItem.Value : null))
                        {
                            continue;
                        }
                        currItem.Value = itemValue;
                    }
                    // And finally do the x-default item.
                    if (xdItem != null)
                    {
                        xdItem.Value = itemValue;
                    }
                }
                break;

            case XmpNodeUtils.CLT_SINGLE_GENERIC:

                // Update the generic item, update x-default if it matches the old
                // value.
                if (haveXDefault && xdItem != itemNode && xdItem != null && xdItem.Value.Equals(itemNode.Value))
                {
                    xdItem.Value = itemValue;
                }
                itemNode.Value = itemValue;     // ! Do this after
                // the x-default
                // check!
                break;

            case XmpNodeUtils.CLT_MULTIPLE_GENERIC:

                // Create the specific language, ignore x-default.
                XmpNodeUtils.AppendLangItem(arrayNode, specificLang, itemValue);
                if (specificXDefault)
                {
                    haveXDefault = true;
                }
                break;

            case XmpNodeUtils.CLT_XDEFAULT:

                // Create the specific language, update x-default if it was the only
                // item.
                if (xdItem != null && arrayNode.ChildrenLength == 1)
                {
                    xdItem.Value = itemValue;
                }
                XmpNodeUtils.AppendLangItem(arrayNode, specificLang, itemValue);
                break;

            case XmpNodeUtils.CLT_FIRST_ITEM:

                // Create the specific language, don't add an x-default item.
                XmpNodeUtils.AppendLangItem(arrayNode, specificLang, itemValue);
                if (specificXDefault)
                {
                    haveXDefault = true;
                }
                break;

            default:
                // does not happen under normal circumstances
                throw new XmpException("Unexpected result from ChooseLocalizedText",
                                       XmpError.INTERNALFAILURE);
            }

            // Add an x-default at the front if needed.
            if (!haveXDefault && arrayNode.ChildrenLength == 1)
            {
                XmpNodeUtils.AppendLangItem(arrayNode, X_DEFAULT, itemValue);
            }
        }
Esempio n. 7
0
        /// <seealso cref= XMPUtilsImpl#appendProperties(XMPMeta, XMPMeta, boolean, boolean, boolean) </seealso>
        /// <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"> </exception>
        private static void AppendSubtree(XmpMetaImpl destXmp, XmpNode sourceNode, XmpNode destParent,
                                          bool replaceOldValues, bool deleteEmptyValues)
        {
            XmpNode destNode = XmpNodeUtils.FindChildNode(destParent, sourceNode.Name, false);

            bool valueIsEmpty = false;

            if (deleteEmptyValues)
            {
                valueIsEmpty = sourceNode.Options.Simple
                                   ? 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.

                PropertyOptions sourceForm = sourceNode.Options;
                PropertyOptions destForm   = destNode.Options;
                if (sourceForm != destForm)
                {
                    return;
                }
                if (sourceForm.Struct)
                {
                    // 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 (IEnumerator it = sourceNode.IterateChildren(); it.MoveNext();)
                    {
                        XmpNode sourceField = (XmpNode)it.Current;
                        if (sourceField == null)
                        {
                            continue;
                        }
                        AppendSubtree(destXmp, sourceField, destNode, replaceOldValues, deleteEmptyValues);
                        if (deleteEmptyValues && !destNode.HasChildren())
                        {
                            destParent.RemoveChild(destNode);
                        }
                    }
                }
                else if (sourceForm.ArrayAltText)
                {
                    // 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 (IEnumerator it = sourceNode.IterateChildren(); it.MoveNext();)
                    {
                        XmpNode sourceItem = (XmpNode)it.Current;
                        if (sourceItem == null)
                        {
                            continue;
                        }
                        if (!sourceItem.HasQualifier() || !XML_LANG.Equals(sourceItem.GetQualifier(1).Name))
                        {
                            continue;
                        }

                        int 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 (!X_DEFAULT.Equals(sourceItem.GetQualifier(1).Value) || !destNode.HasChildren())
                            {
                                sourceItem.CloneSubtree(destNode);
                            }
                            else
                            {
                                XmpNode destItem = new XmpNode(sourceItem.Name, sourceItem.Value, sourceItem.Options);
                                sourceItem.CloneSubtree(destItem);
                                destNode.AddChild(1, destItem);
                            }
                        }
                    }
                }
                else if (sourceForm.Array)
                {
                    // 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 (IEnumerator @is = sourceNode.IterateChildren(); @is.MoveNext();)
                    {
                        XmpNode sourceItem = (XmpNode)@is.Current;
                        if (sourceItem == null)
                        {
                            continue;
                        }
                        bool match = false;
                        for (IEnumerator id = destNode.IterateChildren(); id.MoveNext();)
                        {
                            XmpNode destItem = (XmpNode)id.Current;
                            if (destItem == null)
                            {
                                continue;
                            }
                            if (ItemValuesMatch(sourceItem, destItem))
                            {
                                match = true;
                            }
                        }
                        if (!match)
                        {
                            destNode = (XmpNode)sourceItem.Clone();
                            destParent.AddChild(destNode);
                        }
                    }
                }
            }
        }