Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compile the PR #65 in MDE repo in a separate library #1

Merged
merged 30 commits into from
Mar 20, 2024
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
a823185
Update readme to include credits to the original owner of the PR
Feb 26, 2024
bcc31ce
fix formatting
Feb 26, 2024
4477922
updated gitignore
Feb 26, 2024
cf9e86e
create a separate project for the plugin
Feb 26, 2024
28ab42b
Added contents of PR #65
Feb 26, 2024
9147df4
cleanup
Feb 26, 2024
6151dd4
Rename JpegSegment class to JpegSegmentPlugin
Feb 26, 2024
6a66957
Align with MDE 2.9.0-rc1 to address the change to XmpReader.JpegSegme…
Feb 26, 2024
2e2153b
Rename the .cs to JpegSegmentPlugin
Feb 26, 2024
5100e7b
Added unit tests as per the original PR
Feb 26, 2024
d003b3f
cleanup unit tests
Feb 26, 2024
7b224fc
Copy data to output folder for tests
Feb 26, 2024
9022520
Change the project output to library
Feb 26, 2024
3a69f16
switch to central packaging
Feb 27, 2024
3dde6f4
cleanup after initial testing
Feb 27, 2024
298a30f
Target the library for net6.0 and net8.0
Mar 4, 2024
197fc69
Switched the writemetadata function to expect IXmpMeta instead of XDo…
Mar 14, 2024
db11767
Encode bytes using the XmpMetaFactory.SerializeToBuffer from xmp-core…
Mar 14, 2024
ff73546
cleanup
Mar 14, 2024
36f2092
update unit test and cleanup
Mar 14, 2024
169ffc2
Refactor the code to reflect JpegXmpWritePluginMDE in namespace
Mar 14, 2024
d138e8b
Update unit tests with IXmpMeta as the xmp string
Mar 15, 2024
7488515
All unit tests passing after change to IXmpMeta as the expected xmp d…
Mar 15, 2024
72d0d95
Repurpose the local byte encoding function to add jpeg xmp preamble
Mar 15, 2024
6b8f7b2
rework breaking out of 'while' loop logic when reading bytes sequenti…
Mar 15, 2024
44725a3
Simplify getting preamble from XmpReader
Mar 18, 2024
c1f05d9
Remove unused xunit ref
Mar 18, 2024
34a3ec6
add a PrivateAssets="All" to the PackageReference
Mar 19, 2024
ec1d042
Update credits
Mar 19, 2024
4f28ed0
remove spaces
Mar 19, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.vs
bin
obj
23 changes: 23 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<Project>
<!-- Enable central package version management -->
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
</PropertyGroup>
<!-- Setup tools to be installed in all projects -->
<ItemGroup>
<!-- Built into the .NET 8 SDK -->
<!-- <GlobalPackageReference Include="Microsoft.SourceLink.AzureRepos.Git" Version="1.1.1" /> -->
</ItemGroup>
<!-- Centrally defined package versions -->
<ItemGroup>
<PackageVersion Include="JetBrains.Annotations" Version="2023.3.0" />
<PackageVersion Include="MetadataExtractor" Version="2.9.0-rc1" />
<PackageVersion Include="xunit.assert" Version="2.7.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.6.0" />
<PackageVersion Include="coverlet.collector" Version="6.0.0" />
<PackageVersion Include="xunit" Version="2.7.0" />
<PackageVersion Include="xunit.analyzers" Version="1.11.0" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.5.7" />
</ItemGroup>
</Project>
Binary file not shown.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions JpegXmpWritePluginMDE.Tests/Data/xmpWriting_XmpContent.xmp
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<x:xmpmeta xmlns:x="adobe:ns:meta/"><rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><rdf:Description rdf:about="uuid:faf5bdd5-ba3d-11da-ad31-d33d75182f1b" xmlns:xmp="http://ns.adobe.com/xap/1.0/"><xmp:CreatorTool>Microsoft Photo Gallery 16.4.3528.331</xmp:CreatorTool></rdf:Description><rdf:Description rdf:about="uuid:faf5bdd5-ba3d-11da-ad31-d33d75182f1b" xmlns:MP="http://ns.microsoft.com/photo/1.2/"><MP:RegionInfo><rdf:Description xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><MPRI:Regions xmlns:MPRI="http://ns.microsoft.com/photo/1.2/t/RegionInfo#"><rdf:Bag xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><rdf:li><rdf:Description xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><MPReg:Rectangle xmlns:MPReg="http://ns.microsoft.com/photo/1.2/t/Region#">0.028800, 0.158273, 0.152000, 0.230216</MPReg:Rectangle><MPReg:PersonDisplayName xmlns:MPReg="http://ns.microsoft.com/photo/1.2/t/Region#">Sean Connery</MPReg:PersonDisplayName></rdf:Description>
</rdf:li><rdf:li><rdf:Description xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><MPReg:Rectangle xmlns:MPReg="http://ns.microsoft.com/photo/1.2/t/Region#">0.257600, 0.141487, 0.128000, 0.191847</MPReg:Rectangle><MPReg:PersonDisplayName xmlns:MPReg="http://ns.microsoft.com/photo/1.2/t/Region#">Roger Moore</MPReg:PersonDisplayName></rdf:Description>
</rdf:li><rdf:li><rdf:Description xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><MPReg:Rectangle xmlns:MPReg="http://ns.microsoft.com/photo/1.2/t/Region#">0.433600, 0.184652, 0.128000, 0.191847</MPReg:Rectangle><MPReg:PersonDisplayName xmlns:MPReg="http://ns.microsoft.com/photo/1.2/t/Region#">Daniel Craig</MPReg:PersonDisplayName></rdf:Description>
</rdf:li><rdf:li><rdf:Description xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><MPReg:Rectangle xmlns:MPReg="http://ns.microsoft.com/photo/1.2/t/Region#">0.617600, 0.194245, 0.120000, 0.179856</MPReg:Rectangle><MPReg:PersonDisplayName xmlns:MPReg="http://ns.microsoft.com/photo/1.2/t/Region#">Timothy Dalton</MPReg:PersonDisplayName></rdf:Description>
</rdf:li><rdf:li><rdf:Description xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><MPReg:Rectangle xmlns:MPReg="http://ns.microsoft.com/photo/1.2/t/Region#">0.825600, 0.206235, 0.136000, 0.203837</MPReg:Rectangle><MPReg:PersonDisplayName xmlns:MPReg="http://ns.microsoft.com/photo/1.2/t/Region#">Pierce Brosnan</MPReg:PersonDisplayName></rdf:Description>
</rdf:li></rdf:Bag>
</MPRI:Regions></rdf:Description>
</MP:RegionInfo></rdf:Description></rdf:RDF></x:xmpmeta>
98 changes: 98 additions & 0 deletions JpegXmpWritePluginMDE.Tests/Formats/Jpeg/JpegFragmentTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#region License
//
// Copyright 2002-2017 Drew Noakes
// Ported from Java to C# by Yakov Danilov for Imazen LLC in 2014
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// More information about this project is available at:
//
// https://github.com/drewnoakes/metadata-extractor-dotnet
// https://drewnoakes.com/code/exif/
//
#endregion

