Introduction
This post describes how to use LEADTOOLS (Windows or Linux) in .NET Core to convert almost any image to a PNG. This service can be used to display non-standard files in any modern web browser without installing plug-ins or third party code on the client.
Background
If you follow this tutorial, the resulting project demonstrates a quick and easy way to use 3rd party C API in .NET Core using DLLImport
. Specifically, this project uses LEADTOOLS DLL/SO C API in .NET Core to convert files to PNG. Native .NET Core support is not available now available in LEADTOOLS version 20. An updated blog post that uses NuGet packages has been published for version 20.
Software Prerequisites
.NET Core development on Windows with Visual Studio requires:
- A supported version of the Windows client or operating system
- Visual Studio 2015 Update 3.3 or later
- NuGet Manager extension for Visual Studio
- .NET Core Tooling Preview 2
The sample project depends on the following 3rd-party libraries:
About the Code
This project is an ASP.NET Core application that will convert any LEADTOOLS supported image formats to PNG. This same application can be deployed to Windows or Linux.
Create new ASP.NET Core Web Application Project
Add LEADTOOLS License File as an Embedded Resource
-
Press Shift+Alt+A to add an existing item to the project.
(or right-click the project and select
Add
>Existing Item…
) -
Update the
buildOptions
section of theProject.json
to embed the resource.
Example
"buildOptions": { "emitEntryPoint": true, "preserveCompilationContext": true, "embed": [ "LEADTOOLS.LIC" ] },
Interop for the C API Library
The LTInterop
class is the bridge between native C API LEADTOOLS Libraries and .NET Core.
Below are three internal classes that correspond to Windows x86, Windows x64, and
Linux (The Linux class is used for x86 and x64).
These classes declare the correct extern
of the C API for that platform.
The static constructor in LTInterop
will dynamically determine which platform the
application is running and assign its delegate fields to the correct extern
function.
Press Shift+Alt+C to create a new class file named LTInterop.cs
and add the code from below.
Make sure to update WinX64BinPath
and WinX86BinPath
(marked below) with the path to the LEADTOOLS CDLLVC10 dlls.
CS Code
using System;
using System.Runtime.InteropServices;
namespace ImageConvertService
{
public static class LTInterop
{
// Update the following with the path to the LEADTOOLS CDLLVC10 DLLs.
private const string WinX64BinPath = @"LEADTOOLS_BIN_CDLLVC10_x64_PATH_HERE";
private const string WinX86BinPath = @"LEADTOOLS_BIN_CDLLVC10_Win32_PATH_HERE";
static LTInterop()
{
var architecture = RuntimeInformation.OSArchitecture;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
var oldPath = System.Environment.GetEnvironmentVariable("Path");
if (architecture == Architecture.X86)
{
Environment.SetEnvironmentVariable("Path", WinX86BinPath + "; " + oldPath);
Winx86.Apply();
}
else if (architecture == Architecture.X64)
{
Environment.SetEnvironmentVariable("Path", WinX64BinPath + "; " + oldPath);
Winx64.Apply();
}
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
Linux.Apply();
}
else
{
throw new InvalidOperationException("Unsupported architecture");
}
}
// Need to call one of the SetLicense functions only once.
public delegate int _L_SetLicenseFile(string licenseFile, string developerKey);
public static _L_SetLicenseFile L_SetLicenseFile;
public delegate int _L_SetLicenseBuffer(byte[] pLicenseBuffer, IntPtr nSize, string pszDeveloperKey);
public static _L_SetLicenseBuffer L_SetLicenseBuffer;
public delegate int _L_FileConvert(string sourceFile, string destFile, int format, int width, int height, int bitsPerPixel, int qfactor, IntPtr loadOptions, IntPtr saveOptions, IntPtr fileInfo);
public static _L_FileConvert L_FileConvert;
private static class Winx86
{
private const string _ltkrn = "ltkrnu.dll";
private const string _ltfil = "ltfilu.dll";
private const CharSet _charset = CharSet.Unicode;
[DllImport(_ltkrn, CharSet = _charset)]
public static extern int L_SetLicenseFile(string licenseFile, string developerKey);
[DllImport(_ltkrn, CharSet = _charset)]
public static extern int L_SetLicenseBuffer(byte[] pLicenseBuffer, IntPtr nSize, string pszDeveloperKey);
[DllImport(_ltfil, CharSet = _charset)]
public static extern int L_FileConvert(string sourceFile, string destFile, int format, int width, int height, int bitsPerPixel, int qfactor, IntPtr loadOptions, IntPtr saveOptions, IntPtr fileInfo);
public static void Apply()
{
LTInterop.L_SetLicenseFile = L_SetLicenseFile;
LTInterop.L_SetLicenseBuffer = L_SetLicenseBuffer;
LTInterop.L_FileConvert = L_FileConvert;
}
}
private static class Winx64
{
private const string _ltkrn = @"ltkrnx.dll";
private const string _ltfil = @"ltfilx.dll";
private const CharSet _charset = CharSet.Unicode;
[DllImport(_ltkrn, CharSet = _charset)]
public static extern int L_SetLicenseFile(string licenseFile, string developerKey);
[DllImport(_ltkrn, CharSet = _charset)]
public static extern int L_SetLicenseBuffer(byte[] pLicenseBuffer, IntPtr nSize, string pszDeveloperKey);
[DllImport(_ltfil, CharSet = _charset)]
public static extern int L_FileConvert(string sourceFile, string destFile, int format, int width, int height, int bitsPerPixel, int qfactor, IntPtr loadOptions, IntPtr saveOptions, IntPtr fileInfo);
public static void Apply()
{
LTInterop.L_SetLicenseFile = L_SetLicenseFile;
LTInterop.L_SetLicenseBuffer = L_SetLicenseBuffer;
LTInterop.L_FileConvert = L_FileConvert;
}
}
private static class Linux
{
private const string _ltkrn = "libltkrn.so";
private const string _ltfil = "libltfil.so";
private const CharSet _charset = CharSet.Ansi;
[DllImport(_ltkrn, CharSet = _charset)]
public static extern int L_SetLicenseFileA(string licenseFile, string developerKey);
[DllImport(_ltkrn, CharSet = _charset)]
public static extern int L_SetLicenseBufferA(byte[] pLicenseBuffer, IntPtr nSize, string pszDeveloperKey);
[DllImport(_ltfil, CharSet = _charset)]
public static extern int L_FileConvertA(string sourceFile, string destFile, int format, int width, int height, int bitsPerPixel, int qfactor, IntPtr loadOptions, IntPtr saveOptions, IntPtr fileInfo);
public static void Apply()
{
LTInterop.L_SetLicenseFile = L_SetLicenseFileA;
LTInterop.L_SetLicenseBuffer = L_SetLicenseBufferA;
LTInterop.L_FileConvert = L_FileConvertA;
}
}
}
}
Set the LEADTOOLS License
Add the following method to Program.cs and call it first in your Main method.
private static void UnlockLeadtools()
{
// TODO: Update with your LEADTOOLS developer key
var key = @"Copy your LEADTOOLS license key here";
var assembly = Assembly.GetEntryAssembly();
// TODO: Make sure the namespace and resource name matches. Reminder: Case Sensitive - even the license file name!!!!
var licStream = assembly.GetManifestResourceStream("ImageConvertService" + "." + "LEADTOOLS License file name here");
if (licStream == null)
throw new FileNotFoundException("Could not load license file from resource stream.");
using (var ms = new MemoryStream())
{
licStream.CopyTo(ms);
var ret = LTInterop.L_SetLicenseBuffer(ms.ToArray(), new IntPtr(ms.Length), key);
if (ret != 1)
throw new Exception("Error setting LEADTOOLS License : " + ret);
}
}
Add the Imaging Controller
This class contains the convert method that takes a URL to an image or document as a parameter, downloads it to a temp file, converts it to PNG, then return the PNG file.
- Press Shift+Alt+C to create a new class file named ImagingController in the “Controllers” folder.
- Add the following code below.
Code Details
using System;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
namespace ImageConvertService.Controllers
{
[Route("api/[controller]")]
public class ImagingController : Controller
{
public async Task<IActionResult> Get(Uri url)
{
const int FILE_PNG = 75; // From ltfil.h
const string mimeType = "image/png";
if (url == null)
return Content("url cannot be null");
if (!url.IsAbsoluteUri)
return Content("url must be absolute. Make sure to include the protocol.");
string sourceFile = null;
string targetFile = null;
try
{
// Download image to a temp file.
sourceFile = Path.GetTempFileName();
using (var client = new HttpClient())
using (var request = new HttpRequestMessage(HttpMethod.Get, url.ToString()))
using (Stream contentStream = await (await client.SendAsync(request)).Content.ReadAsStreamAsync())
using (Stream fileStream = new FileStream(sourceFile, FileMode.Create, FileAccess.Write, FileShare.None, 3145728, true))
{
await contentStream.CopyToAsync(fileStream);
}
// Call native L_FileConvert
// https://www.leadtools.com/help/leadtools/v19/main/api/l_fileconvert.html
targetFile = Path.GetTempFileName();
int ret = LTInterop.L_FileConvert(sourceFile, targetFile, FILE_PNG, 0, 0, 0, 0, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
if (ret != 1)
return Content("Error Converting File : " + ret);
// Load converted file and return.
byte[] buffer = System.IO.File.ReadAllBytes(targetFile);
return File(buffer, mimeType);
}
finally
{
// Clean up
if (sourceFile != null && System.IO.File.Exists(sourceFile))
System.IO.File.Delete(sourceFile);
if (targetFile != null && System.IO.File.Exists(targetFile))
System.IO.File.Delete(targetFile);
}
}
}
}
Testing the service on Windows
- Run the service from Visual Studio. A web browser should open to the URL set in Project Properties > Debug > Launch URL & App URL
-
Call the web method.
- DICOM example: http://localhost:63077/api/Imaging/convert?url=http://demo.leadtools.com/images/dcm/image2.dcm
- TIFF example: http://localhost:63077/api/Imaging/convert?url=http://demo.leadtools.com/images/tiff/ocr1.tif
Deploying to Linux
Prerequisites
- Make sure you have .NET Core installed on your Linux machine.
- Also, make sure you have the LEADTOOLS Linux binaries copied to your machine.
Publish your Project
- Right click on your project > Publish
- Enter an name for your Publish Profile
- Publish your project to a folder on your machine
- Copy the output to your Linux machine
Run your Project on Linux
We will be running this application from the command prompt and will be setting the
LD_LIBRARY_PATH
environment variable to the path of our LEADTOOLS native
libraries with the command below:
LD_LIBRARY_PATH=<Path to the LEADTOOLS Linux libraries>:$LD_LIBRARY_PATH dotnet <Path to .NET Core application DLL>
For Example:
LD_LIBRARY_PATH=/home/torvalds/LEADTOOLS19/Bin/Lib/x64/:$LD_LIBRARY_PATH dotnet LEADTOOLS_NetCore_ImageConvertService.dll
Possible Future Improvements
- Use redirection to directly load and save to a stream.
Troubleshoot
.NET Core 1.1.0
This project was built with .NET Core 1.0.1. If you use .NET Core 1.1.0, then it is possible that you will get the error below.
To solve the issue, update the project.json file to include a runtimes section as shown below.
History
- 5 Jan 2017: Added information for .NET Core 1.1
- 4 Jan 2017: Initial version
Fantastic. I ran into this problem Build a .NET Core Image Conversion MicroService. Thanks to your article I have now successfully resolved the problem. Thank you
Pingback: Build a .NET Core Image Conversion MicroService using LEADTOOLS NuGet Packages | LEADTOOLS Blog
very relevant blog about core dotnet tools……good quality ..
well done……….keep updating,………..