Compare commits

...

10 Commits

Author SHA1 Message Date
944480b8cb Increment refile version number. 2024-08-30 19:57:11 +03:00
6a1d9b13fa Fix sorting. 2024-08-30 19:56:42 +03:00
428c94d0af Fix URL. 2024-08-30 19:20:19 +03:00
7fc92d1e66 Quick update. 2024-08-30 19:17:44 +03:00
6d192adce7 Add a new README for refile. 2024-08-30 17:33:45 +03:00
75fe42a68d Create the refile package as tool. 2024-08-30 17:30:46 +03:00
868bc9a3cf Add package description. 2024-08-30 17:13:54 +03:00
dd17462e9e Finish NuGet package. 2024-08-30 17:11:40 +03:00
63b85ecfad Rename "ReMime.Cli" to ReMime.ReFile. 2024-08-30 17:01:33 +03:00
bf953ceb32 Update readme and add license. 2024-08-30 15:38:14 +03:00
18 changed files with 321 additions and 126 deletions

21
LICENSE.md Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 ReFuel & H. Utku Maden
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -12,11 +12,12 @@ Platform Caveats
* On Linux, not all `/etc/mime.types` syntax is supported.
* None of this was written with MacOS in mind. But maybe it'll work?
Refer to `ReMime.Cli` as an example of how to use the library. Refer to in line
Refer to `ReMime.ReFile` as an example of how to use the library. Refer to in line
documentation and the given default resolvers as an example resolver to
implementations.
Contributing
------------
Feel free to contribute your own file type resolvers and bug fixes. The more
file types that can be detected accurately, the better.
file types that can be detected accurately, the better. Currently the
repository is available at https://git.mixedup.dev/ReFuel/ReMime. Accepting [email patches](<mailto:sht7ntgni@mozmail.com>).

View File

@@ -1,17 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\ReMime\ReMime.csproj" />
</ItemGroup>
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
<SelfContained>true</SelfContained>
<PublishSingleFile>true</PublishSingleFile>
<AssemblyName>refile</AssemblyName>
</PropertyGroup>
</Project>

View File

