// EMPTY /// <seealso cref="Com.Adobe.Xmp.XMPUtils.CatenateArrayItems(Com.Adobe.Xmp.XMPMeta, string, string, string, string, bool)"/> /// <param name="xmp">The XMP object containing the array to be catenated.</param> /// <param name="schemaNS"> /// The schema namespace URI for the array. Must not be null or /// the empty string. /// </param> /// <param name="arrayName"> /// The name of the array. May be a general path expression, must /// not be null or the empty string. Each item in the array must /// be a simple string value. /// </param> /// <param name="separator"> /// The string to be used to separate the items in the catenated /// string. Defaults to "; ", ASCII semicolon and space /// (U+003B, U+0020). /// </param> /// <param name="quotes"> /// The characters to be used as quotes around array items that /// contain a separator. Defaults to '"' /// </param> /// <param name="allowCommas">Option flag to control the catenation.</param> /// <returns>Returns the string containing the catenated array items.</returns> /// <exception cref="Com.Adobe.Xmp.XMPException">Forwards the Exceptions from the metadata processing</exception> public static string CatenateArrayItems(XMPMeta xmp, string schemaNS, string arrayName, string separator, string quotes, bool allowCommas) { ParameterAsserts.AssertSchemaNS(schemaNS); ParameterAsserts.AssertArrayName(arrayName); ParameterAsserts.AssertImplementation(xmp); if (separator == null || separator.Length == 0) { separator = "; "; } if (quotes == null || quotes.Length == 0) { quotes = "\""; } XMPMetaImpl xmpImpl = (XMPMetaImpl)xmp; XMPNode arrayNode = null; XMPNode currItem = null; // Return an empty result if the array does not exist, // hurl if it isn't the right form. XMPPath arrayPath = XMPPathParser.ExpandXPath(schemaNS, arrayName); arrayNode = XMPNodeUtils.FindNode(xmpImpl.GetRoot(), arrayPath, false, null); if (arrayNode == null) { return string.Empty; } else { if (!arrayNode.GetOptions().IsArray() || arrayNode.GetOptions().IsArrayAlternate()) { throw new XMPException("Named property must be non-alternate array", XMPErrorConstants.Badparam); } } // Make sure the separator is OK. CheckSeparator(separator); // Make sure the open and close quotes are a legitimate pair. char openQuote = quotes[0]; char closeQuote = CheckQuotes(quotes, openQuote); // Build the result, quoting the array items, adding separators. // Hurl if any item isn't simple. StringBuilder catinatedString = new StringBuilder(); for (Iterator it = arrayNode.IterateChildren(); it.HasNext(); ) { currItem = (XMPNode)it.Next(); if (currItem.GetOptions().IsCompositeProperty()) { throw new XMPException("Array items must be simple", XMPErrorConstants.Badparam); } string str = ApplyQuotes(currItem.GetValue(), openQuote, closeQuote, allowCommas); catinatedString.Append(str); if (it.HasNext()) { catinatedString.Append(separator); } } return catinatedString.ToString(); }
public virtual void XxeTestFromString() { String metadataToParse = MessageFormatUtil.Format(XMP_WITH_XXE, XXE_FILE_PATH); XMPMeta xmpMeta = XMPMetaParser.Parse(metadataToParse, null); NUnit.Framework.Assert.AreEqual(EXPECTED_SERIALIZED_XMP, XMPMetaFactory.SerializeToString(xmpMeta, null)); }
public virtual void XxeTestFromByteBuffer() { String metadataToParse = MessageFormatUtil.Format(XMP_WITH_XXE, XXE_FILE_PATH); XMPMeta xmpMeta = XMPMetaParser.Parse(metadataToParse.GetBytes(System.Text.Encoding.UTF8), null); NUnit.Framework.Assert.AreEqual(EXPECTED_SERIALIZED_XMP, XMPMetaFactory.SerializeToString(xmpMeta, null)); }
/// <exception cref="iText.Kernel.XMP.XMPException"/> /// <exception cref="System.IO.IOException"/> public virtual void SetXmpMetadata(XMPMeta xmpMeta) { SerializeOptions serializeOptions = new SerializeOptions(); serializeOptions.SetPadding(2000); SetXmpMetadata(xmpMeta, serializeOptions); }
public virtual void CreatePdfTest() { String fileName = "xmp_metadata.pdf"; // step 1 PdfDocument pdfDocument = new PdfDocument(new PdfWriter(destinationFolder + "xmp_metadata.pdf")); Document document = new Document(pdfDocument); // step 2 ByteArrayOutputStream os = new ByteArrayOutputStream(); XMPMeta xmp = XMPMetaFactory.Create(); xmp.AppendArrayItem(XMPConst.NS_DC, "subject", new PropertyOptions(PropertyOptions.ARRAY), "Hello World", null); xmp.AppendArrayItem(XMPConst.NS_DC, "subject", new PropertyOptions(PropertyOptions.ARRAY), "XMP & Metadata" , null); xmp.AppendArrayItem(XMPConst.NS_DC, "subject", new PropertyOptions(PropertyOptions.ARRAY), "Metadata", null ); pdfDocument.SetXmpMetadata(xmp); // step 4 document.Add(new Paragraph("Hello World")); // step 5 document.Close(); CompareTool ct = new CompareTool(); NUnit.Framework.Assert.IsNull(ct.CompareXmp(destinationFolder + fileName, sourceFolder + "cmp_" + fileName , true)); }
/// <seealso cref="Com.Adobe.Xmp.XMPUtils.AppendProperties(Com.Adobe.Xmp.XMPMeta, Com.Adobe.Xmp.XMPMeta, bool, bool)"/> /// <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="Com.Adobe.Xmp.XMPException">Forwards the Exceptions from the metadata processing</exception> public static void AppendProperties(XMPMeta source, XMPMeta destination, bool doAllProperties, bool replaceOldValues, bool deleteEmptyValues) { ParameterAsserts.AssertImplementation(source); ParameterAsserts.AssertImplementation(destination); XMPMetaImpl src = (XMPMetaImpl)source; XMPMetaImpl dest = (XMPMetaImpl)destination; for (Iterator it = src.GetRoot().IterateChildren(); it.HasNext();) { XMPNode sourceSchema = (XMPNode)it.Next(); // Make sure we have a destination schema node XMPNode destSchema = XMPNodeUtils.FindSchemaNode(dest.GetRoot(), sourceSchema.GetName(), false); bool createdSchema = false; if (destSchema == null) { destSchema = new XMPNode(sourceSchema.GetName(), sourceSchema.GetValue(), new PropertyOptions().SetSchemaNode(true)); dest.GetRoot().AddChild(destSchema); createdSchema = true; } // Process the source schema's children. for (Iterator ic = sourceSchema.IterateChildren(); ic.HasNext();) { XMPNode sourceProp = (XMPNode)ic.Next(); if (doAllProperties || !Utils.IsInternalProperty(sourceSchema.GetName(), sourceProp.GetName())) { AppendSubtree(dest, sourceProp, destSchema, replaceOldValues, deleteEmptyValues); } } if (!destSchema.HasChildren() && (createdSchema || deleteEmptyValues)) { // Don't create an empty schema / remove empty schema. dest.GetRoot().RemoveChild(destSchema); } } }
/// <param name="xmp">Asserts that xmp is compatible to <code>XMPMetaImpl</code>.s</param> private static void AssertImplementation(XMPMeta xmp) { if (!(xmp is XMPMetaImpl)) { throw new NotSupportedException("The serializing service works only" + "with the XMPMeta implementation of this library"); } }
public virtual XMPMeta GetXMPMeta() { if (_xmpMeta == null) { _xmpMeta = new XMPMetaImpl(); } return(_xmpMeta); }
/// <exception cref="Com.Adobe.Xmp.XMPException"/> private void ProcessXmpDateTag(XMPMeta meta, XmpDirectory directory, string schemaNS, string propName, int tagType) { Sharpen.Calendar cal = meta.GetPropertyCalendar(schemaNS, propName); if (cal != null) { directory.SetDate(tagType, cal.GetTime()); } }
/// <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) { } }
// EMPTY /// <seealso cref="Com.Adobe.Xmp.XMPUtils.CatenateArrayItems(Com.Adobe.Xmp.XMPMeta, string, string, string, string, bool)"/> /// <param name="xmp">The XMP object containing the array to be catenated.</param> /// <param name="schemaNS"> /// The schema namespace URI for the array. Must not be null or /// the empty string. /// </param> /// <param name="arrayName"> /// The name of the array. May be a general path expression, must /// not be null or the empty string. Each item in the array must /// be a simple string value. /// </param> /// <param name="separator"> /// The string to be used to separate the items in the catenated /// string. Defaults to "; ", ASCII semicolon and space /// (U+003B, U+0020). /// </param> /// <param name="quotes"> /// The characters to be used as quotes around array items that /// contain a separator. Defaults to '"' /// </param> /// <param name="allowCommas">Option flag to control the catenation.</param> /// <returns>Returns the string containing the catenated array items.</returns> /// <exception cref="Com.Adobe.Xmp.XMPException">Forwards the Exceptions from the metadata processing</exception> public static string CatenateArrayItems(XMPMeta xmp, string schemaNS, string arrayName, string separator, string quotes, bool allowCommas) { ParameterAsserts.AssertSchemaNS(schemaNS); ParameterAsserts.AssertArrayName(arrayName); ParameterAsserts.AssertImplementation(xmp); if (separator == null || separator.Length == 0) { separator = "; "; } if (quotes == null || quotes.Length == 0) { quotes = "\""; } XMPMetaImpl xmpImpl = (XMPMetaImpl)xmp; XMPNode arrayNode = null; XMPNode currItem = null; // Return an empty result if the array does not exist, // hurl if it isn't the right form. XMPPath arrayPath = XMPPathParser.ExpandXPath(schemaNS, arrayName); arrayNode = XMPNodeUtils.FindNode(xmpImpl.GetRoot(), arrayPath, false, null); if (arrayNode == null) { return(string.Empty); } else { if (!arrayNode.GetOptions().IsArray() || arrayNode.GetOptions().IsArrayAlternate()) { throw new XMPException("Named property must be non-alternate array", XMPErrorConstants.Badparam); } } // Make sure the separator is OK. CheckSeparator(separator); // Make sure the open and close quotes are a legitimate pair. char openQuote = quotes[0]; char closeQuote = CheckQuotes(quotes, openQuote); // Build the result, quoting the array items, adding separators. // Hurl if any item isn't simple. StringBuilder catinatedString = new StringBuilder(); for (Iterator it = arrayNode.IterateChildren(); it.HasNext();) { currItem = (XMPNode)it.Next(); if (currItem.GetOptions().IsCompositeProperty()) { throw new XMPException("Array items must be simple", XMPErrorConstants.Badparam); } string str = ApplyQuotes(currItem.GetValue(), openQuote, closeQuote, allowCommas); catinatedString.Append(str); if (it.HasNext()) { catinatedString.Append(separator); } } return(catinatedString.ToString()); }
internal static void AppendMetadataToInfo(byte[] xmpMetadata, PdfDocumentInfo info) { if (xmpMetadata != null) { try { XMPMeta meta = XMPMetaFactory.ParseFromBuffer(xmpMetadata); XMPProperty title = meta.GetLocalizedText(XMPConst.NS_DC, PdfConst.Title, XMPConst.X_DEFAULT, XMPConst.X_DEFAULT ); if (title != null) { info.SetTitle(title.GetValue()); } String author = FetchArrayIntoString(meta, XMPConst.NS_DC, PdfConst.Creator); if (author != null) { info.SetAuthor(author); } // We assume that pdf:keywords has precedence over dc:subject XMPProperty keywords = meta.GetProperty(XMPConst.NS_PDF, PdfConst.Keywords); if (keywords != null) { info.SetKeywords(keywords.GetValue()); } else { String keywordsStr = FetchArrayIntoString(meta, XMPConst.NS_DC, PdfConst.Subject); if (keywordsStr != null) { info.SetKeywords(keywordsStr); } } XMPProperty subject = meta.GetLocalizedText(XMPConst.NS_DC, PdfConst.Description, XMPConst.X_DEFAULT, XMPConst .X_DEFAULT); if (subject != null) { info.SetSubject(subject.GetValue()); } XMPProperty creator = meta.GetProperty(XMPConst.NS_XMP, PdfConst.CreatorTool); if (creator != null) { info.SetCreator(creator.GetValue()); } XMPProperty producer = meta.GetProperty(XMPConst.NS_PDF, PdfConst.Producer); if (producer != null) { info.Put(PdfName.Producer, new PdfString(producer.GetValue(), PdfEncodings.UNICODE_BIG)); } XMPProperty trapped = meta.GetProperty(XMPConst.NS_PDF, PdfConst.Trapped); if (trapped != null) { info.SetTrapped(new PdfName(trapped.GetValue())); } } catch (XMPException) { } } }
/// <exception cref="Com.Adobe.Xmp.XMPException"/> private static void ProcessXmpTags(XmpDirectory directory, XMPMeta xmpMeta) { // store the XMPMeta object on the directory in case others wish to use it directory.SetXMPMeta(xmpMeta); // read all the tags and send them to the directory // I've added some popular tags, feel free to add more tags ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagLensInfo, FmtString); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagLens, FmtString); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagCameraSerialNumber, FmtString); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagFirmware, FmtString); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagMake, FmtString); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagModel, FmtString); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagExposureTime, FmtString); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagExposureProgram, FmtInt); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagApertureValue, FmtRational); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagFNumber, FmtRational); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagFocalLength, FmtRational); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagShutterSpeed, FmtRational); ProcessXmpDateTag(xmpMeta, directory, XmpDirectory.TagDatetimeOriginal); ProcessXmpDateTag(xmpMeta, directory, XmpDirectory.TagDatetimeDigitized); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagRating, FmtDouble); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagLabel, FmtString); // this requires further research // processXmpTag(xmpMeta, directory, Schema.DUBLIN_CORE_SPECIFIC_PROPERTIES, "dc:title", XmpDirectory.TAG_TITLE, FMT_STRING); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagSubject, FmtStringArray); // processXmpDateTag(xmpMeta, directory, Schema.DUBLIN_CORE_SPECIFIC_PROPERTIES, "dc:date", XmpDirectory.TAG_DATE); // processXmpTag(xmpMeta, directory, Schema.DUBLIN_CORE_SPECIFIC_PROPERTIES, "dc:type", XmpDirectory.TAG_TYPE, FMT_STRING); // processXmpTag(xmpMeta, directory, Schema.DUBLIN_CORE_SPECIFIC_PROPERTIES, "dc:description", XmpDirectory.TAG_DESCRIPTION, FMT_STRING); // processXmpTag(xmpMeta, directory, Schema.DUBLIN_CORE_SPECIFIC_PROPERTIES, "dc:relation", XmpDirectory.TAG_RELATION, FMT_STRING); // processXmpTag(xmpMeta, directory, Schema.DUBLIN_CORE_SPECIFIC_PROPERTIES, "dc:coverage", XmpDirectory.TAG_COVERAGE, FMT_STRING); // processXmpTag(xmpMeta, directory, Schema.DUBLIN_CORE_SPECIFIC_PROPERTIES, "dc:creator", XmpDirectory.TAG_CREATOR, FMT_STRING); // processXmpTag(xmpMeta, directory, Schema.DUBLIN_CORE_SPECIFIC_PROPERTIES, "dc:publisher", XmpDirectory.TAG_PUBLISHER, FMT_STRING); // processXmpTag(xmpMeta, directory, Schema.DUBLIN_CORE_SPECIFIC_PROPERTIES, "dc:contributor", XmpDirectory.TAG_CONTRIBUTOR, FMT_STRING); // processXmpTag(xmpMeta, directory, Schema.DUBLIN_CORE_SPECIFIC_PROPERTIES, "dc:rights", XmpDirectory.TAG_RIGHTS, FMT_STRING); // processXmpTag(xmpMeta, directory, Schema.DUBLIN_CORE_SPECIFIC_PROPERTIES, "dc:format", XmpDirectory.TAG_FORMAT, FMT_STRING); // processXmpTag(xmpMeta, directory, Schema.DUBLIN_CORE_SPECIFIC_PROPERTIES, "dc:identifier", XmpDirectory.TAG_IDENTIFIER, FMT_STRING); // processXmpTag(xmpMeta, directory, Schema.DUBLIN_CORE_SPECIFIC_PROPERTIES, "dc:language", XmpDirectory.TAG_LANGUAGE, FMT_STRING); // processXmpTag(xmpMeta, directory, Schema.DUBLIN_CORE_SPECIFIC_PROPERTIES, "dc:audience", XmpDirectory.TAG_AUDIENCE, FMT_STRING); // processXmpTag(xmpMeta, directory, Schema.DUBLIN_CORE_SPECIFIC_PROPERTIES, "dc:provenance", XmpDirectory.TAG_PROVENANCE, FMT_STRING); // processXmpTag(xmpMeta, directory, Schema.DUBLIN_CORE_SPECIFIC_PROPERTIES, "dc:rightsHolder", XmpDirectory.TAG_RIGHTS_HOLDER, FMT_STRING); // processXmpTag(xmpMeta, directory, Schema.DUBLIN_CORE_SPECIFIC_PROPERTIES, "dc:instructionalMethod", XmpDirectory.TAG_INSTRUCTIONAL_METHOD, // FMT_STRING); // processXmpTag(xmpMeta, directory, Schema.DUBLIN_CORE_SPECIFIC_PROPERTIES, "dc:accrualMethod", XmpDirectory.TAG_ACCRUAL_METHOD, FMT_STRING); // processXmpTag(xmpMeta, directory, Schema.DUBLIN_CORE_SPECIFIC_PROPERTIES, "dc:accrualPeriodicity", XmpDirectory.TAG_ACCRUAL_PERIODICITY, // FMT_STRING); // processXmpTag(xmpMeta, directory, Schema.DUBLIN_CORE_SPECIFIC_PROPERTIES, "dc:accrualPolicy", XmpDirectory.TAG_ACCRUAL_POLICY, FMT_STRING); for (XMPIterator iterator = xmpMeta.Iterator(); iterator.HasNext();) { XMPPropertyInfo propInfo = (XMPPropertyInfo)iterator.Next(); string path = propInfo.GetPath(); string value = propInfo.GetValue(); if (path != null && value != null) { directory.AddProperty(path, value); } } }
/* (non-Javadoc) * @see com.itextpdf.pdfa.PdfADocument#addCustomMetadataExtensions(com.itextpdf.kernel.xmp.XMPMeta) */ protected override void AddCustomMetadataExtensions(XMPMeta xmpMeta) { base.AddCustomMetadataExtensions(xmpMeta); try { AddZugferdRdfDescription(xmpMeta, zugferdConformanceLevel); } catch (XMPException e) { ILog logger = LogManager.GetLogger(typeof(iText.Zugferd.ZugferdDocument)); logger.Error(iText.IO.LogMessageConstant.EXCEPTION_WHILE_UPDATING_XMPMETADATA, e); } }
private byte[] RemoveAlwaysDifferentEntries(byte[] cmpBytes) { XMPMeta xmpMeta = XMPMetaFactory.ParseFromBuffer(cmpBytes); XMPUtils.RemoveProperties(xmpMeta, XMPConst.NS_XMP, PdfConst.CreateDate, true, true); XMPUtils.RemoveProperties(xmpMeta, XMPConst.NS_XMP, PdfConst.ModifyDate, true, true); XMPUtils.RemoveProperties(xmpMeta, XMPConst.NS_XMP, PdfConst.MetadataDate, true, true); XMPUtils.RemoveProperties(xmpMeta, XMPConst.NS_PDF, PdfConst.Producer, true, true); cmpBytes = XMPMetaFactory.SerializeToBuffer(xmpMeta, new SerializeOptions(SerializeOptions.SORT)); return(cmpBytes); }
/// <exception cref="Com.Adobe.Xmp.XMPException"/> private static void ProcessXmpDateTag([NotNull] XMPMeta meta, [NotNull] XmpDirectory directory, int tagType) { string schemaNS = XmpDirectory._tagSchemaMap.Get(tagType); string propName = XmpDirectory._tagPropNameMap.Get(tagType); Sharpen.Calendar cal = meta.GetPropertyCalendar(schemaNS, propName); if (cal != null) { directory.SetDate(tagType, cal.GetTime()); } }
public virtual void MetadataReadingInEncryptedDoc() { PdfReader reader = new PdfReader(sourceFolder + "encryptedWithPlainMetadata.pdf", new ReaderProperties().SetPassword (OWNER)); PdfDocument doc = new PdfDocument(reader); XMPMeta xmpMeta = XMPMetaFactory.ParseFromBuffer(doc.GetXmpMetadata()); XMPProperty creatorToolXmp = xmpMeta.GetProperty(XMPConst.NS_XMP, "CreatorTool"); doc.Close(); NUnit.Framework.Assert.IsNotNull(creatorToolXmp); NUnit.Framework.Assert.AreEqual("iText 7", creatorToolXmp.GetValue()); }
/// <exception cref="iText.Kernel.XMP.XMPException"/> private static void AppendArrayItemIfDoesNotExist(XMPMeta meta, String ns, String arrayName, String value) { int currentCnt = meta.CountArrayItems(ns, arrayName); for (int i = 0; i < currentCnt; i++) { XMPProperty item = meta.GetArrayItem(ns, arrayName, i + 1); if (value.Equals(item.GetValue())) { return; } } meta.AppendArrayItem(ns, arrayName, new PropertyOptions(PropertyOptions.ARRAY_ORDERED), value, null); }
/// <summary> /// Performs the XMP data extraction, adding found values to the specified instance of /// <see cref="Com.Drew.Metadata.Metadata"/> /// . /// <p/> /// The extraction is done with Adobe's XMPCore library. /// </summary> public virtual void Extract(string xmpString, Com.Drew.Metadata.Metadata metadata) { XmpDirectory directory = metadata.GetOrCreateDirectory <XmpDirectory>(); try { XMPMeta xmpMeta = XMPMetaFactory.ParseFromString(xmpString); ProcessXmpTags(directory, xmpMeta); } catch (XMPException e) { directory.AddError("Error processing XMP data: " + e.Message); } }
/// <summary> /// Asserts that the xmp object is of this implemention /// ( /// <see cref="XMPMetaImpl"/> /// ). /// </summary> /// <param name="xmp">the XMP object</param> /// <exception cref="Com.Adobe.Xmp.XMPException">A wrong implentaion is used.</exception> public static void AssertImplementation(XMPMeta xmp) { if (xmp == null) { throw new XMPException("Parameter must not be null", XMPErrorConstants.Badparam); } else { if (!(xmp is XMPMetaImpl)) { throw new XMPException("The XMPMeta-object is not compatible with this implementation", XMPErrorConstants.Badparam); } } }
protected override void AddCustomMetadataExtensions(XMPMeta xmpMeta) { if (this.IsTagged()) { try { XMPMeta taggedExtensionMeta = XMPMetaFactory.ParseFromString(PdfAXMPUtil.PDF_UA_EXTENSION); XMPUtils.AppendProperties(taggedExtensionMeta, xmpMeta, true, false); } catch (XMPException exc) { ILogger logger = LoggerFactory.GetLogger(typeof(iText.Pdfa.PdfADocument)); logger.Error(LogMessageConstant.EXCEPTION_WHILE_UPDATING_XMPMETADATA, exc); } } }
protected override void UpdateXmpMetadata() { try { XMPMeta xmpMeta = UpdateDefaultXmpMetadata(); xmpMeta.SetProperty(XMPConst.NS_PDFA_ID, XMPConst.PART, checker.GetConformanceLevel().GetPart()); xmpMeta.SetProperty(XMPConst.NS_PDFA_ID, XMPConst.CONFORMANCE, checker.GetConformanceLevel().GetConformance ()); AddCustomMetadataExtensions(xmpMeta); SetXmpMetadata(xmpMeta); } catch (XMPException e) { ILog logger = LogManager.GetLogger(typeof(iText.Pdfa.PdfADocument)); logger.Error(iText.IO.LogMessageConstant.EXCEPTION_WHILE_UPDATING_XMPMETADATA, e); } }
/// <summary> /// Performs the XMP data extraction, adding found values to the specified instance of /// <see cref="Com.Drew.Metadata.Metadata"/> /// . /// <p> /// The extraction is done with Adobe's XMPCore library. /// </summary> public virtual void Extract([NotNull] string xmpString, [NotNull] Com.Drew.Metadata.Metadata metadata) { XmpDirectory directory = new XmpDirectory(); try { XMPMeta xmpMeta = XMPMetaFactory.ParseFromString(xmpString); ProcessXmpTags(directory, xmpMeta); } catch (XMPException e) { directory.AddError("Error processing XMP data: " + e.Message); } if (!directory.IsEmpty()) { metadata.AddDirectory(directory); } }
protected override void UpdateXmpMetadata() { try { XMPMeta xmpMeta = UpdateDefaultXmpMetadata(); xmpMeta.SetProperty(XMPConst.NS_PDFA_ID, XMPConst.PART, checker.GetConformanceLevel().GetPart()); xmpMeta.SetProperty(XMPConst.NS_PDFA_ID, XMPConst.CONFORMANCE, checker.GetConformanceLevel().GetConformance ()); if (this.IsTagged()) { XMPMeta taggedExtensionMeta = XMPMetaFactory.ParseFromString(PdfAXMPUtil.PDF_UA_EXTENSION); XMPUtils.AppendProperties(taggedExtensionMeta, xmpMeta, true, false); } SetXmpMetadata(xmpMeta); } catch (XMPException e) { ILogger logger = LoggerFactory.GetLogger(typeof(iText.Pdfa.PdfADocument)); logger.Error(LogMessageConstant.EXCEPTION_WHILE_UPDATING_XMPMETADATA, e); } }
/// <summary>Adds the ZUGFeRD RDF description.</summary> /// <param name="xmpMeta">the xmp meta</param> /// <param name="zugferdConformanceLevel">the zugferd conformance level</param> /// <exception cref="iText.Kernel.XMP.XMPException">the XMP exception</exception> private void AddZugferdRdfDescription(XMPMeta xmpMeta, ZugferdConformanceLevel zugferdConformanceLevel) { switch (zugferdConformanceLevel) { case ZugferdConformanceLevel.ZUGFeRDBasic: case ZugferdConformanceLevel.ZUGFeRDComfort: case ZugferdConformanceLevel.ZUGFeRDExtended: { // fallthrough // fallthrough XMPMeta taggedExtensionMetaComfort = XMPMetaFactory.ParseFromString(GetZugferdExtension(zugferdConformanceLevel )); XMPUtils.AppendProperties(taggedExtensionMetaComfort, xmpMeta, true, false); break; } default: { break; } } }
public virtual void SetXMPMeta([NotNull] XMPMeta xmpMeta) { _xmpMeta = xmpMeta; try { int valueCount = 0; for (Iterator i = _xmpMeta.Iterator(); i.HasNext();) { XMPPropertyInfo prop = (XMPPropertyInfo)i.Next(); if (prop.GetPath() != null) { //System.out.printf("%s = %s\n", prop.getPath(), prop.getValue()); valueCount++; } } SetInt(TagXmpValueCount, valueCount); } catch (XMPException) { } }
/// <summary>Serializes the XmpDirectory component of <code>Metadata</code> into an <code>OutputStream</code></summary> /// <param name="os">Destination for the xmp data</param> /// <param name="data">populated metadata</param> /// <returns>serialize success</returns> public static bool Write(OutputStream os, Com.Drew.Metadata.Metadata data) { XmpDirectory dir = data.GetFirstDirectoryOfType <XmpDirectory>(); if (dir == null) { return(false); } XMPMeta meta = dir.GetXMPMeta(); try { SerializeOptions so = new SerializeOptions().SetOmitPacketWrapper(true); XMPMetaFactory.Serialize(meta, os, so); } catch (XMPException e) { Sharpen.Runtime.PrintStackTrace(e); return(false); } return(true); }
public static iText.Kernel.Pdf.PdfAConformanceLevel GetConformanceLevel(XMPMeta meta) { XMPProperty conformanceXmpProperty = null; XMPProperty partXmpProperty = null; try { conformanceXmpProperty = meta.GetProperty(XMPConst.NS_PDFA_ID, XMPConst.CONFORMANCE); partXmpProperty = meta.GetProperty(XMPConst.NS_PDFA_ID, XMPConst.PART); } catch (XMPException) { } if (conformanceXmpProperty == null || partXmpProperty == null) { return(null); } else { String conformance = conformanceXmpProperty.GetValue(); String part = partXmpProperty.GetValue(); return(GetConformanceLevel(part, conformance)); } }
/// <exception cref="iText.Kernel.XMP.XMPException"/> private static String FetchArrayIntoString(XMPMeta meta, String ns, String arrayName) { int keywordsCnt = meta.CountArrayItems(ns, arrayName); StringBuilder sb = null; for (int i = 0; i < keywordsCnt; i++) { XMPProperty curKeyword = meta.GetArrayItem(ns, arrayName, i + 1); if (sb == null) { sb = new StringBuilder(); } else { if (sb.Length > 0) { sb.Append("; "); } } sb.Append(curKeyword.GetValue()); } return(sb != null?sb.ToString() : null); }
/// <summary> /// see /// <see cref="Com.Adobe.Xmp.XMPUtils.SeparateArrayItems(Com.Adobe.Xmp.XMPMeta, string, string, string, Com.Adobe.Xmp.Options.PropertyOptions, bool)"/> /// </summary> /// <param name="xmp">The XMP object containing the array to be updated.</param> /// <param name="schemaNS"> /// The schema namespace URI for the array. Must not be null or /// the empty string. /// </param> /// <param name="arrayName"> /// The name of the array. May be a general path expression, must /// not be null or the empty string. Each item in the array must /// be a simple string value. /// </param> /// <param name="catedStr">The string to be separated into the array items.</param> /// <param name="arrayOptions">Option flags to control the separation.</param> /// <param name="preserveCommas">Flag if commas shall be preserved</param> /// <exception cref="Com.Adobe.Xmp.XMPException">Forwards the Exceptions from the metadata processing</exception> public static void SeparateArrayItems(XMPMeta xmp, string schemaNS, string arrayName, string catedStr, PropertyOptions arrayOptions, bool preserveCommas) { ParameterAsserts.AssertSchemaNS(schemaNS); ParameterAsserts.AssertArrayName(arrayName); if (catedStr == null) { throw new XMPException("Parameter must not be null", XMPErrorConstants.Badparam); } ParameterAsserts.AssertImplementation(xmp); XMPMetaImpl xmpImpl = (XMPMetaImpl)xmp; // Keep a zero value, has special meaning below. XMPNode arrayNode = SeparateFindCreateArray(schemaNS, arrayName, arrayOptions, xmpImpl); // Extract the item values one at a time, until the whole input string is done. string itemValue; int itemStart; int itemEnd; int nextKind = UckNormal; int charKind = UckNormal; char ch = (char) 0; char nextChar = (char) 0; itemEnd = 0; int endPos = catedStr.Length; while (itemEnd < endPos) { // Skip any leading spaces and separation characters. Always skip commas here. // They can be kept when within a value, but not when alone between values. for (itemStart = itemEnd; itemStart < endPos; itemStart++) { ch = catedStr[itemStart]; charKind = ClassifyCharacter(ch); if (charKind == UckNormal || charKind == UckQuote) { break; } } if (itemStart >= endPos) { break; } if (charKind != UckQuote) { // This is not a quoted value. Scan for the end, create an array // item from the substring. for (itemEnd = itemStart; itemEnd < endPos; itemEnd++) { ch = catedStr[itemEnd]; charKind = ClassifyCharacter(ch); if (charKind == UckNormal || charKind == UckQuote || (charKind == UckComma && preserveCommas)) { continue; } else { if (charKind != UckSpace) { break; } else { if ((itemEnd + 1) < endPos) { ch = catedStr[itemEnd + 1]; nextKind = ClassifyCharacter(ch); if (nextKind == UckNormal || nextKind == UckQuote || (nextKind == UckComma && preserveCommas)) { continue; } } } } // Anything left? break; } // Have multiple spaces, or a space followed by a // separator. itemValue = Sharpen.Runtime.Substring(catedStr, itemStart, itemEnd); } else { // Accumulate quoted values into a local string, undoubling // internal quotes that // match the surrounding quotes. Do not undouble "unmatching" // quotes. char openQuote = ch; char closeQuote = GetClosingQuote(openQuote); itemStart++; // Skip the opening quote; itemValue = string.Empty; for (itemEnd = itemStart; itemEnd < endPos; itemEnd++) { ch = catedStr[itemEnd]; charKind = ClassifyCharacter(ch); if (charKind != UckQuote || !IsSurroundingQuote(ch, openQuote, closeQuote)) { // This is not a matching quote, just append it to the // item value. itemValue += ch; } else { // This is a "matching" quote. Is it doubled, or the // final closing quote? // Tolerate various edge cases like undoubled opening // (non-closing) quotes, // or end of input. if ((itemEnd + 1) < endPos) { nextChar = catedStr[itemEnd + 1]; nextKind = ClassifyCharacter(nextChar); } else { nextKind = UckSemicolon; nextChar = (char)unchecked((int)(0x3B)); } if (ch == nextChar) { // This is doubled, copy it and skip the double. itemValue += ch; // Loop will add in charSize. itemEnd++; } else { if (!IsClosingingQuote(ch, openQuote, closeQuote)) { // This is an undoubled, non-closing quote, copy it. itemValue += ch; } else { // This is an undoubled closing quote, skip it and // exit the loop. itemEnd++; break; } } } } } // Add the separated item to the array. // Keep a matching old value in case it had separators. int foundIndex = -1; for (int oldChild = 1; oldChild <= arrayNode.GetChildrenLength(); oldChild++) { if (itemValue.Equals(arrayNode.GetChild(oldChild).GetValue())) { foundIndex = oldChild; break; } } XMPNode newItem = null; if (foundIndex < 0) { newItem = new XMPNode(XMPConstConstants.ArrayItemName, itemValue, null); arrayNode.AddChild(newItem); } } }
/// <summary>Serializes an <code>XMPMeta</code>-object as RDF into a string.</summary> /// <remarks> /// Serializes an <code>XMPMeta</code>-object as RDF into a string. <em>Note:</em> Encoding /// is ignored when serializing to a string. /// </remarks> /// <param name="xmp">a metadata object</param> /// <param name="options"> /// Options to control the serialization (see /// <see cref="Com.Adobe.Xmp.Options.SerializeOptions"/> /// ). /// </param> /// <returns>Returns a string containing the serialized RDF.</returns> /// <exception cref="XMPException">on serializsation errors.</exception> /// <exception cref="Com.Adobe.Xmp.XMPException"/> public static string SerializeToString(XMPMeta xmp, SerializeOptions options) { AssertImplementation(xmp); return XMPSerializerHelper.SerializeToString((XMPMetaImpl)xmp, options); }
public virtual void SetXMPMeta(XMPMeta xmpMeta) { _xmpMeta = xmpMeta; }
/// <exception cref="Com.Adobe.Xmp.XMPException"/> private static void ProcessXmpTags(XmpDirectory directory, XMPMeta xmpMeta) { // store the XMPMeta object on the directory in case others wish to use it directory.SetXMPMeta(xmpMeta); // read all the tags and send them to the directory // I've added some popular tags, feel free to add more tags ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagLensInfo, FmtString); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagLens, FmtString); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagCameraSerialNumber, FmtString); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagFirmware, FmtString); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagMake, FmtString); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagModel, FmtString); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagExposureTime, FmtString); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagExposureProgram, FmtInt); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagApertureValue, FmtRational); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagFNumber, FmtRational); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagFocalLength, FmtRational); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagShutterSpeed, FmtRational); ProcessXmpDateTag(xmpMeta, directory, XmpDirectory.TagDatetimeOriginal); ProcessXmpDateTag(xmpMeta, directory, XmpDirectory.TagDatetimeDigitized); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagRating, FmtDouble); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagLabel, FmtString); // this requires further research // processXmpTag(xmpMeta, directory, Schema.DUBLIN_CORE_SPECIFIC_PROPERTIES, "dc:title", XmpDirectory.TAG_TITLE, FMT_STRING); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagSubject, FmtStringArray); // processXmpDateTag(xmpMeta, directory, Schema.DUBLIN_CORE_SPECIFIC_PROPERTIES, "dc:date", XmpDirectory.TAG_DATE); // processXmpTag(xmpMeta, directory, Schema.DUBLIN_CORE_SPECIFIC_PROPERTIES, "dc:type", XmpDirectory.TAG_TYPE, FMT_STRING); // processXmpTag(xmpMeta, directory, Schema.DUBLIN_CORE_SPECIFIC_PROPERTIES, "dc:description", XmpDirectory.TAG_DESCRIPTION, FMT_STRING); // processXmpTag(xmpMeta, directory, Schema.DUBLIN_CORE_SPECIFIC_PROPERTIES, "dc:relation", XmpDirectory.TAG_RELATION, FMT_STRING); // processXmpTag(xmpMeta, directory, Schema.DUBLIN_CORE_SPECIFIC_PROPERTIES, "dc:coverage", XmpDirectory.TAG_COVERAGE, FMT_STRING); // processXmpTag(xmpMeta, directory, Schema.DUBLIN_CORE_SPECIFIC_PROPERTIES, "dc:creator", XmpDirectory.TAG_CREATOR, FMT_STRING); // processXmpTag(xmpMeta, directory, Schema.DUBLIN_CORE_SPECIFIC_PROPERTIES, "dc:publisher", XmpDirectory.TAG_PUBLISHER, FMT_STRING); // processXmpTag(xmpMeta, directory, Schema.DUBLIN_CORE_SPECIFIC_PROPERTIES, "dc:contributor", XmpDirectory.TAG_CONTRIBUTOR, FMT_STRING); // processXmpTag(xmpMeta, directory, Schema.DUBLIN_CORE_SPECIFIC_PROPERTIES, "dc:rights", XmpDirectory.TAG_RIGHTS, FMT_STRING); // processXmpTag(xmpMeta, directory, Schema.DUBLIN_CORE_SPECIFIC_PROPERTIES, "dc:format", XmpDirectory.TAG_FORMAT, FMT_STRING); // processXmpTag(xmpMeta, directory, Schema.DUBLIN_CORE_SPECIFIC_PROPERTIES, "dc:identifier", XmpDirectory.TAG_IDENTIFIER, FMT_STRING); // processXmpTag(xmpMeta, directory, Schema.DUBLIN_CORE_SPECIFIC_PROPERTIES, "dc:language", XmpDirectory.TAG_LANGUAGE, FMT_STRING); // processXmpTag(xmpMeta, directory, Schema.DUBLIN_CORE_SPECIFIC_PROPERTIES, "dc:audience", XmpDirectory.TAG_AUDIENCE, FMT_STRING); // processXmpTag(xmpMeta, directory, Schema.DUBLIN_CORE_SPECIFIC_PROPERTIES, "dc:provenance", XmpDirectory.TAG_PROVENANCE, FMT_STRING); // processXmpTag(xmpMeta, directory, Schema.DUBLIN_CORE_SPECIFIC_PROPERTIES, "dc:rightsHolder", XmpDirectory.TAG_RIGHTS_HOLDER, FMT_STRING); // processXmpTag(xmpMeta, directory, Schema.DUBLIN_CORE_SPECIFIC_PROPERTIES, "dc:instructionalMethod", XmpDirectory.TAG_INSTRUCTIONAL_METHOD, // FMT_STRING); // processXmpTag(xmpMeta, directory, Schema.DUBLIN_CORE_SPECIFIC_PROPERTIES, "dc:accrualMethod", XmpDirectory.TAG_ACCRUAL_METHOD, FMT_STRING); // processXmpTag(xmpMeta, directory, Schema.DUBLIN_CORE_SPECIFIC_PROPERTIES, "dc:accrualPeriodicity", XmpDirectory.TAG_ACCRUAL_PERIODICITY, // FMT_STRING); // processXmpTag(xmpMeta, directory, Schema.DUBLIN_CORE_SPECIFIC_PROPERTIES, "dc:accrualPolicy", XmpDirectory.TAG_ACCRUAL_POLICY, FMT_STRING); for (XMPIterator iterator = xmpMeta.Iterator(); iterator.HasNext(); ) { XMPPropertyInfo propInfo = (XMPPropertyInfo)iterator.Next(); string path = propInfo.GetPath(); string value = propInfo.GetValue(); if (path != null && value != null) { directory.AddProperty(path, value); } } }
public virtual XMPMeta GetXMPMeta() { if (_xmpMeta == null) { _xmpMeta = new XMPMetaImpl(); } return _xmpMeta; }
/// <summary> /// Serializes an <code>XMPMeta</code>-object as RDF into an <code>OutputStream</code> /// with default options. /// </summary> /// <param name="xmp">a metadata object</param> /// <param name="out">an <code>OutputStream</code> to write the serialized RDF to.</param> /// <exception cref="XMPException">on serializsation errors.</exception> /// <exception cref="Com.Adobe.Xmp.XMPException"/> public static void Serialize(XMPMeta xmp, OutputStream @out) { Serialize(xmp, @out, null); }
/// <summary>Serializes an <code>XMPMeta</code>-object as RDF into a byte buffer.</summary> /// <param name="xmp">a metadata object</param> /// <param name="options"> /// Options to control the serialization (see /// <see cref="Com.Adobe.Xmp.Options.SerializeOptions"/> /// ). /// </param> /// <returns>Returns a byte buffer containing the serialized RDF.</returns> /// <exception cref="XMPException">on serializsation errors.</exception> /// <exception cref="Com.Adobe.Xmp.XMPException"/> public static sbyte[] SerializeToBuffer(XMPMeta xmp, SerializeOptions options) { AssertImplementation(xmp); return XMPSerializerHelper.SerializeToBuffer((XMPMetaImpl)xmp, options); }
/// <summary> /// The initial support for WAV files mapped a legacy ID3 audio copyright /// into a new xmpDM:copyright property. /// </summary> /// <remarks> /// The initial support for WAV files mapped a legacy ID3 audio copyright /// into a new xmpDM:copyright property. This is special case code to migrate /// that into dc:rights['x-default']. The rules: /// <pre> /// 1. If there is no dc:rights array, or an empty array - /// Create one with dc:rights['x-default'] set from double linefeed and xmpDM:copyright. /// 2. If there is a dc:rights array but it has no x-default item - /// Create an x-default item as a copy of the first item then apply rule #3. /// 3. If there is a dc:rights array with an x-default item, /// Look for a double linefeed in the value. /// A. If no double linefeed, compare the x-default value to the xmpDM:copyright value. /// A1. If they match then leave the x-default value alone. /// A2. Otherwise, append a double linefeed and /// the xmpDM:copyright value to the x-default value. /// B. If there is a double linefeed, compare the trailing text to the xmpDM:copyright value. /// B1. If they match then leave the x-default value alone. /// B2. Otherwise, replace the trailing x-default text with the xmpDM:copyright value. /// 4. In all cases, delete the xmpDM:copyright property. /// </pre> /// </remarks> /// <param name="xmp">the metadata object</param> /// <param name="dmCopyright">the "dm:copyright"-property</param> private static void MigrateAudioCopyright(XMPMeta xmp, XMPNode dmCopyright) { try { XMPNode dcSchema = XMPNodeUtils.FindSchemaNode(((XMPMetaImpl)xmp).GetRoot(), XMPConstConstants.NsDc, true); string dmValue = dmCopyright.GetValue(); string doubleLF = "\n\n"; XMPNode dcRightsArray = XMPNodeUtils.FindChildNode(dcSchema, "dc:rights", false); if (dcRightsArray == null || !dcRightsArray.HasChildren()) { // 1. No dc:rights array, create from double linefeed and xmpDM:copyright. dmValue = doubleLF + dmValue; xmp.SetLocalizedText(XMPConstConstants.NsDc, "rights", string.Empty, XMPConstConstants.XDefault, dmValue, null); } else { int xdIndex = XMPNodeUtils.LookupLanguageItem(dcRightsArray, XMPConstConstants.XDefault); if (xdIndex < 0) { // 2. No x-default item, create from the first item. string firstValue = dcRightsArray.GetChild(1).GetValue(); xmp.SetLocalizedText(XMPConstConstants.NsDc, "rights", string.Empty, XMPConstConstants.XDefault, firstValue, null); xdIndex = XMPNodeUtils.LookupLanguageItem(dcRightsArray, XMPConstConstants.XDefault); } // 3. Look for a double linefeed in the x-default value. XMPNode defaultNode = dcRightsArray.GetChild(xdIndex); string defaultValue = defaultNode.GetValue(); int lfPos = defaultValue.IndexOf(doubleLF); if (lfPos < 0) { // 3A. No double LF, compare whole values. if (!dmValue.Equals(defaultValue)) { // 3A2. Append the xmpDM:copyright to the x-default // item. defaultNode.SetValue(defaultValue + doubleLF + dmValue); } } else { // 3B. Has double LF, compare the tail. if (!Sharpen.Runtime.Substring(defaultValue, lfPos + 2).Equals(dmValue)) { // 3B2. Replace the x-default tail. defaultNode.SetValue(Sharpen.Runtime.Substring(defaultValue, 0, lfPos + 2) + dmValue); } } } // 4. Get rid of the xmpDM:copyright. dmCopyright.GetParent().RemoveChild(dmCopyright); } catch (XMPException) { } }
/// <summary><p>Append properties from one XMP object to another.</summary> /// <remarks> /// <p>Append properties from one XMP object to another. /// <p>XMPUtils#appendProperties was created to support the File Info dialog's Append button, and /// has been been generalized somewhat from those specific needs. It appends information from one /// XMP object (source) to another (dest). The default operation is to append only external /// properties that do not already exist in the destination. The flag /// <code>doAllProperties</code> can be used to operate on all properties, external and internal. /// The flag <code>replaceOldValues</code> option can be used to replace the values /// of existing properties. The notion of external /// versus internal applies only to top level properties. The keep-or-replace-old notion applies /// within structs and arrays as described below. /// <ul> /// <li>If <code>replaceOldValues</code> is true then the processing is restricted to the top /// level properties. The processed properties from the source (according to /// <code>doAllProperties</code>) are propagated to the destination, /// replacing any existing values.Properties in the destination that are not in the source /// are left alone. /// <li>If <code>replaceOldValues</code> is not passed then the processing is more complicated. /// Top level properties are added to the destination if they do not already exist. /// If they do exist but differ in form (simple/struct/array) then the destination is left alone. /// If the forms match, simple properties are left unchanged while structs and arrays are merged. /// <li>If <code>deleteEmptyValues</code> is passed then an empty value in the source XMP causes /// the corresponding destination XMP property to be deleted. The default is to treat empty /// values the same as non-empty values. An empty value is any of a simple empty string, an array /// with no items, or a struct with no fields. Qualifiers are ignored. /// </ul> /// <p>The detailed behavior is defined by the following pseudo-code: /// <blockquote> /// <pre> /// appendProperties ( sourceXMP, destXMP, doAllProperties, /// replaceOldValues, deleteEmptyValues ): /// for all source schema (top level namespaces): /// for all top level properties in sourceSchema: /// if doAllProperties or prop is external: /// appendSubtree ( sourceNode, destSchema, replaceOldValues, deleteEmptyValues ) /// appendSubtree ( sourceNode, destParent, replaceOldValues, deleteEmptyValues ): /// if deleteEmptyValues and source value is empty: /// delete the corresponding child from destParent /// else if sourceNode not in destParent (by name): /// copy sourceNode's subtree to destParent /// else if replaceOld: /// delete subtree from destParent /// copy sourceNode's subtree to destParent /// else: /// // Already exists in dest and not replacing, merge structs and arrays /// if sourceNode and destNode forms differ: /// return, leave the destNode alone /// else if form is a struct: /// for each field in sourceNode: /// AppendSubtree ( sourceNode.field, destNode, replaceOldValues ) /// else if form is an alt-text array: /// copy new items by "xml:lang" value into the destination /// else if form is an array: /// copy new items by value into the destination, ignoring order and duplicates /// </pre> /// </blockquote> /// <p><em>Note:</em> appendProperties can be expensive if replaceOldValues is not passed and /// the XMP contains large arrays. The array item checking described above is n-squared. /// Each source item is checked to see if it already exists in the destination, /// without regard to order or duplicates. /// <p>Simple items are compared by value and "xml:lang" qualifier, other qualifiers are ignored. /// Structs are recursively compared by field names, without regard to field order. Arrays are /// compared by recursively comparing all items. /// </remarks> /// <param name="source">The source XMP object.</param> /// <param name="dest">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> /// <exception cref="Com.Adobe.Xmp.XMPException"/> public static void AppendProperties(XMPMeta source, XMPMeta dest, bool doAllProperties, bool replaceOldValues, bool deleteEmptyValues) { XMPUtilsImpl.AppendProperties(source, dest, doAllProperties, replaceOldValues, deleteEmptyValues); }
// EMPTY /// <summary>Create a single edit string from an array of strings.</summary> /// <param name="xmp">The XMP object containing the array to be catenated.</param> /// <param name="schemaNS"> /// The schema namespace URI for the array. Must not be null or /// the empty string. /// </param> /// <param name="arrayName"> /// The name of the array. May be a general path expression, must /// not be null or the empty string. Each item in the array must /// be a simple string value. /// </param> /// <param name="separator"> /// The string to be used to separate the items in the catenated /// string. Defaults to "; ", ASCII semicolon and space /// (U+003B, U+0020). /// </param> /// <param name="quotes"> /// The characters to be used as quotes around array items that /// contain a separator. Defaults to '"' /// </param> /// <param name="allowCommas">Option flag to control the catenation.</param> /// <returns>Returns the string containing the catenated array items.</returns> /// <exception cref="XMPException">Forwards the Exceptions from the metadata processing</exception> /// <exception cref="Com.Adobe.Xmp.XMPException"/> public static string CatenateArrayItems(XMPMeta xmp, string schemaNS, string arrayName, string separator, string quotes, bool allowCommas) { return XMPUtilsImpl.CatenateArrayItems(xmp, schemaNS, arrayName, separator, quotes, allowCommas); }
/// <summary>Remove multiple properties from an XMP object.</summary> /// <remarks> /// Remove multiple properties from an XMP object. /// RemoveProperties was created to support the File Info dialog's Delete /// button, and has been been generalized somewhat from those specific needs. /// It operates in one of three main modes depending on the schemaNS and /// propName parameters: /// <ul> /// <li> Non-empty <code>schemaNS</code> and <code>propName</code> - The named property is /// removed if it is an external property, or if the /// flag <code>doAllProperties</code> option is true. It does not matter whether the /// named property is an actual property or an alias. /// <li> Non-empty <code>schemaNS</code> and empty <code>propName</code> - The all external /// properties in the named schema are removed. Internal properties are also /// removed if the flag <code>doAllProperties</code> option is set. In addition, /// aliases from the named schema will be removed if the flag <code>includeAliases</code> /// option is set. /// <li> Empty <code>schemaNS</code> and empty <code>propName</code> - All external properties in /// all schema are removed. Internal properties are also removed if the /// flag <code>doAllProperties</code> option is passed. Aliases are implicitly handled /// because the associated actuals are internal if the alias is. /// </ul> /// It is an error to pass an empty <code>schemaNS</code> and non-empty <code>propName</code>. /// </remarks> /// <param name="xmp">The XMP object containing the properties to be removed.</param> /// <param name="schemaNS"> /// Optional schema namespace URI for the properties to be /// removed. /// </param> /// <param name="propName">Optional path expression for the property to be removed.</param> /// <param name="doAllProperties"> /// Option flag to control the deletion: do internal properties in /// addition to external properties. /// </param> /// <param name="includeAliases"> /// Option flag to control the deletion: /// Include aliases in the "named schema" case above. /// <em>Note:</em> Currently not supported. /// </param> /// <exception cref="XMPException">Forwards the Exceptions from the metadata processing</exception> /// <exception cref="Com.Adobe.Xmp.XMPException"/> public static void RemoveProperties(XMPMeta xmp, string schemaNS, string propName, bool doAllProperties, bool includeAliases) { XMPUtilsImpl.RemoveProperties(xmp, schemaNS, propName, doAllProperties, includeAliases); }
/// <summary>Separate a single edit string into an array of strings.</summary> /// <param name="xmp">The XMP object containing the array to be updated.</param> /// <param name="schemaNS"> /// The schema namespace URI for the array. Must not be null or /// the empty string. /// </param> /// <param name="arrayName"> /// The name of the array. May be a general path expression, must /// not be null or the empty string. Each item in the array must /// be a simple string value. /// </param> /// <param name="catedStr">The string to be separated into the array items.</param> /// <param name="arrayOptions">Option flags to control the separation.</param> /// <param name="preserveCommas">Flag if commas shall be preserved</param> /// <exception cref="XMPException">Forwards the Exceptions from the metadata processing</exception> /// <exception cref="Com.Adobe.Xmp.XMPException"/> public static void SeparateArrayItems(XMPMeta xmp, string schemaNS, string arrayName, string catedStr, PropertyOptions arrayOptions, bool preserveCommas) { XMPUtilsImpl.SeparateArrayItems(xmp, schemaNS, arrayName, catedStr, arrayOptions, preserveCommas); }
/// <summary>Alias without the new option <code>deleteEmptyValues</code>.</summary> /// <param name="source">The source XMP object.</param> /// <param name="dest">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> /// <exception cref="XMPException">Forwards the Exceptions from the metadata processing</exception> /// <exception cref="Com.Adobe.Xmp.XMPException"/> public static void AppendProperties(XMPMeta source, XMPMeta dest, bool doAllProperties, bool replaceOldValues) { AppendProperties(source, dest, doAllProperties, replaceOldValues, false); }
/// <summary>Serializes an <code>XMPMeta</code>-object as RDF into an <code>OutputStream</code>.</summary> /// <param name="xmp">a metadata object</param> /// <param name="options"> /// Options to control the serialization (see /// <see cref="Com.Adobe.Xmp.Options.SerializeOptions"/> /// ). /// </param> /// <param name="out">an <code>OutputStream</code> to write the serialized RDF to.</param> /// <exception cref="XMPException">on serializsation errors.</exception> /// <exception cref="Com.Adobe.Xmp.XMPException"/> public static void Serialize(XMPMeta xmp, OutputStream @out, SerializeOptions options) { AssertImplementation(xmp); XMPSerializerHelper.Serialize((XMPMetaImpl)xmp, @out, options); }
/// <seealso cref="Com.Adobe.Xmp.XMPUtils.RemoveProperties(Com.Adobe.Xmp.XMPMeta, string, string, bool, bool)"/> /// <param name="xmp">The XMP object containing the properties to be removed.</param> /// <param name="schemaNS"> /// Optional schema namespace URI for the properties to be /// removed. /// </param> /// <param name="propName">Optional path expression for the property to be removed.</param> /// <param name="doAllProperties"> /// Option flag to control the deletion: do internal properties in /// addition to external properties. /// </param> /// <param name="includeAliases"> /// Option flag to control the deletion: Include aliases in the /// "named schema" case above. /// </param> /// <exception cref="Com.Adobe.Xmp.XMPException">If metadata processing fails</exception> public static void RemoveProperties(XMPMeta xmp, string schemaNS, string propName, bool doAllProperties, bool includeAliases) { ParameterAsserts.AssertImplementation(xmp); XMPMetaImpl xmpImpl = (XMPMetaImpl)xmp; if (propName != null && propName.Length > 0) { // Remove just the one indicated property. This might be an alias, // the named schema might not actually exist. So don't lookup the // schema node. if (schemaNS == null || schemaNS.Length == 0) { throw new XMPException("Property name requires schema namespace", XMPErrorConstants.Badparam); } XMPPath expPath = XMPPathParser.ExpandXPath(schemaNS, propName); XMPNode propNode = XMPNodeUtils.FindNode(xmpImpl.GetRoot(), expPath, false, null); if (propNode != null) { if (doAllProperties || !Utils.IsInternalProperty(expPath.GetSegment(XMPPath.StepSchema).GetName(), expPath.GetSegment(XMPPath.StepRootProp).GetName())) { XMPNode parent = propNode.GetParent(); parent.RemoveChild(propNode); if (parent.GetOptions().IsSchemaNode() && !parent.HasChildren()) { // remove empty schema node parent.GetParent().RemoveChild(parent); } } } } else { if (schemaNS != null && schemaNS.Length > 0) { // Remove all properties from the named schema. Optionally include // aliases, in which case // there might not be an actual schema node. // XMP_NodePtrPos schemaPos; XMPNode schemaNode = XMPNodeUtils.FindSchemaNode(xmpImpl.GetRoot(), schemaNS, false); if (schemaNode != null) { if (RemoveSchemaChildren(schemaNode, doAllProperties)) { xmpImpl.GetRoot().RemoveChild(schemaNode); } } if (includeAliases) { // We're removing the aliases also. Look them up by their // namespace prefix. // But that takes more code and the extra speed isn't worth it. // Lookup the XMP node // from the alias, to make sure the actual exists. XMPAliasInfo[] aliases = XMPMetaFactory.GetSchemaRegistry().FindAliases(schemaNS); for (int i = 0; i < aliases.Length; i++) { XMPAliasInfo info = aliases[i]; XMPPath path = XMPPathParser.ExpandXPath(info.GetNamespace(), info.GetPropName()); XMPNode actualProp = XMPNodeUtils.FindNode(xmpImpl.GetRoot(), path, false, null); if (actualProp != null) { XMPNode parent = actualProp.GetParent(); parent.RemoveChild(actualProp); } } } } else { // Remove all appropriate properties from all schema. In this case // we don't have to be // concerned with aliases, they are handled implicitly from the // actual properties. for (Iterator it = xmpImpl.GetRoot().IterateChildren(); it.HasNext(); ) { XMPNode schema = (XMPNode)it.Next(); if (RemoveSchemaChildren(schema, doAllProperties)) { it.Remove(); } } } } }
public virtual void SetXMPMeta([NotNull] XMPMeta xmpMeta) { _xmpMeta = xmpMeta; try { int valueCount = 0; for (Iterator i = _xmpMeta.Iterator(); i.HasNext(); ) { XMPPropertyInfo prop = (XMPPropertyInfo)i.Next(); if (prop.GetPath() != null) { //System.out.printf("%s = %s\n", prop.getPath(), prop.getValue()); valueCount++; } } SetInt(TagXmpValueCount, valueCount); } catch (XMPException) { } }
/// <seealso cref="Com.Adobe.Xmp.XMPUtils.AppendProperties(Com.Adobe.Xmp.XMPMeta, Com.Adobe.Xmp.XMPMeta, bool, bool)"/> /// <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="Com.Adobe.Xmp.XMPException">Forwards the Exceptions from the metadata processing</exception> public static void AppendProperties(XMPMeta source, XMPMeta destination, bool doAllProperties, bool replaceOldValues, bool deleteEmptyValues) { ParameterAsserts.AssertImplementation(source); ParameterAsserts.AssertImplementation(destination); XMPMetaImpl src = (XMPMetaImpl)source; XMPMetaImpl dest = (XMPMetaImpl)destination; for (Iterator it = src.GetRoot().IterateChildren(); it.HasNext(); ) { XMPNode sourceSchema = (XMPNode)it.Next(); // Make sure we have a destination schema node XMPNode destSchema = XMPNodeUtils.FindSchemaNode(dest.GetRoot(), sourceSchema.GetName(), false); bool createdSchema = false; if (destSchema == null) { destSchema = new XMPNode(sourceSchema.GetName(), sourceSchema.GetValue(), new PropertyOptions().SetSchemaNode(true)); dest.GetRoot().AddChild(destSchema); createdSchema = true; } // Process the source schema's children. for (Iterator ic = sourceSchema.IterateChildren(); ic.HasNext(); ) { XMPNode sourceProp = (XMPNode)ic.Next(); if (doAllProperties || !Utils.IsInternalProperty(sourceSchema.GetName(), sourceProp.GetName())) { AppendSubtree(dest, sourceProp, destSchema, replaceOldValues, deleteEmptyValues); } } if (!destSchema.HasChildren() && (createdSchema || deleteEmptyValues)) { // Don't create an empty schema / remove empty schema. dest.GetRoot().RemoveChild(destSchema); } } }
/// <summary>Serializes an <code>XMPMeta</code>-object as RDF into a string.</summary> /// <remarks> /// Serializes an <code>XMPMeta</code>-object as RDF into a string. <em>Note:</em> Encoding /// is ignored when serializing to a string. /// </remarks> /// <param name="xmp">a metadata object</param> /// <param name="options"> /// Options to control the serialization (see /// <see cref="Com.Adobe.Xmp.Options.SerializeOptions"/> /// ). /// </param> /// <returns>Returns a string containing the serialized RDF.</returns> /// <exception cref="XMPException">on serializsation errors.</exception> /// <exception cref="Com.Adobe.Xmp.XMPException"/> public static string SerializeToString(XMPMeta xmp, SerializeOptions options) { AssertImplementation(xmp); return(XMPSerializerHelper.SerializeToString((XMPMetaImpl)xmp, options)); }
// UTF-8 /// <summary>The actual serialization.</summary> /// <param name="xmp">the metadata object to be serialized</param> /// <param name="out">outputStream the output stream to serialize to</param> /// <param name="options">the serialization options</param> /// <exception cref="Com.Adobe.Xmp.XMPException">If case of wrong options or any other serialization error.</exception> public virtual void Serialize(XMPMeta xmp, OutputStream @out, SerializeOptions options) { try { outputStream = new CountOutputStream(@out); writer = new OutputStreamWriter(outputStream, options.GetEncoding()); this.xmp = (XMPMetaImpl)xmp; this.options = options; this.padding = options.GetPadding(); writer = new OutputStreamWriter(outputStream, options.GetEncoding()); CheckOptionsConsistence(); // serializes the whole packet, but don't write the tail yet // and flush to make sure that the written bytes are calculated correctly string tailStr = SerializeAsRDF(); writer.Flush(); // adds padding AddPadding(tailStr.Length); // writes the tail Write(tailStr); writer.Flush(); outputStream.Close(); } catch (IOException) { throw new XMPException("Error writing to the OutputStream", XMPErrorConstants.Unknown); } }
/// <exception cref="iText.Kernel.XMP.XMPException"/> internal static void AppendDocumentInfoToMetadata(PdfDocumentInfo info, XMPMeta xmpMeta) { PdfDictionary docInfo = info.GetPdfObject(); if (docInfo != null) { PdfName key; PdfObject obj; String value; foreach (PdfName pdfName in docInfo.KeySet()) { key = pdfName; obj = docInfo.Get(key); if (obj == null) { continue; } if (obj.IsString()) { value = ((PdfString)obj).ToUnicodeString(); } else { if (obj.IsName()) { value = ((PdfName)obj).GetValue(); } else { continue; } } if (PdfName.Title.Equals(key)) { xmpMeta.SetLocalizedText(XMPConst.NS_DC, PdfConst.Title, XMPConst.X_DEFAULT, XMPConst.X_DEFAULT, value); } else { if (PdfName.Author.Equals(key)) { foreach (String v in iText.IO.Util.StringUtil.Split(value, ",|;")) { if (v.Trim().Length > 0) { AppendArrayItemIfDoesNotExist(xmpMeta, XMPConst.NS_DC, PdfConst.Creator, v.Trim(), PropertyOptions.ARRAY_ORDERED ); } } } else { if (PdfName.Subject.Equals(key)) { xmpMeta.SetLocalizedText(XMPConst.NS_DC, PdfConst.Description, XMPConst.X_DEFAULT, XMPConst.X_DEFAULT, value ); } else { if (PdfName.Keywords.Equals(key)) { foreach (String v in iText.IO.Util.StringUtil.Split(value, ",|;")) { if (v.Trim().Length > 0) { AppendArrayItemIfDoesNotExist(xmpMeta, XMPConst.NS_DC, PdfConst.Subject, v.Trim(), PropertyOptions.ARRAY); } } xmpMeta.SetProperty(XMPConst.NS_PDF, PdfConst.Keywords, value); } else { if (PdfName.Creator.Equals(key)) { xmpMeta.SetProperty(XMPConst.NS_XMP, PdfConst.CreatorTool, value); } else { if (PdfName.Producer.Equals(key)) { xmpMeta.SetProperty(XMPConst.NS_PDF, PdfConst.Producer, value); } else { if (PdfName.CreationDate.Equals(key)) { xmpMeta.SetProperty(XMPConst.NS_XMP, PdfConst.CreateDate, PdfDate.GetW3CDate(value)); } else { if (PdfName.ModDate.Equals(key)) { xmpMeta.SetProperty(XMPConst.NS_XMP, PdfConst.ModifyDate, PdfDate.GetW3CDate(value)); } else { if (PdfName.Trapped.Equals(key)) { xmpMeta.SetProperty(XMPConst.NS_PDF, PdfConst.Trapped, value); } } } } } } } } } } } }
/// <summary>Reads an property value with given namespace URI and property name.</summary> /// <remarks>Reads an property value with given namespace URI and property name. Add property value to directory if exists</remarks> /// <exception cref="Com.Adobe.Xmp.XMPException"/> private void ProcessXmpTag(XMPMeta meta, XmpDirectory directory, string schemaNS, string propName, int tagType, int formatCode) { string property = meta.GetPropertyString(schemaNS, propName); if (property == null) { return; } switch (formatCode) { case FmtRational: { string[] rationalParts = property.Split("/", 2); if (rationalParts.Length == 2) { try { Rational rational = new Rational((long)float.Parse(rationalParts[0]), (long)float.Parse(rationalParts[1])); directory.SetRational(tagType, rational); } catch (FormatException) { directory.AddError(Sharpen.Extensions.StringFormat("Unable to parse XMP property %s as a Rational.", propName)); } } else { directory.AddError("Error in rational format for tag " + tagType); } break; } case FmtInt: { try { directory.SetInt(tagType, Sharpen.Extensions.ValueOf(property)); } catch (FormatException) { directory.AddError(Sharpen.Extensions.StringFormat("Unable to parse XMP property %s as an int.", propName)); } break; } case FmtDouble: { try { directory.SetDouble(tagType, double.Parse(property)); } catch (FormatException) { directory.AddError(Sharpen.Extensions.StringFormat("Unable to parse XMP property %s as an double.", propName)); } break; } case FmtString: { directory.SetString(tagType, property); break; } default: { directory.AddError(Sharpen.Extensions.StringFormat("Unknown format code %d for tag %d", formatCode, tagType)); break; } } }
/// <summary> /// 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> /// </summary> /// <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).Root, XmpConst.NS_DC, true); string dmValue = dmCopyright.Value; const 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).Value; 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.Value; 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.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) + dmValue; } } } // 4. Get rid of the xmpDM:copyright. dmCopyright.Parent.RemoveChild(dmCopyright); } catch (XmpException) { // Don't let failures (like a bad dc:rights form) stop other // cleanup. } }