Ejemplo n.º 1
0
        public static string GetExtendedXmpGuid(XmpPacket xmpPacket)
        {
            if (xmpPacket is null)
            {
                ExceptionUtil.ThrowArgumentNullException(nameof(xmpPacket));
            }

            string extendedXmpGuid = null;

            XElement rdfElement = xmpPacket.Document.Descendants(RdfContainerElementName).First();

            XObject hasExtendedXmpObject = FindHasExtendedXmpObject(rdfElement);

            if (hasExtendedXmpObject != null)
            {
                if (hasExtendedXmpObject is XAttribute attribute)
                {
                    extendedXmpGuid = attribute.Value;
                }
                else if (hasExtendedXmpObject is XElement element)
                {
                    extendedXmpGuid = element.Value;
                }
            }

            return(extendedXmpGuid);
        }
        public JpegXmpPackets GetXmpPackets()
        {
            if (!fileParsed)
            {
                ParseFile();
                fileParsed = true;
            }

            if (mainXmpPacket == null)
            {
                // The file does not contain any XMP data.
                return(null);
            }

            if (mergedXmpPacket == null)
            {
                if (extendedXmpPacket != null)
                {
                    mergedXmpPacket = XmpUtil.MergeExtendedXmp(mainXmpPacket, extendedXmpPacket);
                }
                else
                {
                    mergedXmpPacket = mainXmpPacket.Clone();
                }
            }

            return(new JpegXmpPackets(mainXmpPacket, extendedXmpPacket, mergedXmpPacket));
        }
        private void AssembleExtendedXmpChunks()
        {
            if (extendedXmpChunks.TryGetValue(extendedXmpGuid, out ExtendedXmp value))
            {
                if (value.TotalLength > int.MaxValue)
                {
                    ExceptionUtil.ThrowInvalidOperationException("The extended XMP packet is larger than 2GB.");
                }

                using (MemoryStream memoryStream = new MemoryStream((int)value.TotalLength))
                {
                    IReadOnlyList <ExtendedXmpContent> chunks = value.GetChunks();

                    uint dstOffset = 0;

                    foreach (ExtendedXmpContent item in chunks.OrderBy(i => i.Offset))
                    {
                        if (item.Length != value.TotalLength)
                        {
                            ExceptionUtil.ThrowInvalidOperationException("The extended XMP chunk length does not equal the total packet length.");
                        }

                        if (item.Offset == dstOffset)
                        {
                            byte[] data = item.GetDataReadOnly();

                            memoryStream.Write(data, 0, data.Length);

                            dstOffset += (uint)data.Length;
                        }
                        else
                        {
                            ExceptionUtil.ThrowInvalidOperationException($"Unexpected offset: { item.Offset }, expected { dstOffset }");
                        }
                    }

                    memoryStream.Position = 0;
                    extendedXmpPacket     = new XmpPacket(memoryStream);
                }
            }
        }