using JpegXmpWritePluginMDE.MetadataExtractor.Formats.Jpeg;
using MetadataExtractor.Formats.Jpeg;
using MetadataExtractor.IO;
using Xunit;

namespace JpegXmpWritePluginMDE.Tests.Formats.Jpeg
{
/// <summary>Unit tests for <see cref="JpegFragment"/>.</summary>
/// <author>Michael Osthege</author>
public sealed class JpegFragmentTest
{

[Fact]
public void TestSplitSingleFragment()
{
// The test file contains exactly one App1 XMP segment (marker, size, payload)
string pathApp1 = "Data/xmpWriting_MicrosoftXmp.app1";

List<JpegFragment> fragments;
using (var stream = TestDataUtil.OpenRead(pathApp1))
fragments = JpegFragmentWriter.SplitFragments(new SequentialStreamReader(stream));
byte[] fileBytes = TestDataUtil.GetBytes(pathApp1);

Assert.Single(fragments);
Assert.True(fragments.First().Bytes.SequenceEqual(fileBytes));
Assert.NotNull(fragments.First().Segment);
Assert.Equal(JpegSegmentType.App1, fragments.First().Segment.Type);
}

[Fact]
public void TestFindsFragment()
{
// The file is an image that contains an App1 Xmp segment
string pathJpeg = "Data/xmpWriting_PictureWithMicrosoftXmp.jpg";
string pathApp1 = "Data/xmpWriting_MicrosoftXmp.app1";

List<JpegFragment> fragments;
using (var stream = TestDataUtil.OpenRead(pathJpeg))
fragments = JpegFragmentWriter.SplitFragments(new SequentialStreamReader(stream));
byte[] xmpFragmentBytes = TestDataUtil.GetBytes(pathApp1);

bool foundXmpFragment = false;
foreach (var fragment in fragments)
{
if (fragment.Bytes.Length == xmpFragmentBytes.Length)
{
Assert.True(fragment.Bytes.SequenceEqual(xmpFragmentBytes));
foundXmpFragment = true;
}
}
Assert.True(foundXmpFragment, "The Xmp App1 fragment was not found correctly.");
}

[Fact]
public void TestSplitConcatenation()
{
// The file is an image that contains an App1 Xmp segment
string pathJpeg = "Data/xmpWriting_PictureWithMicrosoftXmp.jpg";

List<JpegFragment> fragments;
using (var stream = TestDataUtil.OpenRead(pathJpeg))
fragments = JpegFragmentWriter.SplitFragments(new SequentialStreamReader(stream));
byte[] original = File.ReadAllBytes(pathJpeg);

int nRead = fragments.Select(f => f.Bytes.Length).Sum();
byte[] joined = JpegFragmentWriter.JoinFragments(fragments).ToArray();

Assert.Equal(original.Length, nRead);
Assert.Equal(original.Length, joined.Length);
Assert.True(original.SequenceEqual(joined));
}

}
}
108 changes: 108 additions & 0 deletions JpegXmpWritePluginMDE.Tests/Formats/Jpeg/JpegMetadataWriterTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#region License
//
// Copyright 2002-2017 Drew Noakes
// Ported from Java to C# by Yakov Danilov for Imazen LLC in 2014
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// More information about this project is available at:
//
// https://github.com/drewnoakes/metadata-extractor-dotnet
// https://drewnoakes.com/code/exif/
//
#endregion
using JpegXmpWritePluginMDE.MetadataExtractor.Formats.Jpeg;
using MetadataExtractor.Formats.Jpeg;
using MetadataExtractor.IO;
using System.Xml.Linq;
using XmpCore;
using Xunit;

