/// <summary> /// Some image editing apps such as Lightroom, On1, etc., do not persist the keyword metadata /// in the images by default. This can mean you keyword-tag them, but those keywords are only /// stored in the sidecars. Damselfly only scans keyword metadata from the EXIF image data /// itself. /// So to rectify this, we can either read the sidecar files for those keywords, and optionally /// write the missing keywords to the Exif Metadata as we index them. /// </summary> /// <param name="img"></param> /// <param name="keywords"></param> private void ProcessSideCarKeywords(Image img, string[] keywords) { var sideCarTags = new List <string>(); var sidecarSearch = Path.ChangeExtension(img.FileName, "*"); DirectoryInfo dir = new DirectoryInfo(img.Folder.Path); var files = dir.GetFiles(sidecarSearch); var on1Sidecar = files.FirstOrDefault(x => x.Extension.Equals(".on1", StringComparison.OrdinalIgnoreCase)); if (on1Sidecar != null) { var on1MetaData = On1Sidecar.LoadMetadata(on1Sidecar); if (on1MetaData != null) { var missingKeywords = on1MetaData.Keywords .Except(keywords, StringComparer.OrdinalIgnoreCase) .ToList(); if (missingKeywords.Any()) { Logging.LogVerbose($"Image {img.FileName} is missing {missingKeywords.Count} keywords present in the On1 Sidecar."); sideCarTags = sideCarTags.Union(missingKeywords, StringComparer.OrdinalIgnoreCase).ToList(); } } } var xmpSidecar = files.FirstOrDefault(x => x.Extension.Equals(".xmp", StringComparison.OrdinalIgnoreCase)); if (xmpSidecar != null) { using var stream = File.OpenRead(xmpSidecar.FullName); IXmpMeta xmp = XmpMetaFactory.Parse(stream); var xmpKeywords = xmp.Properties.FirstOrDefault(x => x.Path == "pdf:Keywords"); if (xmpKeywords != null) { var missingKeywords = xmpKeywords.Value .Split(",") .Select(x => x.Trim()) .Except(keywords) .ToList(); if (missingKeywords.Any()) { Logging.LogVerbose($"Image {img.FileName} is missing {missingKeywords.Count} keywords present in the XMP Sidecar."); sideCarTags = sideCarTags.Union(missingKeywords, StringComparer.OrdinalIgnoreCase).ToList(); } } } if (sideCarTags.Any()) { // Now, submit the tags; note they won't get created immediately, but in batch. Logging.Log($"Applying {sideCarTags.Count} keywords from sidecar files to image {img.FileName}"); _ = MetaDataService.Instance.UpdateTagsAsync(new[] { img }, sideCarTags, null); } }
/// <param name="xmp">Asserts that xmp is compatible to <c>XMPMetaImpl</c>.s</param> private static void AssertImplementation(IXmpMeta xmp) { if (!(xmp is XmpMeta)) { throw new NotSupportedException("The serializing service works only with the XMPMeta implementation of this library"); } }
// UTF-8 /// <summary>The actual serialization.</summary> /// <param name="xmp">the metadata object to be serialized</param> /// <param name="stream">outputStream the output stream to serialize to</param> /// <param name="options">the serialization options</param> /// <exception cref="XmpException">If case of wrong options or any other serialization error.</exception> public void Serialize(IXmpMeta xmp, Stream stream, SerializeOptions options) { try { _stream = stream; _startPos = _stream.Position; _writer = new StreamWriter(_stream, options.GetEncoding()); _xmp = (XmpMeta)xmp; _options = options; _padding = options.Padding; _writer = new StreamWriter(_stream, 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 var tailStr = SerializeAsRdf(); _writer.Flush(); // adds padding AddPadding(tailStr.Length); // writes the tail Write(tailStr); _writer.Flush(); } catch (IOException) { throw new XmpException("Error writing to the OutputStream", XmpErrorCode.Unknown); } }
/** Sets the identifier. * * @param xmpMeta * @param id */ public static void SetIdentifiers(IXmpMeta xmpMeta, String[] id) { XmpUtils.RemoveProperties(xmpMeta, XmpConst.NS_DC, IDENTIFIER, true, true); for (int i = 0; i < id.Length; i++) { xmpMeta.AppendArrayItem(XmpConst.NS_DC, IDENTIFIER, new PropertyOptions(PropertyOptions.ARRAY), id[i], null); } }
/** * Test doesPropertyExist, deleteProperty, and related methods. * @param meta a predefined <code>XmpMeta</code> object. */ private static void CoverExistingProperties(IXmpMeta meta) { writeMajorLabel("Test doesPropertyExist, deleteProperty, and related methods"); printXmpMeta(meta, "XMP object"); log.WriteLine("doesPropertyExist ns1:Prop = " + meta.DoesPropertyExist(TestData.NS1, "Prop")); log.WriteLine("doesPropertyExist ns1:Struct = " + meta.DoesPropertyExist(TestData.NS1, "ns1:Struct")); log.WriteLine("doesArrayItemExist ns1:Bag[2] = " + meta.DoesArrayItemExist(TestData.NS1, "Bag", 2)); log.WriteLine("doesArrayItemExist ns1:Seq[last()] = " + meta.DoesArrayItemExist(TestData.NS1, "ns1:Seq", XmpConstants.ArrayLastItem)); log.WriteLine("doesStructFieldExist ns1:Struct/ns2:Field1 = " + meta.DoesStructFieldExist(TestData.NS1, "Struct", TestData.NS2, "Field1")); log.WriteLine("doesQualifierExist ns1:QualProp1/?ns2:Qual1 = " + meta.DoesQualifierExist(TestData.NS1, "QualProp1", TestData.NS2, "Qual1")); log.WriteLine("doesQualifierExist ns1:QualProp2/?xml:lang = " + meta.DoesQualifierExist(TestData.NS1, "QualProp2", XmpConstants.NsXml, "lang")); log.WriteLine(); log.WriteLine("doesPropertyExist (namespace is null) = " + meta.DoesPropertyExist(null, "ns1:Bag")); log.WriteLine("doesArrayItemExist (namespace is null) = " + meta.DoesArrayItemExist(null, "ns1:Bag", XmpConstants.ArrayLastItem)); log.WriteLine("doesQualifierExist ns:Bogus (namespace not existing) = " + meta.DoesPropertyExist("ns:bogus/", "Bogus")); log.WriteLine("doesPropertyExist ns1:Bogus = " + meta.DoesPropertyExist(TestData.NS1, "Bogus")); log.WriteLine("doesArrayItemExist ns1:Bag[99] = " + meta.DoesArrayItemExist(TestData.NS1, "Bag", 99)); log.WriteLine("doesStructFieldExist ns1:Struct/ns2:Bogus = " + meta.DoesStructFieldExist(TestData.NS1, "Struct", TestData.NS2, "Bogus")); log.WriteLine("doesQualifierExist ns1:Prop/?ns2:Bogus = " + meta.DoesQualifierExist(TestData.NS1, "Prop", TestData.NS2, "Bogus")); }
public override void Close(PdfWriter writer) { base.Close(writer); bool ok = false; IXmpMeta xmpMeta = writer.XmpWriter.XmpMeta; try { String docFileName = xmpMeta.GetPropertyString(PdfAXmpWriter.zugferdSchemaNS, PdfAXmpWriter.zugferdDocumentFileName); foreach (PdfFileSpecification attachment in attachments) { if (docFileName.Equals(attachment.GetAsString(PdfName.UF).ToString())) { PdfName relationship = attachment.GetAsName(PdfName.AFRELATIONSHIP); if (!AFRelationshipValue.Alternative.Equals(relationship)) { attachments.Clear(); throw new PdfAConformanceException(attachment, MessageLocalization.GetComposedMessage("afrelationship.value.shall.be.alternative")); } ok = true; break; } } } catch (Exception e) { attachments.Clear(); throw e; } attachments.Clear(); if (!ok) { throw new PdfAConformanceException(xmpMeta, MessageLocalization.GetComposedMessage("zugferd.xmp.schema.shall.contain.attachment.name")); } }
/** * Adds information about the PDF/A conformance level to the XMP metadata. * * @param conformanceLevel * @throws IOException */ private void AddRdfDescription(PdfAConformanceLevel conformanceLevel) { switch (conformanceLevel) { case PdfAConformanceLevel.PDF_A_1A: xmpMeta.SetProperty(XmpConst.NS_PDFA_ID, PdfAProperties.PART, "1"); xmpMeta.SetProperty(XmpConst.NS_PDFA_ID, PdfAProperties.CONFORMANCE, "A"); break; case PdfAConformanceLevel.PDF_A_1B: xmpMeta.SetProperty(XmpConst.NS_PDFA_ID, PdfAProperties.PART, "1"); xmpMeta.SetProperty(XmpConst.NS_PDFA_ID, PdfAProperties.CONFORMANCE, "B"); break; case PdfAConformanceLevel.PDF_A_2A: xmpMeta.SetProperty(XmpConst.NS_PDFA_ID, PdfAProperties.PART, "2"); xmpMeta.SetProperty(XmpConst.NS_PDFA_ID, PdfAProperties.CONFORMANCE, "A"); break; case PdfAConformanceLevel.PDF_A_2B: xmpMeta.SetProperty(XmpConst.NS_PDFA_ID, PdfAProperties.PART, "2"); xmpMeta.SetProperty(XmpConst.NS_PDFA_ID, PdfAProperties.CONFORMANCE, "B"); break; case PdfAConformanceLevel.PDF_A_2U: xmpMeta.SetProperty(XmpConst.NS_PDFA_ID, PdfAProperties.PART, "2"); xmpMeta.SetProperty(XmpConst.NS_PDFA_ID, PdfAProperties.CONFORMANCE, "U"); break; case PdfAConformanceLevel.PDF_A_3A: xmpMeta.SetProperty(XmpConst.NS_PDFA_ID, PdfAProperties.PART, "3"); xmpMeta.SetProperty(XmpConst.NS_PDFA_ID, PdfAProperties.CONFORMANCE, "A"); break; case PdfAConformanceLevel.PDF_A_3B: xmpMeta.SetProperty(XmpConst.NS_PDFA_ID, PdfAProperties.PART, "3"); xmpMeta.SetProperty(XmpConst.NS_PDFA_ID, PdfAProperties.CONFORMANCE, "B"); break; case PdfAConformanceLevel.PDF_A_3U: xmpMeta.SetProperty(XmpConst.NS_PDFA_ID, PdfAProperties.PART, "3"); xmpMeta.SetProperty(XmpConst.NS_PDFA_ID, PdfAProperties.CONFORMANCE, "U"); break; case PdfAConformanceLevel.ZUGFeRD: xmpMeta.SetProperty(XmpConst.NS_PDFA_ID, PdfAProperties.PART, "3"); xmpMeta.SetProperty(XmpConst.NS_PDFA_ID, PdfAProperties.CONFORMANCE, "B"); IXmpMeta taggedExtensionMeta = XmpMetaFactory.ParseFromString(zugferdExtension); XmpUtils.AppendProperties(taggedExtensionMeta, xmpMeta, true, false); break; default: break; } if (writer.IsTagged()) { IXmpMeta taggedExtensionMeta = XmpMetaFactory.ParseFromString(pdfUaExtension); XmpUtils.AppendProperties(taggedExtensionMeta, xmpMeta, true, false); } }
/** * Sets a subject. * * @param xmpMeta * @param subject array of subjects */ public static void SetSubject(IXmpMeta xmpMeta, String[] subject) { XmpUtils.RemoveProperties(xmpMeta, XmpConst.NS_DC, SUBJECT, true, true); for (int i = 0; i < subject.Length; i++) { xmpMeta.AppendArrayItem(XmpConst.NS_DC, SUBJECT, new PropertyOptions(PropertyOptions.ARRAY), subject[i], null); } }
/** * Sets an array of authors. * * @param xmpMeta * @param author */ public static void SetAuthor(IXmpMeta xmpMeta, String[] author) { XmpUtils.RemoveProperties(xmpMeta, XmpConst.NS_DC, CREATOR, true, true); for (int i = 0; i < author.Length; i++) { xmpMeta.AppendArrayItem(XmpConst.NS_DC, CREATOR, new PropertyOptions(PropertyOptions.ARRAY_ORDERED), author[i], null); } }
private byte[] GetPropertyBase64(IXmpMeta meta, string schemaNs, string propName) { // Use System.Convert rather than IXmpMeta Base64 decoder. // The base64 strings have the padding removed which causes an exception in the IXmpMeta decoder. string base64 = meta.GetPropertyString(schemaNs, propName); base64 = base64.PadRight(base64.Length + (4 - base64.Length % 4) % 4, '='); return(Convert.FromBase64String(base64)); }
/** * Sets an array of publishers. * * @param xmpMeta * @param publisher */ public static void SetPublisher(IXmpMeta xmpMeta, String[] publisher) { XmpUtils.RemoveProperties(xmpMeta, XmpConst.NS_DC, PUBLISHER, true, true); for (int i = 0; i < publisher.Length; i++) { xmpMeta.AppendArrayItem(XmpConst.NS_DC, PUBLISHER, new PropertyOptions(PropertyOptions.ARRAY_ORDERED), publisher[i], null); } }
/// <exception cref="XmpException"/> private static void ProcessXmpTags(XmpDirectory directory, IXmpMeta 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, FormatType.String); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagLens, FormatType.String); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagCameraSerialNumber, FormatType.String); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagFirmware, FormatType.String); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagMake, FormatType.String); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagModel, FormatType.String); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagExposureTime, FormatType.String); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagExposureProgram, FormatType.Int); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagApertureValue, FormatType.Rational); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagFNumber, FormatType.Rational); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagFocalLength, FormatType.Rational); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagShutterSpeed, FormatType.Rational); ProcessXmpDateTag(xmpMeta, directory, XmpDirectory.TagDateTimeOriginal); ProcessXmpDateTag(xmpMeta, directory, XmpDirectory.TagDateTimeDigitized); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagRating, FormatType.Double); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagLabel, FormatType.String); // this requires further research // processXmpTag(xmpMeta, directory, Schema.DUBLIN_CORE_SPECIFIC_PROPERTIES, "dc:title", XmpDirectory.TAG_TITLE, FMT_STRING); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagSubject, FormatType.StringArray); // 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); foreach (var prop in xmpMeta.Properties) { var path = prop.Path; var value = prop.Value; if (path != null && value != null) { directory.AddProperty(path, value); } } }
/// <summary> /// The initial support for WAV files mapped a legacy ID3 audio copyright /// into a new xmpDM:copyright property. /// </summary> /// <remarks> /// The initial support for WAV files mapped a legacy ID3 audio copyright /// into a new xmpDM:copyright property. This is special case code to migrate /// that into dc:rights['x-default']. The rules: /// <pre> /// 1. If there is no dc:rights array, or an empty array - /// Create one with dc:rights['x-default'] set from double linefeed and xmpDM:copyright. /// 2. If there is a dc:rights array but it has no x-default item - /// Create an x-default item as a copy of the first item then apply rule #3. /// 3. If there is a dc:rights array with an x-default item, /// Look for a double linefeed in the value. /// A. If no double linefeed, compare the x-default value to the xmpDM:copyright value. /// A1. If they match then leave the x-default value alone. /// A2. Otherwise, append a double linefeed and /// the xmpDM:copyright value to the x-default value. /// B. If there is a double linefeed, compare the trailing text to the xmpDM:copyright value. /// B1. If they match then leave the x-default value alone. /// B2. Otherwise, replace the trailing x-default text with the xmpDM:copyright value. /// 4. In all cases, delete the xmpDM:copyright property. /// </pre> /// </remarks> /// <param name="xmp">the metadata object</param> /// <param name="dmCopyright">the "dm:copyright"-property</param> private static void MigrateAudioCopyright(IXmpMeta xmp, XmpNode dmCopyright) { try { var dcSchema = XmpNodeUtils.FindSchemaNode(((XmpMeta)xmp).GetRoot(), XmpConstants.NsDC, true); var dmValue = dmCopyright.Value; var doubleLf = "\n\n"; var dcRightsArray = XmpNodeUtils.FindChildNode(dcSchema, "dc:rights", false); if (dcRightsArray == null || !dcRightsArray.HasChildren) { // 1. No dc:rights array, create from double linefeed and xmpDM:copyright. dmValue = doubleLf + dmValue; xmp.SetLocalizedText(XmpConstants.NsDC, "rights", string.Empty, XmpConstants.XDefault, dmValue, null); } else { var xdIndex = XmpNodeUtils.LookupLanguageItem(dcRightsArray, XmpConstants.XDefault); if (xdIndex < 0) { // 2. No x-default item, create from the first item. var firstValue = dcRightsArray.GetChild(1).Value; xmp.SetLocalizedText(XmpConstants.NsDC, "rights", string.Empty, XmpConstants.XDefault, firstValue, null); xdIndex = XmpNodeUtils.LookupLanguageItem(dcRightsArray, XmpConstants.XDefault); } // 3. Look for a double linefeed in the x-default value. var defaultNode = dcRightsArray.GetChild(xdIndex); var defaultValue = defaultNode.Value; var lfPos = defaultValue.IndexOf(doubleLf); if (lfPos < 0) { // 3A. No double LF, compare whole values. if (!dmValue.Equals(defaultValue)) { // 3A2. Append the xmpDM:copyright to the x-default // item. defaultNode.Value = defaultValue + doubleLf + dmValue; } } else { // 3B. Has double LF, compare the tail. if (!defaultValue.Substring(lfPos + 2).Equals(dmValue)) { // 3B2. Replace the x-default tail. defaultNode.Value = defaultValue.Substring(0, lfPos + 2 - 0) + dmValue; } } } // 4. Get rid of the xmpDM:copyright. dmCopyright.Parent.RemoveChild(dmCopyright); } catch (XmpException) { } }
private void ParseExtendedDirectory(IXmpMeta meta) { try { ImageData = meta.GetPropertyBase64(nsImage, "GImage:Data"); DepthData = meta.GetPropertyBase64(nsDepthmap, "GDepth:Data"); } catch { } }
public static void AssertImplementation(IXmpMeta xmp) { if (xmp == null) { throw new XmpException("Parameter must not be null", XmpErrorCode.BadParam); } if (!(xmp is XmpMeta)) { throw new XmpException("The XMPMeta-object is not compatible with this implementation", XmpErrorCode.BadParam); } }
private static void ProcessXmpTags([NotNull] XmpDirectory directory, [NotNull] IXmpMeta xmpMeta) { // store the XMPMeta object on the directory in case others wish to use it directory.SetXmpMeta(xmpMeta); // extract selected XMP tags ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagLensInfo, FormatType.String); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagLens, FormatType.String); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagCameraSerialNumber, FormatType.String); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagFirmware, FormatType.String); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagMake, FormatType.String); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagModel, FormatType.String); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagExposureTime, FormatType.String); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagExposureProgram, FormatType.Int); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagApertureValue, FormatType.Rational); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagFNumber, FormatType.Rational); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagFocalLength, FormatType.Rational); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagShutterSpeed, FormatType.Rational); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagDateTimeOriginal, FormatType.String); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagDateTimeDigitized, FormatType.String); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagBaseUrl, FormatType.String); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagCreateDate, FormatType.String); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagCreatorTool, FormatType.String); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagIdentifier, FormatType.String); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagMetadataDate, FormatType.String); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagModifyDate, FormatType.String); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagNickname, FormatType.String); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagRating, FormatType.Double); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagLabel, FormatType.String); ProcessXmpTag(xmpMeta, directory, XmpDirectory.TagSubject, FormatType.StringArray); // this requires further research // ProcessXmpTag(xmpMeta, directory, Schema.DUBLIN_CORE_SPECIFIC_PROPERTIES, "dc:title", XmpDirectory.TAG_TITLE, FMT_STRING); // 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); }
private void GenerateEAttachmentDocument(PdfAWriter writer, XmpWriter xmpWriter, EAttactment attachment) { // Use default intent if output intent of this instance was not set if (attachment.outputIntents == null) { //byte[] iccProfile = File.ReadAllBytes("/Resources/sRGB Color Space Profile.icm"); byte[] iccProfile = Properties.Resources.sRGB_Color_Space_Profile; ICC_Profile icc = ICC_Profile.GetInstance(iccProfile); writer.SetOutputIntents("sRGB IEC61966-2.1", "", "http://www.color.org", "sRGB IEC61966-2.1", icc); } else { OutputIntents outputIntents = attachment.outputIntents; byte[] iccProfileByteArray = File.ReadAllBytes(outputIntents.colorProfilePath); ICC_Profile icc = ICC_Profile.GetInstance(iccProfileByteArray); writer.SetOutputIntents(outputIntents.outputConditionIdentifier, outputIntents.outputCondition, outputIntents.registryName, outputIntents.info, icc); } //============= Create Exchange ECertificate ================= // 1 add ContentInformation.xml to document PdfArray attachmentArray = new PdfArray(); writer.ExtraCatalog.Put(new PdfName("AF"), attachmentArray); PdfFileSpecification contentSpec = this.EmbeddedAttachment(attachment.contentInformationXMLPath, attachment.attachmentName, attachment.attachmentMIME, new PdfName(attachment.attachmentType), writer, attachment.attachmentDescription); attachmentArray.Add(contentSpec.Reference); foreach (var item in attachment.fileAttachments) { contentSpec = this.EmbeddedAttachment(item.attachmentPath, item.attachmentName, item.attachmentMIME, new PdfName(item.attachmentType), writer, item.attachmentDescription); attachmentArray.Add(contentSpec.Reference); } // 2 add Electronic Document XMP Metadata ElectronicDocumentSchema ed = ElectronicDocumentSchema.generateED(attachment.attachmentName, attachment.documentVersion, attachment.documentID, attachment.documentOID); xmpWriter.AddRdfDescription(ed); string pdfaSchema = Properties.Resources.EDocument_PDFAExtensionSchema; // convert string to stream byte[] byteArray = Encoding.UTF8.GetBytes(pdfaSchema); //byte[] byteArray = Encoding.ASCII.GetBytes(contents); MemoryStream stream = new MemoryStream(byteArray); IXmpMeta edPDFAextension = XmpMetaFactory.Parse(stream); IXmpMeta originalXMP = xmpWriter.XmpMeta; XmpUtils.AppendProperties(edPDFAextension, originalXMP, true, true); }
private void WriteXmpMetaData(string imageFilePath, MasterImage masterImage, bool alwaysWriteMetadata, bool preview) { bool writeMetadata = false; IXmpMeta xmp = XmpMetaFactory.Create(); if ((!String.IsNullOrEmpty(masterImage.Caption) && !IsEquivalent(masterImage.Caption, imageFilePath)) || alwaysWriteMetadata) { xmp.AppendArrayItem(XmpConstants.NsDC, "dc:title", new PropertyOptions { IsArrayAlternate = true }, masterImage.Caption, null); writeMetadata = true; } if (!String.IsNullOrEmpty(masterImage.Comment)) { xmp.AppendArrayItem(XmpConstants.NsDC, "dc:description", new PropertyOptions { IsArrayAlternate = true }, masterImage.Comment, null); writeMetadata = true; } if (masterImage.Rating != null && (int)masterImage.Rating > 0) { xmp.SetProperty(XmpConstants.NsXmp, "xmp:Rating", ((int)masterImage.Rating).ToString()); writeMetadata = true; } // TODO: Handle faces. if (writeMetadata) { string metaFilePath = Path.ChangeExtension(imageFilePath, ".xmp"); if (File.Exists(metaFilePath)) { Console.Error.WriteLine("ERROR: XMP meta file already exists, skipping '" + metaFilePath + "'."); } else if (!preview) { Directory.CreateDirectory(Path.GetDirectoryName(metaFilePath)); using (var stream = File.OpenWrite(metaFilePath)) { XmpMetaFactory.Serialize(xmp, stream, new SerializeOptions { OmitPacketWrapper = true }); } numMetadataFilesCreated++; } } }
public void SetXmpMeta([NotNull] IXmpMeta xmpMeta) { _xmpMeta = xmpMeta; try { Set(TagXmpValueCount, _xmpMeta.Properties.Count(prop => prop.Path != null)); } catch (XmpException) { } }
/** * Print the content of an XmpMeta object a headline and its name. * @param meta an <code>XmpMeta</code> object * @param title the headline */ private void PrintXmpMeta(IXmpMeta meta, string title) { var name = meta.GetObjectName(); if (!string.IsNullOrEmpty(name)) { _log.WriteLine("{0} (Name: '{1}'):", title, name); } else { _log.WriteLine("{0}:", title); } _log.WriteLine(meta.DumpObject()); }
/// <seealso cref= XMPUtils#appendProperties(XMPMeta, XMPMeta, boolean, boolean) </seealso> /// <param name="source"> The source XMP object. </param> /// <param name="destination"> The destination XMP object. </param> /// <param name="doAllProperties"> Do internal properties in addition to external properties. </param> /// <param name="replaceOldValues"> Replace the values of existing properties. </param> /// <param name="deleteEmptyValues"> Delete destination values if source property is empty. </param> /// <exception cref="XmpException"> Forwards the Exceptions from the metadata processing </exception> public static void AppendProperties(IXmpMeta source, IXmpMeta destination, bool doAllProperties, bool replaceOldValues, bool deleteEmptyValues) { ParameterAsserts.AssertImplementation(source); ParameterAsserts.AssertImplementation(destination); XmpMetaImpl src = (XmpMetaImpl)source; XmpMetaImpl dest = (XmpMetaImpl)destination; for (IEnumerator it = src.Root.IterateChildren(); it.MoveNext();) { XmpNode sourceSchema = (XmpNode)it.Current; if (sourceSchema == null) { continue; } // Make sure we have a destination schema node XmpNode destSchema = XmpNodeUtils.FindSchemaNode(dest.Root, sourceSchema.Name, false); bool createdSchema = false; if (destSchema == null) { PropertyOptions propertyOptions = new PropertyOptions(); propertyOptions.SchemaNode = true; destSchema = new XmpNode(sourceSchema.Name, sourceSchema.Value, propertyOptions); dest.Root.AddChild(destSchema); createdSchema = true; } // Process the source schema's children. for (IEnumerator ic = sourceSchema.IterateChildren(); ic.MoveNext();) { XmpNode sourceProp = (XmpNode)ic.Current; if (sourceProp == null) { continue; } if (doAllProperties || !Utils.IsInternalProperty(sourceSchema.Name, sourceProp.Name)) { AppendSubtree(dest, sourceProp, destSchema, replaceOldValues, deleteEmptyValues); } } if (!destSchema.HasChildren() && (createdSchema || deleteEmptyValues)) { // Don't create an empty schema / remove empty schema. dest.Root.RemoveChild(destSchema); } } }
// --------------------------------------------------------------------------------------------- // Utilities for this example // --------------------------------------------------------------------------------------------- /** * Print the content of an XmpMeta object a headline and its name. * @param meta an <code>XmpMeta</code> object * @param title the headline */ private static void printXmpMeta(IXmpMeta meta, string title) { string name = meta.GetObjectName(); if (!string.IsNullOrEmpty(name)) { log.WriteLine("{0} (Name: '{1}'):", title, name); } else { log.WriteLine("{0}:", title); } log.WriteLine(meta.DumpObject()); log.WriteLine(); }
/// <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> public static string CatenateArrayItems(IXmpMeta xmp, string schemaNs, string arrayName, string separator, string quotes, bool allowCommas) { ParameterAsserts.AssertSchemaNs(schemaNs); ParameterAsserts.AssertArrayName(arrayName); ParameterAsserts.AssertImplementation(xmp); if (string.IsNullOrEmpty(separator)) separator = "; "; if (string.IsNullOrEmpty(quotes)) quotes = "\""; var xmpImpl = (XmpMeta)xmp; // Return an empty result if the array does not exist, // hurl if it isn't the right form. var arrayPath = XmpPathParser.ExpandXPath(schemaNs, arrayName); var arrayNode = XmpNodeUtils.FindNode(xmpImpl.GetRoot(), arrayPath, false, null); if (arrayNode == null) return string.Empty; if (!arrayNode.Options.IsArray || arrayNode.Options.IsArrayAlternate) throw new XmpException("Named property must be non-alternate array", XmpErrorCode.BadParam); // Make sure the separator is OK. CheckSeparator(separator); // Make sure the open and close quotes are a legitimate pair. var openQuote = quotes[0]; var closeQuote = CheckQuotes(quotes, openQuote); // Build the result, quoting the array items, adding separators. // Hurl if any item isn't simple. var catenatedString = new StringBuilder(); for (var it = arrayNode.IterateChildren(); it.HasNext(); ) { var currItem = (XmpNode)it.Next(); if (currItem.Options.IsCompositeProperty) throw new XmpException("Array items must be simple", XmpErrorCode.BadParam); var str = ApplyQuotes(currItem.Value, openQuote, closeQuote, allowCommas); catenatedString.Append(str); if (it.HasNext()) catenatedString.Append(separator); } return catenatedString.ToString(); }
private void ReadPdfAInfo() { byte[] metadata = null; IXmpProperty pdfaidConformance = null; IXmpProperty pdfaidPart = null; try { metadata = reader.Metadata; xmpMeta = XmpMetaParser.Parse(metadata, null); pdfaidConformance = xmpMeta.GetProperty(XmpConst.NS_PDFA_ID, "pdfaid:conformance"); pdfaidPart = xmpMeta.GetProperty(XmpConst.NS_PDFA_ID, "pdfaid:part"); } catch (Exception e) { throw new PdfAConformanceException(MessageLocalization.GetComposedMessage("only.pdfa.documents.can.be.opened.in.PdfAStamper")); } if (pdfaidConformance == null || pdfaidPart == null) { throw new PdfAConformanceException(MessageLocalization.GetComposedMessage("only.pdfa.documents.can.be.opened.in.PdfAStamper")); } switch (((IPdfAConformance)pdfIsoConformance).ConformanceLevel) { case PdfAConformanceLevel.PDF_A_1A: case PdfAConformanceLevel.PDF_A_1B: if (!"1".Equals(pdfaidPart.Value)) { throw new PdfAConformanceException(MessageLocalization.GetComposedMessage("only.pdfa.1.documents.can.be.opened.in.PdfAStamper", "1")); } break; case PdfAConformanceLevel.PDF_A_2A: case PdfAConformanceLevel.PDF_A_2B: case PdfAConformanceLevel.PDF_A_2U: if (!"2".Equals(pdfaidPart.Value)) { throw new PdfAConformanceException(MessageLocalization.GetComposedMessage("only.pdfa.1.documents.can.be.opened.in.PdfAStamper", "2")); } break; case PdfAConformanceLevel.PDF_A_3A: case PdfAConformanceLevel.PDF_A_3B: case PdfAConformanceLevel.PDF_A_3U: if (!"3".Equals(pdfaidPart.Value)) { throw new PdfAConformanceException(MessageLocalization.GetComposedMessage("only.pdfa.1.documents.can.be.opened.in.PdfAStamper", "3")); } break; } }
virtual public void AddRdfDescription(XmpSchema s) { try { String str = "<rdf:RDF xmlns:rdf=\"" + XmpConst.NS_RDF + "\">" + "<rdf:Description rdf:about=\"" + xmpMeta.ObjectName + "\" " + s.Xmlns + ">" + s.ToString() + "</rdf:Description></rdf:RDF>\n"; IXmpMeta extMeta = XmpMetaFactory.ParseFromString(str); XmpUtils.AppendProperties(extMeta, xmpMeta, true, true); } catch (XmpException xmpExc) { throw new IOException(xmpExc.Message); } }
/// <exception cref="XmpException"/> private static void ProcessXmpDateTag([NotNull] IXmpMeta meta, [NotNull] XmpDirectory directory, int tagType) { string schemaNs; string propName; if (!XmpDirectory.TagSchemaMap.TryGetValue(tagType, out schemaNs) || !XmpDirectory.TagPropNameMap.TryGetValue(tagType, out propName)) { return; } var cal = meta.GetPropertyCalendar(schemaNs, propName); if (cal != null) { directory.Set(tagType, cal.GetTime()); } }
public void AddRdfDescription(String xmlns, String content) { try { String str = "<rdf:RDF xmlns:rdf=\"" + XmpConst.NS_RDF + "\">" + "<rdf:Description rdf:about=\"" + xmpMeta.ObjectName + "\" " + xmlns + ">" + content + "</rdf:Description></rdf:RDF>\n"; byte[] bytes = Encoding.Convert(Encoding.UTF8, Encoding.ASCII, Encoding.UTF8.GetBytes(str)); IXmpMeta extMeta = XmpMetaFactory.ParseFromString(str); XmpUtils.AppendProperties(extMeta, xmpMeta, true, true); } catch (XmpException xmpExc) { throw new IOException(xmpExc.Message); } }
private void ParseExtendedDirectory(IXmpMeta meta) { try { if (ImageMime != null && meta.DoesPropertyExist(nsImage, "GImage:Data")) { ImageData = GetPropertyBase64(meta, nsImage, "GImage:Data"); } if (AudioMime != null && meta.DoesPropertyExist(nsAudio, "GAudio:Data")) { AudioData = GetPropertyBase64(meta, nsAudio, "GAudio:Data"); } } catch { } }
private void GpsAltitudeRef(IXmpMeta xmp, FileIndexItem item) { string gpsAltitude = null; string gpsAltitudeRef = null; foreach (var property in xmp.Properties) { // Path=exif:GPSAltitude Namespace=http://ns.adobe.com/exif/1.0/ Value=627/10 // Path=exif:GPSAltitudeRef Namespace=http://ns.adobe.com/exif/1.0/ Value=0 var gpsAltitudeLocal = GetContentNameSpace(property, "exif:GPSAltitude"); if (gpsAltitudeLocal != null) { gpsAltitude = gpsAltitudeLocal; } var gpsAltitudeRefLocal = GetContentNameSpace(property, "exif:GPSAltitudeRef"); if (gpsAltitudeRefLocal != null) { gpsAltitudeRef = gpsAltitudeRefLocal; } } if (gpsAltitude == null || gpsAltitudeRef == null) { return; } if (!gpsAltitude.Contains("/")) { return; } var locationAltitude = MathFraction.Fraction(gpsAltitude); if (Math.Abs(locationAltitude) < 0) { return; } item.LocationAltitude = locationAltitude; //For items under the sea level if (gpsAltitudeRef == "1") { item.LocationAltitude = item.LocationAltitude * -1; } }
public void SetXmpMeta(IXmpMeta xmpMeta) { XmpMeta = xmpMeta; int valueCount = 0; XmpIterator i = new XmpIterator((XmpMeta)XmpMeta, null, null, _iteratorOptions); while (i.HasNext()) { var prop = (IXmpPropertyInfo)i.Next(); if (prop.Path != null) { valueCount++; } } Set(TagXmpValueCount, valueCount); }
/// <param name="source">The source XMP object.</param> /// <param name="destination">The destination XMP object.</param> /// <param name="doAllProperties">Do internal properties in addition to external properties.</param> /// <param name="replaceOldValues">Replace the values of existing properties.</param> /// <param name="deleteEmptyValues">Delete destination values if source property is empty.</param> /// <exception cref="XmpException">Forwards the Exceptions from the metadata processing</exception> public static void AppendProperties(IXmpMeta source, IXmpMeta destination, bool doAllProperties, bool replaceOldValues, bool deleteEmptyValues) { ParameterAsserts.AssertImplementation(source); ParameterAsserts.AssertImplementation(destination); var src = (XmpMeta)source; var dest = (XmpMeta)destination; for (var it = src.GetRoot().IterateChildren(); it.HasNext();) { var sourceSchema = (XmpNode)it.Next(); // Make sure we have a destination schema node var destSchema = XmpNodeUtils.FindSchemaNode(dest.GetRoot(), sourceSchema.Name, false); var createdSchema = false; if (destSchema == null) { destSchema = new XmpNode(sourceSchema.Name, sourceSchema.Value, new PropertyOptions { IsSchemaNode = true }); dest.GetRoot().AddChild(destSchema); createdSchema = true; } // Process the source schema's children. for (var ic = sourceSchema.IterateChildren(); ic.HasNext();) { var sourceProp = (XmpNode)ic.Next(); if (doAllProperties || !Utils.IsInternalProperty(sourceSchema.Name, sourceProp.Name)) { AppendSubtree(dest, sourceProp, destSchema, replaceOldValues, deleteEmptyValues); } } if (!destSchema.HasChildren && (createdSchema || deleteEmptyValues)) { // Don't create an empty schema / remove empty schema. dest.GetRoot().RemoveChild(destSchema); } } }
/** * Sets a description. * * @param xmpMeta * @param desc * @param genericLang The name of the generic language * @param specificLang The name of the specific language */ public static void SetDescription(IXmpMeta xmpMeta, String desc, String genericLang, String specificLang) { xmpMeta.SetLocalizedText(XmpConst.NS_DC, DESCRIPTION, genericLang, specificLang, desc); }
/// <summary> /// see {@link XMPUtils#separateArrayItems(XMPMeta, String, String, String, /// PropertyOptions, boolean)} /// </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> public static void SeparateArrayItems(IXmpMeta 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", XmpError.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. int charKind = UCK_NORMAL; char ch = (char) 0; int itemEnd = 0; int endPos = catedStr.Length; while (itemEnd < endPos) { string itemValue; int itemStart; // 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 == UCK_NORMAL || charKind == UCK_QUOTE) { break; } } if (itemStart >= endPos) { break; } int nextKind; if (charKind != UCK_QUOTE) { // 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 == UCK_NORMAL || charKind == UCK_QUOTE || (charKind == UCK_COMMA && preserveCommas)) { continue; } if (charKind != UCK_SPACE) { break; } if ((itemEnd + 1) < endPos) { ch = catedStr[itemEnd + 1]; nextKind = ClassifyCharacter(ch); if (nextKind == UCK_NORMAL || nextKind == UCK_QUOTE || (nextKind == UCK_COMMA && preserveCommas)) { continue; } } // Anything left? break; // Have multiple spaces, or a space followed by a // separator. } itemValue = catedStr.Substring(itemStart, itemEnd - itemStart); } 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 = ""; for (itemEnd = itemStart; itemEnd < endPos; itemEnd++) { ch = catedStr[itemEnd]; charKind = ClassifyCharacter(ch); if (charKind != UCK_QUOTE || !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. char nextChar; if ((itemEnd + 1) < endPos) { nextChar = catedStr[itemEnd + 1]; nextKind = ClassifyCharacter(nextChar); } else { nextKind = UCK_SEMICOLON; nextChar = (char) 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.ChildrenLength; oldChild++) { if (itemValue.Equals(arrayNode.GetChild(oldChild).Value)) { foundIndex = oldChild; break; } } if (foundIndex < 0) { XmpNode newItem = new XmpNode(ARRAY_ITEM_NAME, itemValue, null); arrayNode.AddChild(newItem); } } }
/// <param name="source">The source XMP object.</param> /// <param name="destination">The destination XMP object.</param> /// <param name="doAllProperties">Do internal properties in addition to external properties.</param> /// <param name="replaceOldValues">Replace the values of existing properties.</param> /// <param name="deleteEmptyValues">Delete destination values if source property is empty.</param> /// <exception cref="XmpException">Forwards the Exceptions from the metadata processing</exception> public static void AppendProperties(IXmpMeta source, IXmpMeta destination, bool doAllProperties, bool replaceOldValues, bool deleteEmptyValues) { ParameterAsserts.AssertImplementation(source); ParameterAsserts.AssertImplementation(destination); var src = (XmpMeta)source; var dest = (XmpMeta)destination; for (var it = src.GetRoot().IterateChildren(); it.HasNext(); ) { var sourceSchema = (XmpNode)it.Next(); // Make sure we have a destination schema node var destSchema = XmpNodeUtils.FindSchemaNode(dest.GetRoot(), sourceSchema.Name, false); var createdSchema = false; if (destSchema == null) { destSchema = new XmpNode(sourceSchema.Name, sourceSchema.Value, new PropertyOptions { IsSchemaNode = true }); dest.GetRoot().AddChild(destSchema); createdSchema = true; } // Process the source schema's children. for (var ic = sourceSchema.IterateChildren(); ic.HasNext(); ) { var sourceProp = (XmpNode)ic.Next(); if (doAllProperties || !Utils.IsInternalProperty(sourceSchema.Name, sourceProp.Name)) AppendSubtree(dest, sourceProp, destSchema, replaceOldValues, deleteEmptyValues); } if (!destSchema.HasChildren && (createdSchema || deleteEmptyValues)) { // Don't create an empty schema / remove empty schema. dest.GetRoot().RemoveChild(destSchema); } } }
/// <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="XmpException"/> public static void SeparateArrayItems(IXmpMeta xmp, string schemaNs, string arrayName, string catedStr, PropertyOptions arrayOptions, bool preserveCommas) { Impl.XmpUtils.SeparateArrayItems(xmp, schemaNs, arrayName, catedStr, arrayOptions, preserveCommas); }
/** * Adds a description. * * @param xmpMeta * @param desc */ public static void AddDescription(IXmpMeta xmpMeta, String desc) { xmpMeta.AppendArrayItem(XmpConst.NS_DC, DESCRIPTION, new PropertyOptions(PropertyOptions.ARRAY_ALTERNATE), desc, null); }
/** * Adds a single author. * * @param xmpMeta * @param author */ public static void AddAuthor(IXmpMeta xmpMeta, String author) { xmpMeta.AppendArrayItem(XmpConst.NS_DC, CREATOR, new PropertyOptions(PropertyOptions.ARRAY_ORDERED), author, null); }
/// <summary>Append properties from one XMP object to another.</summary> /// <remarks> /// Append properties from one XMP object to another. /// <para />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 /// <c>doAllProperties</c> can be used to operate on all properties, external and internal. /// The flag <c>replaceOldValues</c> 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. /// <list type="bullet"> /// <item>If <c>replaceOldValues</c> is true then the processing is restricted to the top /// level properties. The processed properties from the source (according to /// <c>doAllProperties</c>) are propagated to the destination, /// replacing any existing values.Properties in the destination that are not in the source /// are left alone.</item> /// <item>If <c>replaceOldValues</c> 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.</item> /// <item>If <c>deleteEmptyValues</c> 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.</item> /// </list> /// <para />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> /// <para /><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. /// <para />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="XmpException"/> public static void AppendProperties(IXmpMeta source, IXmpMeta dest, bool doAllProperties, bool replaceOldValues, bool deleteEmptyValues = false) { Impl.XmpUtils.AppendProperties(source, dest, doAllProperties, replaceOldValues, deleteEmptyValues); }
private void ReadPdfAInfo() { byte[] metadata = null; IXmpProperty pdfaidConformance = null; IXmpProperty pdfaidPart = null; try { metadata = reader.Metadata; xmpMeta = XmpMetaParser.Parse(metadata, null); pdfaidConformance = xmpMeta.GetProperty(XmpConst.NS_PDFA_ID, "pdfaid:conformance"); pdfaidPart = xmpMeta.GetProperty(XmpConst.NS_PDFA_ID, "pdfaid:part"); } catch(Exception e) { throw new PdfAConformanceException(MessageLocalization.GetComposedMessage("only.pdfa.documents.can.be.opened.in.PdfAStamper")); } if(pdfaidConformance == null || pdfaidPart == null) { throw new PdfAConformanceException(MessageLocalization.GetComposedMessage("only.pdfa.documents.can.be.opened.in.PdfAStamper")); } switch(((IPdfAConformance)pdfIsoConformance).ConformanceLevel) { case PdfAConformanceLevel.PDF_A_1A: case PdfAConformanceLevel.PDF_A_1B: if(!"1".Equals(pdfaidPart.Value)) { throw new PdfAConformanceException(MessageLocalization.GetComposedMessage("only.pdfa.1.documents.can.be.opened.in.PdfAStamper", "1")); } break; case PdfAConformanceLevel.PDF_A_2A: case PdfAConformanceLevel.PDF_A_2B: case PdfAConformanceLevel.PDF_A_2U: if(!"2".Equals(pdfaidPart.Value)) { throw new PdfAConformanceException(MessageLocalization.GetComposedMessage("only.pdfa.1.documents.can.be.opened.in.PdfAStamper", "2")); } break; case PdfAConformanceLevel.PDF_A_3A: case PdfAConformanceLevel.PDF_A_3B: case PdfAConformanceLevel.PDF_A_3U: if(!"3".Equals(pdfaidPart.Value)) { throw new PdfAConformanceException(MessageLocalization.GetComposedMessage("only.pdfa.1.documents.can.be.opened.in.PdfAStamper", "3")); } break; } }
/// <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="XmpException"> If case of wrong options or any other serialization error. </exception> public virtual void Serialize(IXmpMeta xmp, Stream @out, SerializeOptions options) { try { _outputStream = new CountOutputStream(@out); _writer = new StreamWriter(_outputStream, Encoding.GetEncoding(options.Encoding)); _xmp = (XmpMetaImpl) xmp; _options = options; _padding = options.Padding; _writer = new StreamWriter(_outputStream, Encoding.GetEncoding(options.Encoding)); 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", XmpError.UNKNOWN); } }
/** * Adds a single publisher. * * @param xmpMeta * @param publisher */ public static void AddPublisher(IXmpMeta xmpMeta, String publisher) { xmpMeta.AppendArrayItem(XmpConst.NS_DC, PUBLISHER, new PropertyOptions(PropertyOptions.ARRAY_ORDERED), publisher, null); }
/** * Creates an XmpWriter. * @param os * @param utfEncoding * @param extraSpace * @throws IOException */ public XmpWriter(Stream os, String utfEncoding, int extraSpace) { outputStream = os; serializeOptions = new SerializeOptions(); if (UTF16BE.Equals(utfEncoding) || UTF16.Equals(utfEncoding)) serializeOptions.EncodeUtf16Be = true; else if (UTF16LE.Equals(utfEncoding)) serializeOptions.EncodeUtf16Le = true; serializeOptions.Padding = extraSpace; xmpMeta = XmpMetaFactory.Create(); xmpMeta.ObjectName = XmpConst.TAG_XMPMETA; xmpMeta.ObjectName = ""; try { xmpMeta.SetProperty(XmpConst.NS_DC, DublinCoreProperties.FORMAT, "application/pdf"); xmpMeta.SetProperty(XmpConst.NS_PDF, PdfProperties.PRODUCER, Version.GetInstance().GetVersion); } catch (XmpException) {} }
/** * Adds keywords. * * @param xmpMeta * @param keywords */ public static void SetKeywords(IXmpMeta xmpMeta, String keywords) { xmpMeta.SetProperty(XmpConst.NS_PDF, KEYWORDS, keywords); }
/// <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: /// <list type="bullet"> /// <item> Non-empty <c>schemaNS</c> and <c>propName</c> - The named property is /// removed if it is an external property, or if the /// flag <c>doAllProperties</c> option is true. It does not matter whether the /// named property is an actual property or an alias.</item> /// <item> Non-empty <c>schemaNS</c> and empty <c>propName</c> - The all external /// properties in the named schema are removed. Internal properties are also /// removed if the flag <c>doAllProperties</c> option is set. In addition, /// aliases from the named schema will be removed if the flag <c>includeAliases</c> /// option is set.</item> /// <item> Empty <c>schemaNS</c> and empty <c>propName</c> - All external properties in /// all schema are removed. Internal properties are also removed if the /// flag <c>doAllProperties</c> option is passed. Aliases are implicitly handled /// because the associated actuals are internal if the alias is.</item> /// </list> /// It is an error to pass an empty <c>schemaNS</c> and non-empty <c>propName</c>. /// </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="XmpException"/> public static void RemoveProperties(IXmpMeta xmp, string schemaNs, string propName, bool doAllProperties, bool includeAliases) { Impl.XmpUtils.RemoveProperties(xmp, schemaNs, propName, doAllProperties, includeAliases); }
/** * Adds the producer. * * @param xmpMeta * @param producer */ public static void SetProducer(IXmpMeta xmpMeta, String producer) { xmpMeta.SetProperty(XmpConst.NS_PDF, PRODUCER, producer); }
/// <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="XmpException"/> public static string CatenateArrayItems(IXmpMeta xmp, string schemaNs, string arrayName, string separator, string quotes, bool allowCommas) { return Impl.XmpUtils.CatenateArrayItems(xmp, schemaNs, arrayName, separator, quotes, allowCommas); }
/** * Adds the version. * * @param xmpMeta * @param version */ public static void SetVersion(IXmpMeta xmpMeta, String version) { xmpMeta.SetProperty(XmpConst.NS_PDF, VERSION, version); }
/// <summary> /// The initial support for WAV files mapped a legacy ID3 audio copyright /// into a new xmpDM:copyright property. /// </summary> /// <remarks> /// The initial support for WAV files mapped a legacy ID3 audio copyright /// into a new xmpDM:copyright property. This is special case code to migrate /// that into dc:rights['x-default']. The rules: /// <pre> /// 1. If there is no dc:rights array, or an empty array - /// Create one with dc:rights['x-default'] set from double linefeed and xmpDM:copyright. /// 2. If there is a dc:rights array but it has no x-default item - /// Create an x-default item as a copy of the first item then apply rule #3. /// 3. If there is a dc:rights array with an x-default item, /// Look for a double linefeed in the value. /// A. If no double linefeed, compare the x-default value to the xmpDM:copyright value. /// A1. If they match then leave the x-default value alone. /// A2. Otherwise, append a double linefeed and /// the xmpDM:copyright value to the x-default value. /// B. If there is a double linefeed, compare the trailing text to the xmpDM:copyright value. /// B1. If they match then leave the x-default value alone. /// B2. Otherwise, replace the trailing x-default text with the xmpDM:copyright value. /// 4. In all cases, delete the xmpDM:copyright property. /// </pre> /// </remarks> /// <param name="xmp">the metadata object</param> /// <param name="dmCopyright">the "dm:copyright"-property</param> private static void MigrateAudioCopyright(IXmpMeta xmp, XmpNode dmCopyright) { try { var dcSchema = XmpNodeUtils.FindSchemaNode(((XmpMeta)xmp).GetRoot(), XmpConstants.NsDC, true); var dmValue = dmCopyright.Value; var doubleLf = "\n\n"; var dcRightsArray = XmpNodeUtils.FindChildNode(dcSchema, "dc:rights", false); if (dcRightsArray == null || !dcRightsArray.HasChildren) { // 1. No dc:rights array, create from double linefeed and xmpDM:copyright. dmValue = doubleLf + dmValue; xmp.SetLocalizedText(XmpConstants.NsDC, "rights", string.Empty, XmpConstants.XDefault, dmValue, null); } else { var xdIndex = XmpNodeUtils.LookupLanguageItem(dcRightsArray, XmpConstants.XDefault); if (xdIndex < 0) { // 2. No x-default item, create from the first item. var firstValue = dcRightsArray.GetChild(1).Value; xmp.SetLocalizedText(XmpConstants.NsDC, "rights", string.Empty, XmpConstants.XDefault, firstValue, null); xdIndex = XmpNodeUtils.LookupLanguageItem(dcRightsArray, XmpConstants.XDefault); } // 3. Look for a double linefeed in the x-default value. var defaultNode = dcRightsArray.GetChild(xdIndex); var defaultValue = defaultNode.Value; var lfPos = defaultValue.IndexOf(doubleLf); if (lfPos < 0) { // 3A. No double LF, compare whole values. if (!dmValue.Equals(defaultValue)) { // 3A2. Append the xmpDM:copyright to the x-default // item. defaultNode.Value = defaultValue + doubleLf + dmValue; } } else { // 3B. Has double LF, compare the tail. if (!defaultValue.Substring (lfPos + 2).Equals(dmValue)) { // 3B2. Replace the x-default tail. defaultNode.Value = defaultValue.Substring (0, lfPos + 2 - 0) + dmValue; } } } // 4. Get rid of the xmpDM:copyright. dmCopyright.Parent.RemoveChild(dmCopyright); } catch (XmpException) { } }
/** * Sets a title. * * @param xmpMeta * @param title * @param genericLang The name of the generic language * @param specificLang The name of the specific language */ public static void SetTitle(IXmpMeta xmpMeta, String title, String genericLang, String specificLang) { xmpMeta.SetLocalizedText(XmpConst.NS_DC, TITLE, genericLang, specificLang, title); }
/** * Adds a subject. * * @param xmpMeta * @param subject */ public static void AddSubject(IXmpMeta xmpMeta, String subject) { xmpMeta.AppendArrayItem(XmpConst.NS_DC, SUBJECT, new PropertyOptions(PropertyOptions.ARRAY), subject, null); }
/// <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="XmpException">If metadata processing fails</exception> public static void RemoveProperties(IXmpMeta xmp, string schemaNs, string propName, bool doAllProperties, bool includeAliases) { ParameterAsserts.AssertImplementation(xmp); var xmpImpl = (XmpMeta)xmp; if (!string.IsNullOrEmpty(propName)) { // 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 (string.IsNullOrEmpty(schemaNs)) throw new XmpException("Property name requires schema namespace", XmpErrorCode.BadParam); var expPath = XmpPathParser.ExpandXPath(schemaNs, propName); var propNode = XmpNodeUtils.FindNode(xmpImpl.GetRoot(), expPath, false, null); if (propNode != null) { if (doAllProperties || !Utils.IsInternalProperty(expPath.GetSegment(XmpPath.StepSchema).Name, expPath.GetSegment(XmpPath.StepRootProp).Name)) { var parent = propNode.Parent; parent.RemoveChild(propNode); if (parent.Options.IsSchemaNode && !parent.HasChildren) { // remove empty schema node parent.Parent.RemoveChild(parent); } } } } else { if (!string.IsNullOrEmpty(schemaNs)) { // Remove all properties from the named schema. Optionally include // aliases, in which case // there might not be an actual schema node. // XMP_NodePtrPos schemaPos; var schemaNode = XmpNodeUtils.FindSchemaNode(xmpImpl.GetRoot(), schemaNs, false); if (schemaNode != null && 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. foreach (var info in XmpMetaFactory.SchemaRegistry.FindAliases(schemaNs)) { var path = XmpPathParser.ExpandXPath(info.Namespace, info.PropName); var actualProp = XmpNodeUtils.FindNode(xmpImpl.GetRoot(), path, false, null); if (actualProp != null) actualProp.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 (var it = xmpImpl.GetRoot().IterateChildren(); it.HasNext(); ) { var schema = (XmpNode)it.Next(); if (RemoveSchemaChildren(schema, doAllProperties)) it.Remove(); } } } }
/// <summary> /// Asserts that the xmp object is of this implemention /// (<seealso cref="XmpMetaImpl"/>). </summary> /// <param name="xmp"> the XMP object </param> /// <exception cref="XmpException"> A wrong implentaion is used. </exception> public static void AssertImplementation(IXmpMeta xmp) { if (xmp == null) { throw new XmpException("Parameter must not be null", XmpError.BADPARAM); } if (!(xmp is XmpMetaImpl)) { throw new XmpException("The XMPMeta-object is not compatible with this implementation", XmpError.BADPARAM); } }
/// <seealso cref= XMPUtils#removeProperties(XMPMeta, String, String, boolean, boolean) /// </seealso> /// <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="XmpException"> If metadata processing fails </exception> public static void RemoveProperties(IXmpMeta xmp, string schemaNs, string propName, bool doAllProperties, bool includeAliases) { ParameterAsserts.AssertImplementation(xmp); XmpMetaImpl xmpImpl = (XmpMetaImpl) xmp; if (!string.IsNullOrEmpty(propName)) { // 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 (string.IsNullOrEmpty(schemaNs)) { throw new XmpException("Property name requires schema namespace", XmpError.BADPARAM); } XmpPath expPath = XmpPathParser.ExpandXPath(schemaNs, propName); XmpNode propNode = XmpNodeUtils.FindNode(xmpImpl.Root, expPath, false, null); if (propNode != null) { if (doAllProperties || !Utils.IsInternalProperty(expPath.GetSegment((int) XmpPath.STEP_SCHEMA).Name, expPath.GetSegment((int) XmpPath.STEP_ROOT_PROP).Name)) { XmpNode parent = propNode.Parent; parent.RemoveChild(propNode); if (parent.Options.SchemaNode && !parent.HasChildren()) { // remove empty schema node parent.Parent.RemoveChild(parent); } } } } else if (!string.IsNullOrEmpty(schemaNs)) { // 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.Root, schemaNs, false); if (schemaNode != null) { if (RemoveSchemaChildren(schemaNode, doAllProperties)) { xmpImpl.Root.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. IXmpAliasInfo[] aliases = XmpMetaFactory.SchemaRegistry.FindAliases(schemaNs); for (int i = 0; i < aliases.Length; i++) { IXmpAliasInfo info = aliases[i]; XmpPath path = XmpPathParser.ExpandXPath(info.Namespace, info.PropName); XmpNode actualProp = XmpNodeUtils.FindNode(xmpImpl.Root, path, false, null); if (actualProp != null) { XmpNode parent = actualProp.Parent; 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. ArrayList schemasToRemove = new ArrayList(); for (IEnumerator it = xmpImpl.Root.IterateChildren(); it.MoveNext();) { XmpNode schema = (XmpNode) it.Current; if (schema == null) continue; if (RemoveSchemaChildren(schema, doAllProperties)) { schemasToRemove.Add(schema); } } foreach (XmpNode xmpNode in schemasToRemove) { xmpImpl.Root.Children.Remove(xmpNode); } schemasToRemove.Clear(); } }
/// <seealso cref= XMPUtils#catenateArrayItems(XMPMeta, String, String, String, String, /// boolean) /// </seealso> /// <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> public static string CatenateArrayItems(IXmpMeta xmp, string schemaNs, string arrayName, string separator, string quotes, bool allowCommas) { ParameterAsserts.AssertSchemaNs(schemaNs); ParameterAsserts.AssertArrayName(arrayName); ParameterAsserts.AssertImplementation(xmp); if (string.IsNullOrEmpty(separator)) { separator = "; "; } if (string.IsNullOrEmpty(quotes)) { quotes = "\""; } XmpMetaImpl xmpImpl = (XmpMetaImpl) xmp; // Return an empty result if the array does not exist, // hurl if it isn't the right form. XmpPath arrayPath = XmpPathParser.ExpandXPath(schemaNs, arrayName); XmpNode arrayNode = XmpNodeUtils.FindNode(xmpImpl.Root, arrayPath, false, null); if (arrayNode == null) { return ""; } if (!arrayNode.Options.Array || arrayNode.Options.ArrayAlternate) { throw new XmpException("Named property must be non-alternate array", XmpError.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 (IEnumerator it = arrayNode.IterateChildren(); it.MoveNext();) { XmpNode currItem = (XmpNode)it.Current; if (currItem == null) continue; if (currItem.Options.CompositeProperty) { throw new XmpException("Array items must be simple", XmpError.BADPARAM); } string str = ApplyQuotes(currItem.Value, openQuote, closeQuote, allowCommas); catinatedString.Append(str); if (it.MoveNext()) { catinatedString.Append(separator); } } return catinatedString.ToString(); }
/** * Adds a title. * * @param xmpMeta * @param title */ public static void AddTitle(IXmpMeta xmpMeta, String title) { xmpMeta.AppendArrayItem(XmpConst.NS_DC, TITLE, new PropertyOptions(PropertyOptions.ARRAY_ALTERNATE), title, null); }
/// <seealso cref= XMPUtils#appendProperties(XMPMeta, XMPMeta, boolean, boolean) </seealso> /// <param name="source"> The source XMP object. </param> /// <param name="destination"> The destination XMP object. </param> /// <param name="doAllProperties"> Do internal properties in addition to external properties. </param> /// <param name="replaceOldValues"> Replace the values of existing properties. </param> /// <param name="deleteEmptyValues"> Delete destination values if source property is empty. </param> /// <exception cref="XmpException"> Forwards the Exceptions from the metadata processing </exception> public static void AppendProperties(IXmpMeta source, IXmpMeta destination, bool doAllProperties, bool replaceOldValues, bool deleteEmptyValues) { ParameterAsserts.AssertImplementation(source); ParameterAsserts.AssertImplementation(destination); XmpMetaImpl src = (XmpMetaImpl) source; XmpMetaImpl dest = (XmpMetaImpl) destination; for (IEnumerator it = src.Root.IterateChildren(); it.MoveNext();) { XmpNode sourceSchema = (XmpNode) it.Current; if (sourceSchema == null) continue; // Make sure we have a destination schema node XmpNode destSchema = XmpNodeUtils.FindSchemaNode(dest.Root, sourceSchema.Name, false); bool createdSchema = false; if (destSchema == null) { PropertyOptions propertyOptions = new PropertyOptions(); propertyOptions.SchemaNode = true; destSchema = new XmpNode(sourceSchema.Name, sourceSchema.Value, propertyOptions); dest.Root.AddChild(destSchema); createdSchema = true; } // Process the source schema's children. for (IEnumerator ic = sourceSchema.IterateChildren(); ic.MoveNext();) { XmpNode sourceProp = (XmpNode) ic.Current; if (sourceProp == null) continue; if (doAllProperties || !Utils.IsInternalProperty(sourceSchema.Name, sourceProp.Name)) { AppendSubtree(dest, sourceProp, destSchema, replaceOldValues, deleteEmptyValues); } } if (!destSchema.HasChildren() && (createdSchema || deleteEmptyValues)) { // Don't create an empty schema / remove empty schema. dest.Root.RemoveChild(destSchema); } } }