Images
The .NET Framework version of the RadPdfProcessing library provides built-in functionality for converting images and scaling their quality. The .NET Standard version does not provide such functionality and requires manual configuration. The FixedExtensibilityManager class exposes extensibility points for image conversion and JPEG 2000 decoding.
Exporting Images
To reduce file size, PDF supports only a limited set of compression filters such as JPEG and JPEG2000 compression of color and grayscale images. To allow the library to export images other than JPEG and JPEG2000, you must process these images before export. The .NET Standard specification does not define APIs for converting or processing images or scaling their quality. To export images other than JPEG and JPEG2000, or to use ImageQuality other than High, PdfProcessing exposes two extensibility points through the static FixedExtensibilityManager class: ImagePropertiesResolver and JpegImageConverter.
On cross-platform targets, set
ImagePropertiesResolverorJpegImageConverterbefore export. If both properties arenull, export throws anInvalidOperationException.
Requirements
To export images other than JPEG and JPEG2000, or to use ImageQuality other than High, add references to the following .NET Standard packages:
| NuGet package | Description |
|---|---|
| Telerik.Documents.ImageUtils | Provides default image resolver and JPEG converter implementations. |
| SkiaSharp.NativeAssets.* (version 3.119.1) | May differ according to the used platform. For Linux use SkiaSharp.NativeAssets.Linux.NoDependencies. |
| SkiaSharp.Views.Blazor and wasm-tools | For Blazor WebAssembly. |
ImagePropertiesResolver
The ImagePropertiesResolver property accepts a resolver that parses image data into color and alpha information. On cross-platform targets, the resolver is required for formats such as PNG when you need transparency preserved in the exported PDF. The resolver reads the image bytes and does not apply the ImageQuality value from the export settings.
Default Implementation for ImagePropertiesResolver
PdfProcessing provides a default implementation called ImagePropertiesResolver. The built-in logic depends on the SkiaSharp library to parse the image data. To use the default functionality, set an instance of the ImagePropertiesResolver class to the FixedExtensibilityManager.ImagePropertiesResolver property.
View the implementation Requirements.
Example 1: Set the default ImagePropertiesResolver implementation
Telerik.Documents.ImageUtils.ImagePropertiesResolver defaultImagePropertiesResolver = new Telerik.Documents.ImageUtils.ImagePropertiesResolver();
Telerik.Documents.Extensibility.FixedExtensibilityManager.ImagePropertiesResolver = defaultImagePropertiesResolver;
Custom Implementation for ImagePropertiesResolver
If you have specific requirements and the default ImagePropertiesResolver does not fit them, you can implement custom logic. To achieve this:
- Inherit the
Telerik.Documents.Core.Imaging.ImagePropertiesResolverBaseclass. - Implement its members.
- Assign an instance of the custom implementation to the
FixedExtensibilityManager.ImagePropertiesResolverproperty.
JpegImageConverter
The JpegImageConverter property uses an implementation of the JpegImageConverterBase abstract class to convert an image to JPEG. Pass this implementation to the JpegImageConverter property of the FixedExtensibilityManager.
If you have both the
ImagePropertiesResolverandJpegImageConverterproperties set,ImagePropertiesResolvertakes priority and is used to parse the image.
Default Implementation for JpegImageConverter
The Telerik.Documents.ImageUtils package provides a default implementation of the JpegImageConverter class that you can use when exporting a document. The default implementation depends on the SkiaSharp library to convert images to JPEG format.
View the implementation Requirements.
Example 2: Set the default JpegImageConverter implementation
Telerik.Documents.Extensibility.JpegImageConverterBase defaultJpegImageConverter = new Telerik.Documents.ImageUtils.JpegImageConverter();
Telerik.Documents.Extensibility.FixedExtensibilityManager.JpegImageConverter = defaultJpegImageConverter;
Custom Implementation for JpegImageConverter
The following example demonstrates how to implement a custom image converter. It depends on the SixLabors.ImageSharp library. You can follow this approach with other image processing libraries according to your specific requirements.
Required NuGet Packages
- SixLabors.ImageSharp - version 3.1.12
- SixLabors.ImageSharp.Drawing - version 2.1.7
The following using/imports statements are required in the project:
- using SixLabors.ImageSharp;
- using SixLabors.ImageSharp.Formats.Jpeg;
- using SixLabors.ImageSharp.Formats.Png;
- using SixLabors.ImageSharp.PixelFormats;
- using SixLabors.ImageSharp.Processing;
Example 3: Create a custom JpegImageConverterBase implementation
// SixLabors.ImageSharp 3.1.12
// SixLabors.ImageSharp.Drawing 2.1.7
//using SixLabors.ImageSharp;
//using SixLabors.ImageSharp.Formats.Jpeg;
//using SixLabors.ImageSharp.Formats.Png;
//using SixLabors.ImageSharp.PixelFormats;
//using SixLabors.ImageSharp.Processing;
internal class CustomJpegImageConverter : Telerik.Documents.Extensibility.JpegImageConverterBase
{
public override bool TryConvertToJpegImageData(byte[] imageData, ImageQuality imageQuality, out byte[] jpegImageData)
{
jpegImageData = null;
try
{
using (var imageStream = new MemoryStream(imageData))
{
SixLabors.ImageSharp.Image image = SixLabors.ImageSharp.Image.Load(imageStream);
var imageFormat = image.Metadata.DecodedImageFormat;
// Handle transparency for PNG
if (imageFormat is PngFormat && image.PixelType.BitsPerPixel == 32)
{
var background = new Image<Rgba32>(image.Width, image.Height, Color.White);
background.Mutate(ctx => ctx.DrawImage(image, 1f));
image.Dispose(); // Dispose original
image = background; // Assign new image
}
var jpegEncoder = new JpegEncoder
{
Quality = (int)imageQuality
};
using (var ms = new MemoryStream())
{
image.Save(ms, jpegEncoder);
jpegImageData = ms.ToArray();
}
image.Dispose(); // Dispose final image
}
return true;
}
catch (SixLabors.ImageSharp.UnknownImageFormatException)
{
return false;
}
catch (SixLabors.ImageSharp.ImageProcessingException)
{
return false;
}
catch (Exception)
{
return false;
}
}
}
Example 4: Assign a custom image converter
JpegImageConverterBase customJpegImageConverter = new CustomJpegImageConverter();
Telerik.Documents.Extensibility.FixedExtensibilityManager.JpegImageConverter = customJpegImageConverter;
A complete SDK example of a custom
JpegImageConverterBaseimplementation is available on the GitHub repository.
Decoding JPEG 2000 Images
PDF files can store JPEG 2000 image data with the JPXDecode filter. To decode these images, add the optional decoder package that matches your target:
| Target | NuGet package |
|---|---|
| .NET Standard and cross-platform .NET | Telerik.Documents.JpxDecodeUtils |
| .NET Framework and .NET for Windows | Telerik.Windows.Documents.JpxDecodeUtils |
Register one JpxImageDecoder instance before you import, render, or export PDF documents that require JPX decoding. The FixedExtensibilityManager.JpxImageDecoder property is global, so configure it once during application startup.
Example 5: Register the JPX decoder
using Telerik.Documents.Extensibility;
using Telerik.Documents.JpxDecodeUtils;
if (FixedExtensibilityManager.JpxImageDecoder == null)
{
FixedExtensibilityManager.JpxImageDecoder = new JpxImageDecoder();
}
The decoder limits estimated peak memory for each image to 256 MB by default. Set JpxImageDecoder.MaximumDecodedBytes to a positive value when your application needs a different limit.
Example 6: Configure the JPX decoded-memory limit
using Telerik.Documents.Extensibility;
using Telerik.Documents.JpxDecodeUtils;
JpxImageDecoder decoder = new JpxImageDecoder
{
MaximumDecodedBytes = 128L * 1024L * 1024L
};
FixedExtensibilityManager.JpxImageDecoder = decoder;
You can also create a custom decoder by inheriting JpxImageDecoderBase and implementing TryDecodeJpxImageData(). Return the decoded samples and image metadata through JpxImageDecodeResult.
If JPX processing requires a decoder and none is registered, RadPdfProcessing throws JpxImageDecoderNotConfiguredException. Attach the same handler to the import and export exception events to skip unsupported JPX images. Leave other exceptions unhandled.
Example 7: Handle a missing decoder during import and export
using System;
using System.IO;
using Telerik.Documents.Fixed.Exceptions;
using Telerik.Documents.Fixed.FormatProviders.Pdf;
using Telerik.Documents.Fixed.Model;
PdfFormatProvider provider = new PdfFormatProvider();
EventHandler<DocumentUnhandledExceptionEventArgs> skipMissingDecoder = (sender, args) =>
{
if (args.Exception is JpxImageDecoderNotConfiguredException)
{
args.Handled = true;
}
};
provider.ImportSettings.DocumentUnhandledException += skipMissingDecoder;
provider.ExportSettings.DocumentUnhandledException += skipMissingDecoder;
using (FileStream input = File.OpenRead("input.pdf"))
{
RadFixedDocument document =
provider.Import(input, TimeSpan.FromSeconds(10));
using (FileStream output = File.Create("output.pdf"))
{
provider.Export(document, output, TimeSpan.FromSeconds(10));
}
}
When the handler marks the exception as handled, import skips the JPX image or export omits it. The remaining document content stays available.