Ejemplo n.º 4
0
        public static XmpPacket MergeExtendedXmp(XmpPacket mainXmp, XmpPacket extendedXmp)
        {
            if (mainXmp is null)
            {
                ExceptionUtil.ThrowArgumentNullException(nameof(mainXmp));
            }

            if (extendedXmp is null)
            {
                ExceptionUtil.ThrowArgumentNullException(nameof(extendedXmp));
            }

            XDocument mergedDocument   = new XDocument(mainXmp.Document);
            XElement  mergedRdfElement = mergedDocument.Descendants(RdfContainerElementName).First();

            // Remove the xmpNote:HasExtendedXMP element.
            XObject hasExtendedXmpObject = FindHasExtendedXmpObject(mergedRdfElement);

            if (hasExtendedXmpObject != null)
            {
                if (ShouldRemoveExtendedXmpParent(hasExtendedXmpObject))
                {
                    hasExtendedXmpObject.Parent.Remove();
                }
                else
                {
                    MaybeRemoveXmpNoteNamespace(hasExtendedXmpObject.Parent);
                    if (hasExtendedXmpObject is XAttribute attribute)
                    {
                        attribute.Remove();
                    }
                    else if (hasExtendedXmpObject is XElement element)
                    {
                        element.Remove();
                    }
                }
            }

            // Add all of the rdf:Description elements in the extended XMP packet to the merged packet.
            //
            // Section 7.4 of the XMP Specification Part 1 allows a single rdf:RDF element to contain multiple
            // rdf:Description elements, with the XMP properties arbitrarily split between them.
            //
            // The XMP Specification Part 1 can be found at:
            // https://wwwimages2.adobe.com/content/dam/acom/en/devnet/xmp/pdfs/XMP%20SDK%20Release%20cc-2016-08/XMPSpecificationPart1.pdf
            //
            // The relevant portion of section 7.4 is quoted below:
            //
            // A single XMP packet shall be serialized using a single rdf:RDF XML element.
            // The rdf:RDF element content shall consist of only zero or more rdf:Description elements.
            //
            // The element content of top-level rdf:Description elements shall consist of zero or more XML elements for XMP properties.
            // XMP properties may be arbitrarily apportioned among the rdf:Description elements.

            XElement extendedXmpRdfElement = extendedXmp.Document.Descendants(RdfContainerElementName).First();

            foreach (XElement item in extendedXmpRdfElement.Elements(RdfDescriptionElementName))
            {
                mergedRdfElement.Add(item);
            }

            return(new XmpPacket(mergedDocument));
        }
        private void GatherXmpDataFromFile(EndianBinaryReader reader)
        {
            ushort marker = reader.ReadUInt16();

            // Check the file signature.
            if (marker == JpegMarkers.StartOfImage)
            {
                while (reader.Position < reader.Length)
                {
                    marker = reader.ReadUInt16();
                    if (marker == 0xFFFF)
                    {
                        // Skip the first padding byte and read the marker again.
                        reader.Position++;
                        continue;
                    }

                    if (marker == JpegMarkers.StartOfScan || marker == JpegMarkers.EndOfImage)
                    {
                        // The application data segments always come before these markers.
                        break;
                    }

                    ushort segmentLength = reader.ReadUInt16();

                    if (segmentLength < 2)
                    {
                        ExceptionUtil.ThrowFormatException($"JPEG segment length must be in the range of [2, 65535], actual value: { segmentLength }");
                    }

                    // The segment length field includes its own length in the total.
                    segmentLength -= sizeof(ushort);

                    if (marker == JpegMarkers.App1)
                    {
                        if (mainXmpPacket == null && segmentLength >= MainXmpSignatureLength)
                        {
                            string sig = reader.ReadAsciiString(MainXmpSignatureLength);
                            if (sig.Equals(MainXmpSignature, StringComparison.Ordinal))
                            {
                                byte[] xmpPacketBytes = reader.ReadBytes(segmentLength - MainXmpSignatureLength);

                                mainXmpPacket = new XmpPacket(xmpPacketBytes);

                                extendedXmpGuid = XmpUtil.GetExtendedXmpGuid(mainXmpPacket);
                                continue;
                            }
                            else
                            {
                                reader.Position -= MainXmpSignatureLength;
                            }
                        }

                        if (segmentLength >= ExtendedXmpPrefixLength)
                        {
                            string sig = reader.ReadAsciiString(ExtendedXmpSignatureLength);
                            if (sig.Equals(ExtendedXmpSignature, StringComparison.Ordinal))
                            {
                                string guid   = reader.ReadAsciiString(ExtendedXmpGUIDLength);
                                uint   length = reader.ReadUInt32();
                                uint   offset = reader.ReadUInt32();

                                byte[] data = reader.ReadBytes(segmentLength - ExtendedXmpPrefixLength);

                                if (extendedXmpChunks.TryGetValue(guid, out ExtendedXmp value))
                                {
                                    value.AddChunk(data, offset, length);
                                }
                                else
                                {
                                    extendedXmpChunks.Add(guid, new ExtendedXmp(data, offset, length));
                                }

                                continue;
                            }
                            else
                            {
                                segmentLength -= ExtendedXmpSignatureLength;
                            }
                        }
                    }

                    reader.Position += segmentLength;
                }
            }
        }
 private XmpPacket(XmpPacket cloneMe)
 {
     Document      = new XDocument(cloneMe.Document);
     LengthInBytes = cloneMe.LengthInBytes;
 }