mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 11:48:06 +01:00
[Image Resizer] Add option to remove metadata (#14176)
* Implements option to remove metadata (see #1928) * Add unit test * renamed settings switch, update ui text * Fix exception type, add justification for swallowing exception * Add unit test to check handling if no metadata is there in targetfile * Reordered the checkboxes as suggested by @htcfreek * Reduced size of test image
This commit is contained in:
committed by
GitHub
parent
9d9df949ef
commit
9ca32aa3ea
@@ -28,6 +28,10 @@
|
|||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
<PlatformTarget>x64</PlatformTarget>
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Remove="TestMetadataIssue1928.jpg" />
|
||||||
|
<None Remove="TestMetadataIssue1928_NoMetadata.jpg" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\ui\ImageResizerUI.csproj" />
|
<ProjectReference Include="..\ui\ImageResizerUI.csproj" />
|
||||||
@@ -53,6 +57,12 @@
|
|||||||
<Content Include="Test.png">
|
<Content Include="Test.png">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
<Content Include="TestMetadataIssue1928.jpg">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="TestMetadataIssue1928_NoMetadata.jpg">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
<Content Include="TestMetadataIssue2447.jpg">
|
<Content Include="TestMetadataIssue2447.jpg">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
using System.Windows.Media.Imaging;
|
using System.Windows.Media.Imaging;
|
||||||
|
using ImageResizer.Extensions;
|
||||||
using ImageResizer.Properties;
|
using ImageResizer.Properties;
|
||||||
using ImageResizer.Test;
|
using ImageResizer.Test;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
@@ -441,6 +442,50 @@ namespace ImageResizer.Models
|
|||||||
Assert.IsTrue(File.Exists(_directory + @"\Directory\Test (Test).png"));
|
Assert.IsTrue(File.Exists(_directory + @"\Directory\Test (Test).png"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void StripMetadata()
|
||||||
|
{
|
||||||
|
var operation = new ResizeOperation(
|
||||||
|
"TestMetadataIssue1928.jpg",
|
||||||
|
_directory,
|
||||||
|
Settings(
|
||||||
|
x =>
|
||||||
|
{
|
||||||
|
x.RemoveMetadata = true;
|
||||||
|
}));
|
||||||
|
|
||||||
|
operation.Execute();
|
||||||
|
|
||||||
|
AssertEx.Image(
|
||||||
|
_directory.File(),
|
||||||
|
image => Assert.IsNull(((BitmapMetadata)image.Frames[0].Metadata).DateTaken));
|
||||||
|
AssertEx.Image(
|
||||||
|
_directory.File(),
|
||||||
|
image => Assert.IsNotNull(((BitmapMetadata)image.Frames[0].Metadata).GetQuerySafe("System.Photo.Orientation")));
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void StripMetadataWhenNoMetadataPresent()
|
||||||
|
{
|
||||||
|
var operation = new ResizeOperation(
|
||||||
|
"TestMetadataIssue1928_NoMetadata.jpg",
|
||||||
|
_directory,
|
||||||
|
Settings(
|
||||||
|
x =>
|
||||||
|
{
|
||||||
|
x.RemoveMetadata = true;
|
||||||
|
}));
|
||||||
|
|
||||||
|
operation.Execute();
|
||||||
|
|
||||||
|
AssertEx.Image(
|
||||||
|
_directory.File(),
|
||||||
|
image => Assert.IsNull(((BitmapMetadata)image.Frames[0].Metadata).DateTaken));
|
||||||
|
AssertEx.Image(
|
||||||
|
_directory.File(),
|
||||||
|
image => Assert.IsNull(((BitmapMetadata)image.Frames[0].Metadata).GetQuerySafe("System.Photo.Orientation")));
|
||||||
|
}
|
||||||
|
|
||||||
private static Settings Settings(Action<Settings> action = null)
|
private static Settings Settings(Action<Settings> action = null)
|
||||||
{
|
{
|
||||||
var settings = new Settings()
|
var settings = new Settings()
|
||||||
|
|||||||
BIN
src/modules/imageresizer/tests/TestMetadataIssue1928.jpg
Normal file
BIN
src/modules/imageresizer/tests/TestMetadataIssue1928.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 40 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
@@ -0,0 +1,55 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Windows.Media.Imaging;
|
||||||
|
|
||||||
|
namespace ImageResizer.Extensions
|
||||||
|
{
|
||||||
|
internal static class BitmapMetadataExtension
|
||||||
|
{
|
||||||
|
public static void CopyMetadataPropertyTo(this BitmapMetadata source, BitmapMetadata target, string query)
|
||||||
|
{
|
||||||
|
if (source == null || target == null || string.IsNullOrWhiteSpace(query))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var value = source.GetQuerySafe(query);
|
||||||
|
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
target.SetQuery(query, value);
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException)
|
||||||
|
{
|
||||||
|
// InvalidOperationException is thrown if metadata object is in readonly state.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static object GetQuerySafe(this BitmapMetadata metadata, string query)
|
||||||
|
{
|
||||||
|
if (metadata == null || string.IsNullOrWhiteSpace(query))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return metadata.GetQuery(query);
|
||||||
|
}
|
||||||
|
catch (NotSupportedException)
|
||||||
|
{
|
||||||
|
// NotSupportedException is throw if the metadata entry is not preset on the target image (e.g. Orientation not set).
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ using System.Linq;
|
|||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
using System.Windows.Media.Imaging;
|
using System.Windows.Media.Imaging;
|
||||||
|
using ImageResizer.Extensions;
|
||||||
using ImageResizer.Properties;
|
using ImageResizer.Properties;
|
||||||
using ImageResizer.Utilities;
|
using ImageResizer.Utilities;
|
||||||
using Microsoft.VisualBasic.FileIO;
|
using Microsoft.VisualBasic.FileIO;
|
||||||
@@ -82,11 +83,22 @@ namespace ImageResizer.Models
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_settings.RemoveMetadata && metadata != null)
|
||||||
|
{
|
||||||
|
// strip any metadata that doesn't affect rendering
|
||||||
|
var newMetadata = new BitmapMetadata(metadata.Format);
|
||||||
|
|
||||||
|
metadata.CopyMetadataPropertyTo(newMetadata, "System.Photo.Orientation");
|
||||||
|
metadata.CopyMetadataPropertyTo(newMetadata, "System.Image.ColorSpace");
|
||||||
|
|
||||||
|
metadata = newMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
encoder.Frames.Add(
|
encoder.Frames.Add(
|
||||||
BitmapFrame.Create(
|
BitmapFrame.Create(
|
||||||
Transform(originalFrame),
|
Transform(originalFrame),
|
||||||
thumbnail: null,
|
thumbnail: null,
|
||||||
metadata, // TODO: Add an option to strip any metadata that doesn't affect rendering (issue #3)
|
metadata,
|
||||||
colorContexts: null));
|
colorContexts: null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -141,6 +141,15 @@ namespace ImageResizer.Properties {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Remove metadata that doesn't affect rendering.
|
||||||
|
/// </summary>
|
||||||
|
public static string Input_RemoveMetadata {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Input_RemoveMetadata", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to R_esize the original pictures (don't create copies).
|
/// Looks up a localized string similar to R_esize the original pictures (don't create copies).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -280,4 +280,7 @@
|
|||||||
<data name="Open_settings" xml:space="preserve">
|
<data name="Open_settings" xml:space="preserve">
|
||||||
<value>Open settings</value>
|
<value>Open settings</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Input_RemoveMetadata" xml:space="preserve">
|
||||||
|
<value>Remove metadata that doesn't affect rendering</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -30,6 +30,7 @@ namespace ImageResizer.Properties
|
|||||||
private int _selectedSizeIndex;
|
private int _selectedSizeIndex;
|
||||||
private bool _replace;
|
private bool _replace;
|
||||||
private bool _ignoreOrientation;
|
private bool _ignoreOrientation;
|
||||||
|
private bool _removeMetadata;
|
||||||
private int _jpegQualityLevel;
|
private int _jpegQualityLevel;
|
||||||
private PngInterlaceOption _pngInterlaceOption;
|
private PngInterlaceOption _pngInterlaceOption;
|
||||||
private TiffCompressOption _tiffCompressOption;
|
private TiffCompressOption _tiffCompressOption;
|
||||||
@@ -44,6 +45,7 @@ namespace ImageResizer.Properties
|
|||||||
ShrinkOnly = false;
|
ShrinkOnly = false;
|
||||||
Replace = false;
|
Replace = false;
|
||||||
IgnoreOrientation = true;
|
IgnoreOrientation = true;
|
||||||
|
RemoveMetadata = false;
|
||||||
JpegQualityLevel = 90;
|
JpegQualityLevel = 90;
|
||||||
PngInterlaceOption = System.Windows.Media.Imaging.PngInterlaceOption.Default;
|
PngInterlaceOption = System.Windows.Media.Imaging.PngInterlaceOption.Default;
|
||||||
TiffCompressOption = System.Windows.Media.Imaging.TiffCompressOption.Default;
|
TiffCompressOption = System.Windows.Media.Imaging.TiffCompressOption.Default;
|
||||||
@@ -280,6 +282,27 @@ namespace ImageResizer.Properties
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether resizing images removes any metadata that doesn't affect rendering.
|
||||||
|
/// Default is false.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Preserved Metadata:
|
||||||
|
/// System.Photo.Orientation,
|
||||||
|
/// System.Image.ColorSpace
|
||||||
|
/// </remarks>
|
||||||
|
[JsonConverter(typeof(WrappedJsonValueConverter))]
|
||||||
|
[JsonPropertyName("imageresizer_removeMetadata")]
|
||||||
|
public bool RemoveMetadata
|
||||||
|
{
|
||||||
|
get => _removeMetadata;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_removeMetadata = value;
|
||||||
|
NotifyPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[JsonConverter(typeof(WrappedJsonValueConverter))]
|
[JsonConverter(typeof(WrappedJsonValueConverter))]
|
||||||
[JsonPropertyName("imageresizer_jpegQualityLevel")]
|
[JsonPropertyName("imageresizer_jpegQualityLevel")]
|
||||||
public int JpegQualityLevel
|
public int JpegQualityLevel
|
||||||
@@ -423,6 +446,7 @@ namespace ImageResizer.Properties
|
|||||||
ShrinkOnly = jsonSettings.ShrinkOnly;
|
ShrinkOnly = jsonSettings.ShrinkOnly;
|
||||||
Replace = jsonSettings.Replace;
|
Replace = jsonSettings.Replace;
|
||||||
IgnoreOrientation = jsonSettings.IgnoreOrientation;
|
IgnoreOrientation = jsonSettings.IgnoreOrientation;
|
||||||
|
RemoveMetadata = jsonSettings.RemoveMetadata;
|
||||||
JpegQualityLevel = jsonSettings.JpegQualityLevel;
|
JpegQualityLevel = jsonSettings.JpegQualityLevel;
|
||||||
PngInterlaceOption = jsonSettings.PngInterlaceOption;
|
PngInterlaceOption = jsonSettings.PngInterlaceOption;
|
||||||
TiffCompressOption = jsonSettings.TiffCompressOption;
|
TiffCompressOption = jsonSettings.TiffCompressOption;
|
||||||
|
|||||||
@@ -122,15 +122,19 @@
|
|||||||
<CheckBox Margin="12,4,12,0"
|
<CheckBox Margin="12,4,12,0"
|
||||||
Content="{x:Static p:Resources.Input_ShrinkOnly}"
|
Content="{x:Static p:Resources.Input_ShrinkOnly}"
|
||||||
IsChecked="{Binding Settings.ShrinkOnly}"/>
|
IsChecked="{Binding Settings.ShrinkOnly}"/>
|
||||||
|
|
||||||
|
<CheckBox Margin="12,4,12,0"
|
||||||
|
Content="{x:Static p:Resources.Input_IgnoreOrientation}"
|
||||||
|
IsChecked="{Binding Settings.IgnoreOrientation}"/>
|
||||||
<!-- TODO: This option doesn't make much sense when resizing into a directory. We should swap it for an option
|
<!-- TODO: This option doesn't make much sense when resizing into a directory. We should swap it for an option
|
||||||
to overwrite any files in the directory instead (issue #88) -->
|
to overwrite any files in the directory instead (issue #88) -->
|
||||||
<CheckBox Margin="12,4,12,0"
|
<CheckBox Margin="12,4,12,0"
|
||||||
Content="{x:Static p:Resources.Input_Replace}"
|
Content="{x:Static p:Resources.Input_Replace}"
|
||||||
IsChecked="{Binding Settings.Replace}"/>
|
IsChecked="{Binding Settings.Replace}"/>
|
||||||
|
|
||||||
<CheckBox Margin="12,4,12,0"
|
<CheckBox Margin="12,4,12,0"
|
||||||
Content="{x:Static p:Resources.Input_IgnoreOrientation}"
|
Content="{x:Static p:Resources.Input_RemoveMetadata}"
|
||||||
IsChecked="{Binding Settings.IgnoreOrientation}"/>
|
IsChecked="{Binding Settings.RemoveMetadata}"/>
|
||||||
|
|
||||||
<TextBlock Grid.Column="0"
|
<TextBlock Grid.Column="0"
|
||||||
FontWeight="Bold"
|
FontWeight="Bold"
|
||||||
|
|||||||
Reference in New Issue
Block a user