namespace JpegXmpWritePluginMDE.Tests.Formats.Jpeg
{
/// <summary>Unit tests for <see cref="JpegMetadataWriter"/>.</summary>
/// <author>Michael Osthege</author>
public sealed class JpegMetadataWriterTest
{
/// <summary>
/// This test is very similar to <see cref="MetadataExtractor.Tests.Formats.Xmp.XmpWriterTest.TestUpdateFragments_Replace"/>
/// but acts on one abstraction level higher, using a collection of metadata objects.
/// It tests if the <see cref="JpegMetadataWriter.UpdateJpegFragments()"/> correctly uses a
/// compatible metadata writer.
/// </summary>
[Fact]
public void TestUpdateJpegFragments_Replace()
{
// substitute the original with re-encoded Xmp content
List<JpegFragment> originalFragments = null;
using (var stream = TestDataUtil.OpenRead("Data/xmpWriting_PictureWithMicrosoftXmp.jpg"))
originalFragments = JpegFragmentWriter.SplitFragments(new SequentialStreamReader(stream));
IXmpMeta xmp = XmpMetaFactory.ParseFromString(File.ReadAllText("Data/xmpWriting_XmpContent.xmp"));
byte[] originalApp1 = File.ReadAllBytes("Data/xmpWriting_MicrosoftXmp.app1");
byte[] expectedApp1 = File.ReadAllBytes("Data/xmpWriting_MicrosoftXmpReencoded.app1");

var metadata_objects = new object[] { xmp };
var updatedFragments = JpegMetadataWriter.UpdateJpegFragments(originalFragments, metadata_objects);

Assert.Equal(originalFragments.Count, updatedFragments.Count);
// Check that only the App1 Xmp fragment is modified
for (int i = 0; i < originalFragments.Count; i++)
{
var ofrag = originalFragments[i];
var ufrag = updatedFragments[i];

if (ofrag.Segment?.Type == JpegSegmentType.App1 && ofrag.Bytes.SequenceEqual(originalApp1))
{
// If this fragment is the original Xmp fragment, we expect the updated fragment
Assert.True(ufrag.Bytes.SequenceEqual(expectedApp1));
}
else
{
// In all other cases, the fragments must remain identical
Assert.True(ufrag.Bytes.SequenceEqual(ofrag.Bytes));
}
}
}

[Fact]
public void TestUpdateJpegFragments_FailsOnUnknownMetadataObject()
{
// substitute the original with re-encoded Xmp content
List<JpegFragment> originalFragments = null;
using (var stream = TestDataUtil.OpenRead("Data/xmpWriting_PictureWithMicrosoftXmp.jpg"))
originalFragments = JpegFragmentWriter.SplitFragments(new SequentialStreamReader(stream));
IXmpMeta xmp = XmpMetaFactory.ParseFromString(File.ReadAllText("Data/xmpWriting_XmpContent.xmp"));

Assert.Throws<NotImplementedException>(delegate
{
var metadata_objects = new object[] { xmp, "This is not a supported metadata object." };
var updatedFragments = JpegMetadataWriter.UpdateJpegFragments(originalFragments, metadata_objects);
});
}

[Fact]
public void TestWriteJpegMetadata()
{
var originalStream = TestDataUtil.OpenRead("Data/xmpWriting_PictureWithMicrosoftXmp.jpg");
IXmpMeta xmp = XmpMetaFactory.ParseFromString(File.ReadAllText("Data/xmpWriting_XmpContent.xmp"));
byte[] expectedResult = TestDataUtil.GetBytes("Data/xmpWriting_PictureWithMicrosoftXmpReencoded.jpg");

var metadata_objects = new object[] { xmp };
var updatedStream = JpegMetadataWriter.WriteMetadata(originalStream, metadata_objects);

var actualResult = updatedStream.ToArray();

Assert.True(actualResult.SequenceEqual(expectedResult));
}
}
}
61 changes: 61 additions & 0 deletions JpegXmpWritePluginMDE.Tests/Formats/Jpeg/JpegSegmentTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#region License
//
// Copyright 2002-2017 Drew Noakes
// Ported from Java to C# by Yakov Danilov for Imazen LLC in 2014
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// More information about this project is available at:
//
// https://github.com/drewnoakes/metadata-extractor-dotnet
// https://drewnoakes.com/code/exif/
//
#endregion

using System.Collections.Generic;
using System.IO;
using System.Linq;
using JpegXmpWritePluginMDE.MetadataExtractor.Formats.Jpeg;
using MetadataExtractor.Formats.Jpeg;
using MetadataExtractor.IO;
using Xunit;

namespace JpegXmpWritePluginMDE.Tests.Formats.Jpeg
{
/// <summary>Unit tests for <see cref="JpegSegment"/>.</summary>
/// <author>Michael Osthege</author>
public sealed class JpegSegmentTest
{
[Fact]
public void TestEncodeSegmentLength()
{
// we are just interested in the payload length
int expected = 42802 - 2;
// the encoded value is 42802 because it includes the length mark
byte[] encoded = new byte[] { 0xA7, 0x32 };
int decoded = JpegSegmentPlugin.DecodePayloadLength(encoded[0], encoded[1]);

Assert.Equal(expected, decoded);
}

[Fact]
public void TestDecodeSegmentLength()
{
int decoded = 42802 - 2;
byte[] expected = new byte[] { 0xA7, 0x32 };
byte[] encoded = JpegSegmentPlugin.EncodePayloadLength(decoded);

Assert.True(encoded.SequenceEqual(expected));
}
}
}
Loading