@@ -2,18 +2,20 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using Microsoft.VisualBasic;
namespace ReMime.Cli
{
public static class Program
{
private const string USAGE = "remime [-r] file/directory/-...\n" +
"remime --help for more help.";
private const string USAGE = "refile [-r] file/directory/-...\n" +
"refile --help for more help.";
private const string HELP =
"ReMime Command Line Tool - Determine file Media Type\n" +
"\n" +
" remime [-r] file/directory/-...\n" +
" refile [-r] file/directory/-...\n" +
"\n" +
" file infer a file\n"+
" directory infer files in directory. Requires -r\n"+
@@ -47,7 +49,10 @@ namespace ReMime.Cli
[DoesNotReturn]
private static void ListTypes()
{
foreach (MediaType type in MediaTypeResolver.KnownTypes)
var list = MediaTypeResolver.KnownTypes.ToList();
list.Sort((a,b) => StringComparer.InvariantCulture.Compare(a.FullType, b.FullType));
foreach (MediaType type in list)
{
Console.WriteLine("{0}\t{1}", type.FullTypeNoParameters, string.Join(' ', type.Extensions));
}

41
ReMime.ReFile/README.md Normal file
View File

@@ -0,0 +1,41 @@
ReFile - Simple Tool demonstrating ReMime
=========================================
```
ReMime Command Line Tool - Determine file Media Type
refile [-r] file/directory/-...
file infer a file
directory infer files in directory. Requires -r
- infer from standard input.
-r search files and folders recursively.
-a include hidden files.
-v verbose mode, use full paths.
--list list known mime types. Will ignore files.
--help show this help text.
```
ReMime - Simple Media Type Resolution
=====================================
ReMime is a very humble library that can identify IANA media types of file
from their file extension and its content. While being fully extensible
with your own resolvers, ReMime will also refer to your operating system's
file type database when resolving files.
Platform Caveats
----------------
* On Windows, the default resolver assumes your application has read access to
the registry.
* On Linux, not all `/etc/mime.types` syntax is supported.
* None of this was written with MacOS in mind. But maybe it'll work?
Refer to `ReMime.ReFile` as an example of how to use the library. Refer to in line
documentation and the given default resolvers as an example resolver to
implementations.
Contributing
------------
Feel free to contribute your own file type resolvers and bug fixes. The more
file types that can be detected accurately, the better. Currently the
repository is available at https://git.mixedup.dev/ReFuel/ReMime. Accepting [email patches](<mailto:sht7ntgni@mozmail.com>).

View File

@@ -0,0 +1,43 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
<AssemblyName>refile</AssemblyName>
<!--NuGet-->
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<PackageId>ReFuel.ReMime.ReFile</PackageId>
<Version>0.1.2</Version>
<Authors>H. Utku Maden</Authors>
<Company>ReFuel</Company>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageLicenseFile>LICENSE.md</PackageLicenseFile>
<PackageIcon>images\icon.png</PackageIcon>
<PackageProjectUrl>https://refuel.mixedup.dev/docs/ReFile.html</PackageProjectUrl>
<RepositoryUrl>https://git.mixedup.dev/ReFuel/ReMime</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>detection; detector; type; file; mime; mime-type; media; media-type; analysis; tool; refile</PackageTags>
<PackageDescription>
ReMime is a very humble library that can identify IANA media types of file
from their file extension and its content. While being fully extensible
with your own resolvers, ReMime will also refer to your operating system's
file type database when resolving files.
This is an example project a tool that will resolve the media types of the
given list of files and directories.
</PackageDescription>
<PackageType>DotnetTool</PackageType>
<PackAsTool>true</PackAsTool>
<ToolCommandName>refile</ToolCommandName>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ReFuel.ReMime" Version="0.1.1" />
<Content Include="README.md" Pack="true" PackagePath="/" />
<Content Include="../LICENSE.md" Pack="true" PackagePath="/" />
<None Include="../remime_favicon.png" Pack="true" PackagePath="images\icon.png"/>
</ItemGroup>
</Project>

View File

@@ -3,21 +3,9 @@ using ReMime.Platform;
namespace ReMime.Tests
{
public abstract class MediaTypesByExtension<T> where T : IMediaTypeResolver, new()
[TestClass]
public class MediaTypesByExtension
{
private T CIT;
protected MediaTypesByExtension()
{
Unsafe.SkipInit(out CIT);
}
[TestInitialize]
public virtual void Initialize()
{
CIT = new T();
}
readonly (string extension, string type)[] ExampleMimeTypes = new (string, string)[] {
("png", "image/png"),
("gif", "image/gif"),
@@ -34,44 +22,10 @@ namespace ReMime.Tests
{
foreach (var(ext, type) in ExampleMimeTypes)
{
Assert.IsTrue(CIT.TryResolve(ext, out MediaType? result));
Assert.IsTrue(MediaTypeResolver.TryResolve(ext, out MediaType? result));
Assert.AreEqual(result!.FullType, type);
Assert.IsTrue(result.Extensions.Contains(ext));
}
}
}
[TestClass]
public class UnixMediaTypes : MediaTypesByExtension<UnixMediaTypeResolver>
{
[TestInitialize]
public override void Initialize()
{
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsFreeBSD())
{
base.Initialize();
}
else
{
Assert.Inconclusive("Cannot test this in this platform.");
}
}
}
[TestClass]
public class Win32MediaTypes : MediaTypesByExtension<Win32MediaTypeResolver>
{
[TestInitialize]
public override void Initialize()
{
if (OperatingSystem.IsWindows())
{
base.Initialize();
}
else
{
Assert.Inconclusive("Cannot test this in this platform.");
}
}
}
}

View File

@@ -7,16 +7,13 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReMime", "ReMime\ReMime.csp
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReMime.Tests", "ReMime.Tests\ReMime.Tests.csproj", "{FEEB5BAD-3B18-4A88-A212-32EC9DA93BDE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReMime.Cli", "ReMime.Cli\ReMime.Cli.csproj", "{51AB44A2-D4EB-4CC8-BE4E-EF1912350629}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReMime.ReFile", "ReMime.ReFile\ReMime.ReFile.csproj", "{BC2655E6-88CF-47EB-AE1C-7B74325B7FEC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{05FAB3CF-78AF-4D34-97D1-C3AB24D4C59F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{05FAB3CF-78AF-4D34-97D1-C3AB24D4C59F}.Debug|Any CPU.Build.0 = Debug|Any CPU
@@ -26,9 +23,12 @@ Global
{FEEB5BAD-3B18-4A88-A212-32EC9DA93BDE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FEEB5BAD-3B18-4A88-A212-32EC9DA93BDE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FEEB5BAD-3B18-4A88-A212-32EC9DA93BDE}.Release|Any CPU.Build.0 = Release|Any CPU
{51AB44A2-D4EB-4CC8-BE4E-EF1912350629}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{51AB44A2-D4EB-4CC8-BE4E-EF1912350629}.Debug|Any CPU.Build.0 = Debug|Any CPU
{51AB44A2-D4EB-4CC8-BE4E-EF1912350629}.Release|Any CPU.ActiveCfg = Release|Any CPU
{51AB44A2-D4EB-4CC8-BE4E-EF1912350629}.Release|Any CPU.Build.0 = Release|Any CPU
{BC2655E6-88CF-47EB-AE1C-7B74325B7FEC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BC2655E6-88CF-47EB-AE1C-7B74325B7FEC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BC2655E6-88CF-47EB-AE1C-7B74325B7FEC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BC2655E6-88CF-47EB-AE1C-7B74325B7FEC}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,18 @@
using System.Collections.Generic;
namespace ReMime.ContentResolvers
{
public interface IMagicValueResolver
{
void AddMagicValue(MagicValueMediaType value);
void AddMagicValues(IEnumerable<MagicValueMediaType> values)
{
foreach (MagicValueMediaType value in values)
{
AddMagicValue(value);
}
}
}
}

View File

@@ -0,0 +1,86 @@
using System;
namespace ReMime.ContentResolvers
{
/* You've heard of regular expressions, now prepare for magical expressions :sparkles:. */
/// <summary>
/// Bit pattern detecting state machine inspired by text regular expressions.
/// </summary>
public class MagEx
{
/**
* 0 1 2 3 4 5 6 7 8 9 a b c d e f 4-bit patterns to match.
* l h Single bit pattern.
* * Any bit pattern.
* ? Any 4-bit pattern.
* 'pattern' ASCII pattern with no terminator. Implies @.
* @ Align to 8-bits.
* % Align to 4-bits.
*/
public string Pattern { get; }
public MagEx(string pattern)
{
Pattern = pattern;
}
public bool Match(ReadOnlySpan<byte> bytes)
{
byte current;
int needle;
int haystack;
int bits;
int pi = 0;
ReadOnlySpan<char> ascii = ReadOnlySpan<char>.Empty;
for (int i = 0; i < bytes.Length; i++)
{
current = bytes[i];
bits = 8;
while (bits > 0)
{
char pat = Pattern[pi];
switch (pat)
{
case '0': case '1': case '2': case '3':
case '4': case '5': case '6': case '7':
case '8': case '9': case 'a': case 'A':
case 'b': case 'B': case 'c': case 'C':
case 'd': case 'D': case 'e': case 'E':
case 'f': case 'F':
haystack = current & 0xF;
current >>= 4;
bits -= 4;
if (pat >= '0' && pat <= '9')
{
needle = pat - '0';
}
else if (pat >= 'a' && pat <= 'f')
{
needle = pat - 'a';
}
else
{
needle = pat - 'A';
}
if (haystack == needle)
{
pi++;
}
else
{
}
break;
}
}
}
return false;
}
}
}

View File

@@ -6,30 +6,28 @@ using System.Linq;
namespace ReMime.ContentResolvers
{
public record MagicValueMediaType(MediaType MediaType, MagicValue[] MagicValues, string[] Extensions);
public record MagicValueMediaType(MediaType MediaType, MagicValue[] MagicValues)
{
public IReadOnlyCollection<string> Extensions { get; } = MediaType.Extensions;
}
public class MagicContentResolver : IMediaContentResolver
public class MagicContentResolver : IMediaContentResolver, IMagicValueResolver
{
private readonly List<MediaType> _mediaTypes = new List<MediaType>();
private readonly Dictionary<string, MediaType> _extensions = new Dictionary<string, MediaType>();
private readonly Tree _tree = new Tree();
private int _maxBytes = 0;
public MagicContentResolver(IEnumerable<MagicValueMediaType> values) : this()
private MagicContentResolver()
{
AddMagicValues(values);
}
public MagicContentResolver()
{
List<MagicValueDatabaseEntry> entries;
IEnumerable<MagicValueMediaType> entries;
using (Stream str = typeof(MagicContentResolver).Assembly.GetManifestResourceStream("ReMime.ContentResolvers.database.jsonc")!)
{
entries = MagicValueDatabaseEntry.GetEntries(str);
}
AddMagicValues(entries.Select(x => (MagicValueMediaType)x));
AddMagicValues(entries);
}
public IReadOnlyCollection<MediaType> MediaTypes => _mediaTypes.AsReadOnly();
@@ -86,6 +84,8 @@ namespace ReMime.ContentResolvers
return _extensions.TryGetValue(extension, out mediaType);
}
public static MagicContentResolver Instance { get; } = new MagicContentResolver();
private class Tree
{
public MagicValueMediaType? Node { get; private set; }

View File

@@ -7,7 +7,6 @@ using System.Text.Json.Serialization;
namespace ReMime.ContentResolvers
{
[JsonSerializable(typeof(MagicValueDatabaseEntry))]
public class MagicValueDatabaseEntry
{
[JsonPropertyName("type")]
@@ -19,24 +18,24 @@ namespace ReMime.ContentResolvers
[JsonPropertyName("extensions")]
public List<string> Extensions { get; set; } = new List<string>();
public static List<MagicValueDatabaseEntry> GetEntries(Stream str)
public static IEnumerable<MagicValueMediaType> GetEntries(Stream str)
{
return JsonSerializer.Deserialize<List<MagicValueDatabaseEntry>>(str, new JsonSerializerOptions()
{
AllowTrailingCommas = true,
ReadCommentHandling = JsonCommentHandling.Skip
}) ?? throw new Exception();
})?.Select(x => (MagicValueMediaType)x)
?? throw new Exception();
}
public static explicit operator MagicValueMediaType(MagicValueDatabaseEntry entry)
{
return new MagicValueMediaType(
new MediaType(entry.Type),
new MediaType(entry.Type, entry.Extensions),
entry.Magic.Select(x => (MagicValue.TryParse(x, out var value), value))
.Where(x => x.Item1)
.Select(x => (MagicValue)x.value!)
.ToArray(),
entry.Extensions.ToArray()
.ToArray()
);
}
}

View File

@@ -7,7 +7,7 @@ using System.Runtime.InteropServices;
namespace ReMime.ContentResolvers
{
public class RiffResolver : IMediaTypeResolver, IMediaContentResolver
public class RiffResolver : IMediaContentResolver, IMagicValueResolver
{
public readonly List<MediaType> _mediaTypes = new List<MediaType>();
public readonly Dictionary<string, MediaType> _extensions = new Dictionary<string, MediaType>();
@@ -15,27 +15,26 @@ namespace ReMime.ContentResolvers
public IReadOnlyCollection<MediaType> MediaTypes { get; }
public RiffResolver()
private RiffResolver()
{
MediaTypes = _mediaTypes.AsReadOnly();
List<MagicValueDatabaseEntry> entries;
IEnumerable<MagicValueMediaType> entries;
using (Stream str = typeof(MagicContentResolver).Assembly.GetManifestResourceStream("ReMime.ContentResolvers.riff.jsonc")!)
{
entries = MagicValueDatabaseEntry.GetEntries(str);
}
foreach (var entry in entries)
{
AddRiffType((MagicValueMediaType)entry);
}
AddMagicValues(entries);
}
public RiffResolver(IEnumerable<MagicValueMediaType> values) : this()
public void AddMagicValues(IEnumerable<MagicValueMediaType> entries)
{
foreach (MagicValueMediaType value in values)
AddRiffType(value);
foreach (var entry in entries)
{
AddMagicValue(entry);
}
}
public bool TryResolve(Stream str, [NotNullWhen(true)] out MediaType? mediaType)
@@ -69,7 +68,7 @@ namespace ReMime.ContentResolvers
/// Add a RIFF sub-magic value to this resolver.
/// </summary>
/// <param name="type"></param>
public void AddRiffType(MagicValueMediaType type)
public void AddMagicValue(MagicValueMediaType type)
{
if (type.MagicValues.Length == 0)
throw new ArgumentException("Expected at least one media type.");
@@ -92,6 +91,8 @@ namespace ReMime.ContentResolvers
}
}
public static RiffResolver Instance { get; } = new RiffResolver();
[StructLayout(LayoutKind.Auto, Size = 12)]
private struct RiffChunk
{

View File

@@ -50,22 +50,13 @@ namespace ReMime
static MediaTypeResolver()
{
AddResolver(new RiffResolver(), 9997);
AddResolver(new MagicContentResolver(), 9998);
AddResolver(RiffResolver.Instance, 1000);
AddResolver(MagicContentResolver.Instance, 1001);
if (OperatingSystem.IsWindows())
{
AddResolver(new Win32MediaTypeResolver());
}
else if (OperatingSystem.IsLinux())
{
AddResolver(new UnixMediaTypeResolver());
// TODO: add freedesktop mime type database.
}
else if (OperatingSystem.IsMacOS())
{
AddResolver(new UnixMediaTypeResolver()); //?
}
if (Win32MediaTypeResolver.Instance != null)
AddResolver(Win32MediaTypeResolver.Instance, 1002);
if (UnixMediaTypeResolver.Instance != null)
AddResolver(UnixMediaTypeResolver.Instance, 1002);
}
/// <summary>

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.IO;
namespace ReMime.Platform
@@ -13,7 +14,7 @@ namespace ReMime.Platform
private readonly Dictionary<string, MediaType> _extensionsMap = new Dictionary<string, MediaType>();
public IReadOnlyCollection<MediaType> MediaTypes { get; }
public UnixMediaTypeResolver()
private UnixMediaTypeResolver()
{
{
bool valid = OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsFreeBSD();
@@ -53,6 +54,20 @@ namespace ReMime.Platform
return _extensionsMap.TryGetValue(extension, out mediaType);
}
public static UnixMediaTypeResolver? Instance { get; } = null;
static UnixMediaTypeResolver()
{
try
{
Instance = new UnixMediaTypeResolver();
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
}
private static readonly char[] s_delimeters = new char[] { '\t', ' ' };
private static void DigestMimeDatabase(TextReader reader, List<MediaType> types)

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.Win32;
@@ -13,7 +14,7 @@ namespace ReMime.Platform
private readonly Dictionary<string, MediaType> _extensionsMap = new Dictionary<string, MediaType>();
public IReadOnlyCollection<MediaType> MediaTypes { get; }
public Win32MediaTypeResolver()
private Win32MediaTypeResolver()
{
if (!OperatingSystem.IsWindows())
throw new PlatformNotSupportedException();
@@ -57,5 +58,19 @@ namespace ReMime.Platform
{
return _extensionsMap.TryGetValue(extension, out mediaType);
}
public static Win32MediaTypeResolver? Instance { get; } = null;
static Win32MediaTypeResolver()
{
try
{
Instance = new Win32MediaTypeResolver();
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
}
}
}

View File

@@ -1,14 +1,36 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
<!--NuGet-->
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<PackageId>ReFuel.ReMime</PackageId>
<Version>0.1.1</Version>
<Authors>H. Utku Maden</Authors>
<Company>ReFuel</Company>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageLicenseFile>LICENSE.md</PackageLicenseFile>
<PackageIcon>images\icon.png</PackageIcon>
<PackageProjectUrl>https://refuel.mixedup.dev/docs/ReMime.html</PackageProjectUrl>
<RepositoryUrl>https://git.mixedup.dev/ReFuel/ReMime</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>detection; detector; type; file; mime; mime-type; media; media-type; analysis</PackageTags>
<PackageDescription>
ReMime is a very humble library that can identify IANA media types of file
from their file extension and its content. While being fully extensible
with your own resolvers, ReMime will also refer to your operating system's
file type database when resolving files.
</PackageDescription>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="ContentResolvers/database.jsonc" />
<EmbeddedResource Include="ContentResolvers/riff.jsonc" />
</ItemGroup>
<Content Include="../*.md" Pack="true" PackagePath="/" />
<None Include="../remime_favicon.png" Pack="true" PackagePath="images\icon.png"/>
</ItemGroup>
</Project>

BIN
remime_favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB