public virtual XMPProperty GetLocalizedText(String schemaNS, String altTextName, String genericLang, String specificLang) { ParameterAsserts.AssertSchemaNS(schemaNS); ParameterAsserts.AssertArrayName(altTextName); ParameterAsserts.AssertSpecificLang(specificLang); genericLang = genericLang != null ? iText.Kernel.XMP.Impl.Utils.NormalizeLangValue (genericLang) : null; specificLang = iText.Kernel.XMP.Impl.Utils.NormalizeLangValue(specificLang); XMPPath arrayPath = XMPPathParser.ExpandXPath(schemaNS, altTextName); XMPNode arrayNode = XMPNodeUtils.FindNode(tree, arrayPath, false, null); if (arrayNode == null) { return null; } Object[] result = XMPNodeUtils.ChooseLocalizedText(arrayNode, genericLang, specificLang ); int match = (int)result[0]; XMPNode itemNode = (XMPNode)result[1]; if (match != XMPNodeUtils.CLT_NO_VALUES) { return new _XMPProperty_428(itemNode); } else { return null; } }
/// <summary>Fixes the GPS Timestamp in EXIF.</summary> /// <param name="exifSchema">the EXIF schema node</param> /// <exception cref="iText.Kernel.XMP.XMPException">Thrown if the date conversion fails. /// </exception> private static void FixGPSTimeStamp(XMPNode exifSchema) { // Note: if dates are not found the convert-methods throws an exceptions, // and this methods returns. XMPNode gpsDateTime = XMPNodeUtils.FindChildNode(exifSchema, "exif:GPSTimeStamp", false); if (gpsDateTime == null) { return; } try { XMPDateTime binGpsStamp = XMPUtils.ConvertToDate(gpsDateTime.GetValue()); if (binGpsStamp.GetYear() != 0 || binGpsStamp.GetMonth() != 0 || binGpsStamp.GetDay() != 0) { return; } XMPNode otherDate = XMPNodeUtils.FindChildNode(exifSchema, "exif:DateTimeOriginal", false); otherDate = otherDate ?? XMPNodeUtils.FindChildNode(exifSchema, "exif:DateTimeDigitized", false); XMPDateTime binOtherDate = XMPUtils.ConvertToDate(otherDate.GetValue()); XMPCalendar cal = binGpsStamp.GetCalendar(); DateTime dt = new DateTime(binOtherDate.GetYear(), binOtherDate.GetMonth(), binOtherDate.GetDay(), cal.GetDateTime().Hour, cal.GetDateTime().Minute, cal.GetDateTime().Second, cal.GetDateTime().Millisecond); cal.SetDateTime(dt); binGpsStamp = new XMPDateTimeImpl(cal); gpsDateTime.SetValue(XMPUtils.ConvertFromDate(binGpsStamp)); } catch (XMPException) { } }
/// <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="iText.Kernel.XMP.XMPException">Thrown if tweaking fails.</exception> private static void TweakOldXMP(XMPNode tree) { if (tree.GetName() != null && tree.GetName().Length >= Utils.UUID_LENGTH) { String nameStr = tree.GetName().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 XMPPath path = XMPPathParser.ExpandXPath(XMPConst.NS_XMP_MM, "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", XMPError.INTERNALFAILURE ); } } } }
// ------------------------------------------------------------------------------------- // private /// <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="iText.Kernel.XMP.XMPException">array item cannot be set</exception> private void DoSetArrayItem(XMPNode arrayNode, int itemIndex, String itemValue, PropertyOptions itemOptions, bool insert) { XMPNode itemNode = new XMPNode(ARRAY_ITEM_NAME, 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. int maxIndex = insert ? arrayNode.GetChildrenLength() + 1 : arrayNode.GetChildrenLength (); if (itemIndex == ARRAY_LAST_ITEM) { 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", XMPError.BADINDEX); } }
/// <summary>Constructor with optionsl initial values.</summary> /// <remarks> /// Constructor with optionsl initial values. If <code>propName</code> is provided, /// <code>schemaNS</code> has also be provided. /// </remarks> /// <param name="xmp">the iterated metadata object.</param> /// <param name="schemaNS">the iteration is reduced to this schema (optional)</param> /// <param name="propPath">the iteration is redurce to this property within the <code>schemaNS</code> /// </param> /// <param name="options"> /// advanced iteration options, see /// <see cref="iText.Kernel.XMP.Options.IteratorOptions"/> /// </param> /// <exception cref="iText.Kernel.XMP.XMPException">If the node defined by the paramters is not existing. /// </exception> public XMPIteratorImpl(XMPMetaImpl xmp, String schemaNS, String propPath, IteratorOptions options) { // make sure that options is defined at least with defaults this.options = options ?? new IteratorOptions(); // the start node of the iteration depending on the schema and property filter XMPNode startNode; string initialPath = null; bool baseSchema = !String.IsNullOrEmpty(schemaNS); bool baseProperty = !String.IsNullOrEmpty(propPath); if (!baseSchema && !baseProperty) { // complete tree will be iterated startNode = xmp.GetRoot(); } else if (baseSchema && baseProperty) { // Schema and property node provided XMPPath path = XMPPathParser.ExpandXPath(schemaNS, propPath); // base path is the prop path without the property leaf XMPPath basePath = new XMPPath(); for (int i = 0; i < path.Size() - 1; i++) { basePath.Add(path.GetSegment(i)); } startNode = XMPNodeUtils.FindNode(xmp.GetRoot(), path, false, null); this.baseNS = schemaNS; initialPath = basePath.ToString(); } else if (baseSchema && !baseProperty) { // Only Schema provided startNode = XMPNodeUtils.FindSchemaNode(xmp.GetRoot(), schemaNS, false); } else // !baseSchema && baseProperty { // No schema but property provided -> error throw new XMPException("Schema namespace URI is required", XMPError.BADSCHEMA); } // create iterator if (startNode != null) { this.nodeIterator = (!this.options.IsJustChildren()) ? new NodeIterator(this, startNode, initialPath, 1) : new NodeIteratorChildren(this, startNode, initialPath); } else { // create null iterator this.nodeIterator = EmptyList.GetEnumerator(); } }
/// <summary> /// The initial support for WAV files mapped a legacy ID3 audio copyright /// into a new xmpDM:copyright property. /// </summary> /// <remarks> /// The initial support for WAV files mapped a legacy ID3 audio copyright /// into a new xmpDM:copyright property. This is special case code to migrate /// that into dc:rights['x-default']. The rules: /// <pre> /// 1. If there is no dc:rights array, or an empty array - /// Create one with dc:rights['x-default'] set from double linefeed and xmpDM:copyright. /// 2. If there is a dc:rights array but it has no x-default item - /// Create an x-default item as a copy of the first item then apply rule #3. /// 3. If there is a dc:rights array with an x-default item, /// Look for a double linefeed in the value. /// A. If no double linefeed, compare the x-default value to the xmpDM:copyright value. /// A1. If they match then leave the x-default value alone. /// A2. Otherwise, append a double linefeed and /// the xmpDM:copyright value to the x-default value. /// B. If there is a double linefeed, compare the trailing text to the xmpDM:copyright value. /// B1. If they match then leave the x-default value alone. /// B2. Otherwise, replace the trailing x-default text with the xmpDM:copyright value. /// 4. In all cases, delete the xmpDM:copyright property. /// </pre> /// </remarks> /// <param name="xmp">the metadata object</param> /// <param name="dmCopyright">the "dm:copyright"-property</param> private static void MigrateAudioCopyright(XMPMeta xmp, XMPNode dmCopyright) { try { XMPNode dcSchema = XMPNodeUtils.FindSchemaNode(((XMPMetaImpl)xmp).GetRoot(), XMPConst .NS_DC, true); String dmValue = dmCopyright.GetValue(); String doubleLF = "\n\n"; XMPNode dcRightsArray = XMPNodeUtils.FindChildNode(dcSchema, "dc:rights", false); if (dcRightsArray == null || !dcRightsArray.HasChildren()) { // 1. No dc:rights array, create from double linefeed and xmpDM:copyright. dmValue = doubleLF + dmValue; xmp.SetLocalizedText(XMPConst.NS_DC, "rights", "", XMPConst.X_DEFAULT, dmValue, null ); } else { int xdIndex = XMPNodeUtils.LookupLanguageItem(dcRightsArray, XMPConst.X_DEFAULT); if (xdIndex < 0) { // 2. No x-default item, create from the first item. String firstValue = dcRightsArray.GetChild(1).GetValue(); xmp.SetLocalizedText(XMPConst.NS_DC, "rights", "", XMPConst.X_DEFAULT, firstValue , null); xdIndex = XMPNodeUtils.LookupLanguageItem(dcRightsArray, XMPConst.X_DEFAULT); } // 3. Look for a double linefeed in the x-default value. XMPNode defaultNode = dcRightsArray.GetChild(xdIndex); String defaultValue = defaultNode.GetValue(); int lfPos = defaultValue.IndexOf(doubleLF); if (lfPos < 0) { // 3A. No double LF, compare whole values. if (!dmValue.Equals(defaultValue)) { // 3A2. Append the xmpDM:copyright to the x-default // item. defaultNode.SetValue(defaultValue + doubleLF + dmValue); } } else { // 3B. Has double LF, compare the tail. if (!defaultValue.Substring(lfPos + 2).Equals(dmValue)) { // 3B2. Replace the x-default tail. defaultNode.SetValue(defaultValue.JSubstring(0, lfPos + 2) + dmValue); } } } // 4. Get rid of the xmpDM:copyright. dmCopyright.GetParent().RemoveChild(dmCopyright); } catch (XMPException) { } }
public virtual void AppendArrayItem(String schemaNS, String arrayName, PropertyOptions arrayOptions, String itemValue, PropertyOptions itemOptions) { ParameterAsserts.AssertSchemaNS(schemaNS); ParameterAsserts.AssertArrayName(arrayName); if (arrayOptions == null) { arrayOptions = new PropertyOptions(); } if (!arrayOptions.IsOnlyArrayOptions()) { throw new XMPException("Only array form flags allowed for arrayOptions", XMPError .BADOPTIONS); } // Check if array options are set correctly. arrayOptions = XMPNodeUtils.VerifySetOptions(arrayOptions, null); // Locate or create the array. If it already exists, make sure the array // form from the options // parameter is compatible with the current state. XMPPath arrayPath = XMPPathParser.ExpandXPath(schemaNS, arrayName); // Just lookup, don't try to create. XMPNode arrayNode = XMPNodeUtils.FindNode(tree, arrayPath, false, null); if (arrayNode != null) { // The array exists, make sure the form is compatible. Zero // arrayForm means take what exists. if (!arrayNode.GetOptions().IsArray()) { throw new XMPException("The named property is not an array", XMPError.BADXPATH); } } else { // if (arrayOptions != null && !arrayOptions.equalArrayTypes(arrayNode.getOptions())) // { // throw new XMPException("Mismatch of existing and specified array form", BADOPTIONS); // } // The array does not exist, try to create it. if (arrayOptions.IsArray()) { arrayNode = XMPNodeUtils.FindNode(tree, arrayPath, true, arrayOptions); if (arrayNode == null) { throw new XMPException("Failure creating array node", XMPError.BADXPATH); } } else { // array options missing throw new XMPException("Explicit arrayOptions required to create new array", XMPError .BADOPTIONS); } } DoSetArrayItem(arrayNode, ARRAY_LAST_ITEM, itemValue, itemOptions, true); }
/// <summary>Visit all schemas to do general fixes and handle special cases.</summary> /// <param name="xmp">the metadata object implementation</param> /// <exception cref="iText.Kernel.XMP.XMPException">Thrown if the normalisation fails. /// </exception> private static void TouchUpDataModel(XMPMetaImpl xmp) { // make sure the DC schema is existing, because it might be needed within the normalization // if not touched it will be removed by removeEmptySchemas XMPNodeUtils.FindSchemaNode(xmp.GetRoot(), XMPConst.NS_DC, true); // Do the special case fixes within each schema. for (IEnumerator it = xmp.GetRoot().IterateChildren(); it.MoveNext();) { XMPNode currSchema = (XMPNode)it.Current; if (XMPConst.NS_DC.Equals(currSchema.GetName())) { NormalizeDCArrays(currSchema); } else { if (XMPConst.NS_EXIF.Equals(currSchema.GetName())) { // Do a special case fix for exif:GPSTimeStamp. FixGPSTimeStamp(currSchema); XMPNode arrayNode = XMPNodeUtils.FindChildNode(currSchema, "exif:UserComment", false ); if (arrayNode != null) { RepairAltText(arrayNode); } } else { if (XMPConst.NS_DM.Equals(currSchema.GetName())) { // Do a special case migration of xmpDM:copyright to // dc:rights['x-default']. XMPNode dmCopyright = XMPNodeUtils.FindChildNode(currSchema, "xmpDM:copyright", false ); if (dmCopyright != null) { MigrateAudioCopyright(xmp, dmCopyright); } } else { if (XMPConst.NS_XMP_RIGHTS.Equals(currSchema.GetName())) { XMPNode arrayNode = XMPNodeUtils.FindChildNode(currSchema, "xmpRights:UsageTerms" , false); if (arrayNode != null) { RepairAltText(arrayNode); } } } } } } }
/// <summary>Associates an alias name with an actual name.</summary> /// <remarks> /// Associates an alias name with an actual name. /// <para> /// Define a alias mapping from one namespace/property to another. Both /// property names must be simple names. An alias can be a direct mapping, /// where the alias and actual have the same data type. It is also possible /// to map a simple alias to an item in an array. This can either be to the /// first item in the array, or to the 'x-default' item in an alt-text array. /// Multiple alias names may map to the same actual, as long as the forms /// match. It is a no-op to reregister an alias in an identical fashion. /// Note: This method is not locking because only called by registerStandardAliases /// which is only called by the constructor. /// Note2: The method is only package-private so that it can be tested with unittests /// </para> /// </remarks> /// <param name="aliasNS"> /// The namespace URI for the alias. Must not be null or the empty /// string. /// </param> /// <param name="aliasProp"> /// The name of the alias. Must be a simple name, not null or the /// empty string and not a general path expression. /// </param> /// <param name="actualNS"> /// The namespace URI for the actual. Must not be null or the /// empty string. /// </param> /// <param name="actualProp"> /// The name of the actual. Must be a simple name, not null or the /// empty string and not a general path expression. /// </param> /// <param name="aliasForm"> /// Provides options for aliases for simple aliases to array /// items. This is needed to know what kind of array to create if /// set for the first time via the simple alias. Pass /// <code>XMP_NoOptions</code>, the default value, for all /// direct aliases regardless of whether the actual data type is /// an array or not (see /// <see cref="iText.Kernel.XMP.Options.AliasOptions"/> /// ). /// </param> /// <exception cref="iText.Kernel.XMP.XMPException">for inconsistant aliases.</exception> internal void RegisterAlias(String aliasNS, String aliasProp, String actualNS, String actualProp, AliasOptions aliasForm) { lock (this) { ParameterAsserts.AssertSchemaNS(aliasNS); ParameterAsserts.AssertPropName(aliasProp); ParameterAsserts.AssertSchemaNS(actualNS); ParameterAsserts.AssertPropName(actualProp); // Fix the alias options AliasOptions aliasOpts = aliasForm != null ? new AliasOptions(XMPNodeUtils.VerifySetOptions (aliasForm.ToPropertyOptions(), null).GetOptions()) : new AliasOptions(); if (_regex.IsMatch(aliasProp) || _regex.IsMatch(actualProp)) { throw new XMPException("Alias and actual property names must be simple", XMPError.BADXPATH); } // check if both namespaces are registered String aliasPrefix = GetNamespacePrefix(aliasNS); String actualPrefix = GetNamespacePrefix(actualNS); if (aliasPrefix == null) { throw new XMPException("Alias namespace is not registered", XMPError.BADSCHEMA); } else { if (actualPrefix == null) { throw new XMPException("Actual namespace is not registered", XMPError.BADSCHEMA); } } String key = aliasPrefix + aliasProp; // check if alias is already existing if (aliasMap.Contains(key)) { throw new XMPException("Alias is already existing", XMPError.BADPARAM); } else { if (aliasMap.Contains(actualPrefix + actualProp)) { throw new XMPException("Actual property is already an alias, use the base property" , XMPError.BADPARAM); } } XMPAliasInfo aliasInfo = new _XMPAliasInfo_409(actualNS, actualPrefix, actualProp , aliasOpts); aliasMap[key] = aliasInfo; } }
public virtual bool DoesPropertyExist(String schemaNS, String propName) { try { ParameterAsserts.AssertSchemaNS(schemaNS); ParameterAsserts.AssertPropName(propName); XMPPath expPath = XMPPathParser.ExpandXPath(schemaNS, propName); XMPNode propNode = XMPNodeUtils.FindNode(tree, expPath, false, null); return propNode != null; } catch (XMPException) { return false; } }
public virtual void SetProperty(String schemaNS, String propName, Object propValue , PropertyOptions options) { ParameterAsserts.AssertSchemaNS(schemaNS); ParameterAsserts.AssertPropName(propName); options = XMPNodeUtils.VerifySetOptions(options, propValue); XMPPath expPath = XMPPathParser.ExpandXPath(schemaNS, propName); XMPNode propNode = XMPNodeUtils.FindNode(tree, expPath, true, options); if (propNode != null) { SetNode(propNode, propValue, options, false); } else { throw new XMPException("Specified property does not exist", XMPError.BADXPATH); } }
public virtual void InsertArrayItem(String schemaNS, String arrayName, int itemIndex , String itemValue, PropertyOptions options) { ParameterAsserts.AssertSchemaNS(schemaNS); ParameterAsserts.AssertArrayName(arrayName); // Just lookup, don't try to create. XMPPath arrayPath = XMPPathParser.ExpandXPath(schemaNS, arrayName); XMPNode arrayNode = XMPNodeUtils.FindNode(tree, arrayPath, false, null); if (arrayNode != null) { DoSetArrayItem(arrayNode, itemIndex, itemValue, options, true); } else { throw new XMPException("Specified array does not exist", XMPError.BADXPATH); } }
public virtual void DeleteProperty(String schemaNS, String propName) { try { ParameterAsserts.AssertSchemaNS(schemaNS); ParameterAsserts.AssertPropName(propName); XMPPath expPath = XMPPathParser.ExpandXPath(schemaNS, propName); XMPNode propNode = XMPNodeUtils.FindNode(tree, expPath, false, null); if (propNode != null) { XMPNodeUtils.DeleteNode(propNode); } } catch (XMPException) { // EMPTY, exceptions are ignored within delete } }
public virtual int CountArrayItems(String schemaNS, String arrayName) { ParameterAsserts.AssertSchemaNS(schemaNS); ParameterAsserts.AssertArrayName(arrayName); XMPPath arrayPath = XMPPathParser.ExpandXPath(schemaNS, arrayName); XMPNode arrayNode = XMPNodeUtils.FindNode(tree, arrayPath, false, null); if (arrayNode == null) { return 0; } if (arrayNode.GetOptions().IsArray()) { return arrayNode.GetChildrenLength(); } else { throw new XMPException("The named property is not an array", XMPError.BADXPATH); } }
/// <summary>Returns a property, but the result value can be requested.</summary> /// <seealso cref="iText.Kernel.XMP.XMPMeta.GetProperty(System.String, System.String) /// "/> /// <param name="schemaNS">a schema namespace</param> /// <param name="propName">a property name or path</param> /// <param name="valueType">the type of the value, see VALUE_...</param> /// <returns> /// Returns the node value as an object according to the /// <code>valueType</code>. /// </returns> /// <exception cref="iText.Kernel.XMP.XMPException">Collects any exception that occurs.</exception> protected internal virtual Object GetPropertyObject(String schemaNS, String propName , int valueType) { ParameterAsserts.AssertSchemaNS(schemaNS); ParameterAsserts.AssertPropName(propName); XMPPath expPath = XMPPathParser.ExpandXPath(schemaNS, propName); XMPNode propNode = XMPNodeUtils.FindNode(tree, expPath, false, null); if (propNode != null) { if (valueType != VALUE_STRING && propNode.GetOptions().IsCompositeProperty()) { throw new XMPException("Property must be simple when a value type is requested", XMPError.BADXPATH); } return EvaluateNodeValue(valueType, propNode); } else { return null; } }
/// <summary> /// Searches for a qualifier selector in a node: /// [?qualName="value"] - an element in an array, chosen by a qualifier value. /// </summary> /// <remarks> /// Searches for a qualifier selector in a node: /// [?qualName="value"] - an element in an array, chosen by a qualifier value. /// No implicit nodes are created for qualifier selectors, /// except for an alias to an x-default item. /// </remarks> /// <param name="arrayNode">an array node</param> /// <param name="qualName">the qualifier name</param> /// <param name="qualValue">the qualifier value</param> /// <param name="aliasForm"> /// in case the qual selector results from an alias, /// an x-default node is created if there has not been one. /// </param> /// <returns>Returns the index of th</returns> /// <exception cref="iText.Kernel.XMP.XMPException"></exception> private static int LookupQualSelector(XMPNode arrayNode, String qualName, String qualValue, int aliasForm) { if (XML_LANG.Equals(qualName)) { qualValue = iText.Kernel.XMP.Impl.Utils.NormalizeLangValue(qualValue); int index = XMPNodeUtils.LookupLanguageItem(arrayNode, qualValue); if (index < 0 && (aliasForm & AliasOptions.PROP_ARRAY_ALT_TEXT) > 0) { XMPNode langNode = new XMPNode(ARRAY_ITEM_NAME, null); XMPNode xdefault = new XMPNode(XML_LANG, X_DEFAULT, null); langNode.AddQualifier(xdefault); arrayNode.AddChild(1, langNode); return(1); } else { return(index); } } else { for (int index = 1; index < arrayNode.GetChildrenLength(); index++) { XMPNode currItem = arrayNode.GetChild(index); for (IEnumerator it = currItem.IterateQualifier(); it.MoveNext();) { XMPNode qualifier = (XMPNode)it.Current; if (qualName.Equals(qualifier.GetName()) && qualValue.Equals(qualifier.GetValue() )) { return(index); } } } return(-1); } }
/// <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 <code>null</code></param> /// <param name="newOptions">options for the new node, must not be <code>null</code>. /// </param> /// <param name="deleteExisting">flag if the existing value is to be overwritten</param> /// <exception cref="iText.Kernel.XMP.XMPException">thrown if options and value do not correspond</exception> internal virtual 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.GetOptions().MergeWith(newOptions); if (!node.GetOptions().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", XMPError.BADXPATH); } node.RemoveChildren(); } }
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 ? iText.Kernel.XMP.Impl.Utils.NormalizeLangValue (genericLang) : null; specificLang = iText.Kernel.XMP.Impl.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); } else { if (!arrayNode.GetOptions().IsArrayAltText()) { if (!arrayNode.HasChildren() && arrayNode.GetOptions().IsArrayAlternate()) { arrayNode.GetOptions().SetArrayAltText(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.GetChildren()) { if (!currItem.HasQualifier() || !XMPConst.XML_LANG.Equals(currItem.GetQualifier(1 ).GetName())) { throw new XMPException("Language qualifier must be first", XMPError.BADXPATH); } else { if (XMPConst.X_DEFAULT.Equals(currItem.GetQualifier(1).GetValue())) { xdItem = currItem; haveXDefault = true; break; } } } // Moves x-default to the beginning of the array if (xdItem != null && arrayNode.GetChildrenLength() > 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)result[0]; XMPNode itemNode = (XMPNode)result[1]; bool specificXDefault = XMPConst.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, XMPConst.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.GetValue().Equals (itemNode.GetValue())) { xdItem.SetValue(itemValue); } // ! Do this after the x-default check! itemNode.SetValue(itemValue); } else { // Update all items whose values match the old x-default value. System.Diagnostics.Debug.Assert(haveXDefault && xdItem == itemNode); for (IEnumerator it_1 = arrayNode.IterateChildren(); it_1.MoveNext(); ) { XMPNode currItem = (XMPNode)it_1.Current; if (currItem == xdItem || !currItem.GetValue().Equals(xdItem != null ? xdItem.GetValue () : null)) { continue; } currItem.SetValue(itemValue); } // And finally do the x-default item. if (xdItem != null) { xdItem.SetValue(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.GetValue().Equals (itemNode.GetValue())) { xdItem.SetValue(itemValue); } itemNode.SetValue(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.GetChildrenLength() == 1) { xdItem.SetValue(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.GetChildrenLength() == 1) { XMPNodeUtils.AppendLangItem(arrayNode, XMPConst.X_DEFAULT, itemValue); } }
/// <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="iText.Kernel.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(); IEnumerator schemaIt = tree.GetUnmodifiableChildren().GetEnumerator(); while (schemaIt.MoveNext()) { XMPNode currSchema = (XMPNode)schemaIt.Current; if (currSchema == null) { continue; } if (!currSchema.GetHasAliases()) { continue; } List <XMPNode> currPropsToRemove = new List <XMPNode>(); IEnumerator propertyIt = currSchema.IterateChildren(); while (propertyIt.MoveNext()) { XMPNode currProp = (XMPNode)propertyIt.Current; if (currProp == null) { continue; } 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); } 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(currProp, baseNode); } currPropsToRemove.Add(currProp); } 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); } currPropsToRemove.Add(currProp); } 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, XMPConst.X_DEFAULT); if (xdIndex != -1) { itemNode = baseNode.GetChild(xdIndex); } } else if (baseNode.HasChildren()) { itemNode = baseNode.GetChild(1); } if (itemNode == null) { TransplantArrayItemAlias(currProp, baseNode); } else { if (strictAliasing) { CompareAliasedSubtrees(currProp, itemNode, true); } } currPropsToRemove.Add(currProp); } } } foreach (XMPNode o in currPropsToRemove) { currSchema.RemoveChild(o); } currPropsToRemove.Clear(); currSchema.SetHasAliases(false); } }