Skip to content

Commit

Permalink
Added optional parameter UseTiling
Browse files Browse the repository at this point in the history
  • Loading branch information
sungaila committed Feb 28, 2024
1 parent c153124 commit b105a25
Show file tree
Hide file tree
Showing 565 changed files with 863 additions and 253 deletions.
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ body:
attributes:
label: PDFtoImage version
description: Which version of PDFtoImage is affected?
value: 3.1.0
value: 4.0.0
validations:
required: true
- type: dropdown
Expand Down
6 changes: 3 additions & 3 deletions src/PDFtoImage/Conversion.cs
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ public static SKBitmap ToImage(Stream pdfStream, bool leaveOpen = false, string?
AdjustForAspectRatio(ref currentWidth, ref currentHeight, pageSize);

// Internals.PdfDocument -> Image
return pdfDocument.Render(page, currentWidth ?? pageSize.Width, currentHeight ?? pageSize.Height, options.Dpi, options.Dpi, options.Rotation, renderFlags, options.WithFormFill, correctFromDpi, options.BackgroundColor ?? SKColors.White, options.Bounds);
return pdfDocument.Render(page, currentWidth ?? pageSize.Width, currentHeight ?? pageSize.Height, options.Dpi, options.Dpi, options.Rotation, renderFlags, options.WithFormFill, correctFromDpi, options.BackgroundColor ?? SKColors.White, options.Bounds, options.UseTiling);
}

/// <summary>
Expand Down Expand Up @@ -594,7 +594,7 @@ public static IEnumerable<SKBitmap> ToImages(Stream pdfStream, bool leaveOpen =
AdjustForAspectRatio(ref currentWidth, ref currentHeight, pageSize);

// Internals.PdfDocument -> Image
yield return pdfDocument.Render(i, currentWidth ?? pageSize.Width, currentHeight ?? pageSize.Height, options.Dpi, options.Dpi, options.Rotation, renderFlags, options.WithFormFill, correctFromDpi, options.BackgroundColor ?? SKColors.White, options.Bounds);
yield return pdfDocument.Render(i, currentWidth ?? pageSize.Width, currentHeight ?? pageSize.Height, options.Dpi, options.Dpi, options.Rotation, renderFlags, options.WithFormFill, correctFromDpi, options.BackgroundColor ?? SKColors.White, options.Bounds, options.UseTiling);
}
}

Expand Down Expand Up @@ -689,7 +689,7 @@ public static async IAsyncEnumerable<SKBitmap> ToImagesAsync(Stream pdfStream, b
AdjustForAspectRatio(ref currentWidth, ref currentHeight, pageSize);

// Internals.PdfDocument -> Image
yield return await Task.Run(() => pdfDocument.Render(i, currentWidth ?? pageSize.Width, currentHeight ?? pageSize.Height, options.Dpi, options.Dpi, options.Rotation, renderFlags, options.WithFormFill, correctFromDpi, options.BackgroundColor ?? SKColors.White, options.Bounds), cancellationToken);
yield return await Task.Run(() => pdfDocument.Render(i, currentWidth ?? pageSize.Width, currentHeight ?? pageSize.Height, options.Dpi, options.Dpi, options.Rotation, renderFlags, options.WithFormFill, correctFromDpi, options.BackgroundColor ?? SKColors.White, options.Bounds, options.UseTiling, cancellationToken), cancellationToken);
}
}
#endif
Expand Down
126 changes: 103 additions & 23 deletions src/PDFtoImage/Internals/PdfDocument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Threading;

namespace PDFtoImage.Internals
{
Expand Down Expand Up @@ -47,6 +48,9 @@ private PdfDocument(Stream stream, string? password, bool disposeStream)
PageSizes = new ReadOnlyCollection<SizeF>(pageSizes);
}

private const int MaxTileWidth = 4000;
private const int MaxTileHeight = 4000;

/// <summary>
/// Renders a page of the PDF document to an image.
/// </summary>
Expand All @@ -61,8 +65,10 @@ private PdfDocument(Stream stream, string? password, bool disposeStream)
/// <param name="correctFromDpi">Change <paramref name="width"/> and <paramref name="height"/> depending on the given <paramref name="dpiX"/> and <paramref name="dpiY"/>.</param>
/// <param name="backgroundColor">The background color used for the output.</param>
/// <param name="bounds">Specifies the bounds for the page relative to <see cref="Conversion.GetPageSizes(string,string)"/>. This can be used for clipping (bounds inside of page) or additional margins (bounds outside of page).</param>
/// <param name="useTiling"></param>
/// <param name="cancellationToken"></param>
/// <returns>The rendered image.</returns>
public SKBitmap Render(int page, float width, float height, float dpiX, float dpiY, PdfRotation rotate, NativeMethods.FPDF flags, bool renderFormFill, bool correctFromDpi, SKColor backgroundColor, RectangleF? bounds)
public SKBitmap Render(int page, float width, float height, float dpiX, float dpiY, PdfRotation rotate, NativeMethods.FPDF flags, bool renderFormFill, bool correctFromDpi, SKColor backgroundColor, RectangleF? bounds, bool useTiling, CancellationToken cancellationToken = default)
{
if (_disposed)
throw new ObjectDisposedException(GetType().Name);
Expand All @@ -73,17 +79,11 @@ public SKBitmap Render(int page, float width, float height, float dpiX, float dp
(dpiX, dpiY) = (dpiY, dpiX);
}

var pageWidth = PageSizes[page].Width;
var pageHeight = PageSizes[page].Height;

if (correctFromDpi)
{
width *= dpiX / 72f;
height *= dpiY / 72f;

pageWidth *= dpiX / 72f;
pageHeight *= dpiY / 72f;

if (bounds != null)
{
bounds = new RectangleF(
Expand All @@ -102,17 +102,17 @@ public SKBitmap Render(int page, float width, float height, float dpiX, float dp
bounds = new RectangleF(
width - bounds.Value.Height - bounds.Value.Y,
bounds.Value.X,
bounds.Value.Width,
bounds.Value.Height
bounds.Value.Height,
bounds.Value.Width
);
}
else if (rotate == PdfRotation.Rotate270)
{
bounds = new RectangleF(
bounds.Value.Y,
height - bounds.Value.Width - bounds.Value.X,
bounds.Value.Width,
bounds.Value.Height
bounds.Value.Height,
bounds.Value.Width
);
}
else if (rotate == PdfRotation.Rotate180)
Expand All @@ -126,23 +126,93 @@ public SKBitmap Render(int page, float width, float height, float dpiX, float dp
}
}

width = (float)Math.Round(width);
height = (float)Math.Round(height);
SKBitmap bitmap;

int horizontalTileCount = (int)Math.Ceiling(width / MaxTileWidth);
int verticalTileCount = (int)Math.Ceiling(height / MaxTileHeight);

if (!useTiling || (horizontalTileCount == 1 && verticalTileCount == 1))
{
bitmap = RenderSubset(_file!, page, width, height, rotate, flags, renderFormFill, backgroundColor, bounds, width, height, cancellationToken);
}
else
{
bitmap = new SKBitmap((int)width, (int)height, SKColorType.Bgra8888, SKAlphaType.Premul);

cancellationToken.ThrowIfCancellationRequested();

float currentTileWidth = width / horizontalTileCount;
float currentTileHeight = height / verticalTileCount;
float boundsWidthFactor = bounds != null ? bounds.Value.Width / width : 0f;
float boundsHeightFactor = bounds != null ? bounds.Value.Height / height : 0f;

using var canvas = new SKCanvas(bitmap);
canvas.Clear(backgroundColor);

for (int y = 0; y < verticalTileCount; y++)
{
for (int x = 0; x < horizontalTileCount; x++)
{
cancellationToken.ThrowIfCancellationRequested();

RectangleF currentBounds;

if (bounds != null)
{
currentBounds = new(
(bounds.Value.X * (currentTileWidth / width)) + (currentTileWidth / horizontalTileCount * x * boundsWidthFactor),
(bounds.Value.Y * (currentTileHeight / height)) + (currentTileHeight / verticalTileCount * y * boundsHeightFactor),
currentTileWidth * boundsWidthFactor,
currentTileHeight * boundsHeightFactor);
}
else
{
currentBounds = new(
currentTileWidth / horizontalTileCount * x,
currentTileHeight / verticalTileCount * y,
currentTileWidth,
currentTileHeight);
}

using var subsetBitmap = RenderSubset(_file!, page, currentTileWidth, currentTileHeight, rotate, flags, renderFormFill, backgroundColor, currentBounds, width, height, cancellationToken);

cancellationToken.ThrowIfCancellationRequested();

canvas.DrawBitmap(subsetBitmap, new SKRect(
(float)Math.Floor(x * currentTileWidth),
(float)Math.Floor(y * currentTileHeight),
(float)Math.Floor(x * currentTileWidth + currentTileWidth),
(float)Math.Floor(y * currentTileHeight + currentTileHeight)));
canvas.Flush();
}
}
}

return bitmap;
}

private static SKBitmap RenderSubset(PdfFile file, int page, float width, float height, PdfRotation rotate, NativeMethods.FPDF flags, bool renderFormFill, SKColor backgroundColor, RectangleF? bounds, float originalWidth, float originalHeight, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
var bitmap = new SKBitmap((int)width, (int)height, SKColorType.Bgra8888, SKAlphaType.Premul);
var handle = NativeMethods.FPDFBitmap_CreateEx((int)width, (int)height, NativeMethods.FPDFBitmap.BGRA, bitmap.GetPixels(), (int)width * 4);
IntPtr handle = IntPtr.Zero;

try
{
cancellationToken.ThrowIfCancellationRequested();
handle = NativeMethods.FPDFBitmap_CreateEx((int)width, (int)height, NativeMethods.FPDFBitmap.BGRA, bitmap.GetPixels(), (int)width * 4);

cancellationToken.ThrowIfCancellationRequested();
NativeMethods.FPDFBitmap_FillRect(handle, 0, 0, (int)width, (int)height, (uint)backgroundColor);

bool success = _file!.RenderPDFPageToBitmap(
cancellationToken.ThrowIfCancellationRequested();
bool success = file.RenderPDFPageToBitmap(
page,
handle,
bounds != null ? -(int)Math.Round(bounds.Value.X * (pageWidth / bounds.Value.Width)) : 0,
bounds != null ? -(int)Math.Round(bounds.Value.Y * (pageHeight / bounds.Value.Height)) : 0,
bounds != null ? (int)Math.Round(pageWidth * (width / bounds.Value.Width)) : (int)width,
bounds != null ? (int)Math.Round(pageHeight * (height / bounds.Value.Height)) : (int)height,
bounds != null ? -(int)Math.Floor(bounds.Value.X * (originalWidth / bounds.Value.Width)) : 0,
bounds != null ? -(int)Math.Floor(bounds.Value.Y * (originalHeight / bounds.Value.Height)) : 0,
bounds != null ? (int)Math.Ceiling(originalWidth * (width / bounds.Value.Width)) : (int)Math.Ceiling(width),
bounds != null ? (int)Math.Ceiling(originalHeight * (height / bounds.Value.Height)) : (int)Math.Ceiling(height),
(int)rotate,
flags,
renderFormFill
Expand All @@ -151,23 +221,33 @@ public SKBitmap Render(int page, float width, float height, float dpiX, float dp
if (!success)
throw new Win32Exception();
}
catch
{
bitmap?.Dispose();
throw;
}
finally
{
NativeMethods.FPDFBitmap_Destroy(handle);
if (handle != IntPtr.Zero)
NativeMethods.FPDFBitmap_Destroy(handle);
}

return bitmap;
}

/// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

/// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
/// <param name="disposing">Whether this method is called from Dispose.</param>
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
/// <param name="disposing">Whether this method is called from <see cref="Dispose()"/>.</param>
private void Dispose(bool disposing)
{
if (!_disposed && disposing)
Expand Down
3 changes: 2 additions & 1 deletion src/PDFtoImage/PDFtoImage.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<!-- NuGet -->
<PropertyGroup>
<VersionPrefix>4.0.0</VersionPrefix>
<VersionSuffix>preview</VersionSuffix>
<VersionSuffix></VersionSuffix>
<Authors>David Sungaila</Authors>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
Expand All @@ -23,6 +23,7 @@
<PackageIconUrl>https://raw.githubusercontent.com/sungaila/PDFtoImage/master/etc/Icon_128.png</PackageIconUrl>
<Description>A .NET library to render PDF files into images.</Description>
<PackageReleaseNotes>- Added optional parameter Bounds.
- Added optional parameter UseTiling.
- RenderOptions introduced for most API calls. This is a breaking change, see README for more information.</PackageReleaseNotes>
<PackageTags>PDF Bitmap Image Convert Conversion C# PDFium SkiaSharp Skia PNG JPG JPEG WEBP Xamarin Android MonoAndroid MAUI wasm WebAssembly</PackageTags>
<RepositoryUrl>https://github.com/sungaila/PDFtoImage.git</RepositoryUrl>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ PDFtoImage.RenderOptions.Dpi.init -> void
PDFtoImage.RenderOptions.Height.get -> int?
PDFtoImage.RenderOptions.Height.init -> void
PDFtoImage.RenderOptions.RenderOptions() -> void
PDFtoImage.RenderOptions.RenderOptions(int Dpi = 300, int? Width = null, int? Height = null, bool WithAnnotations = false, bool WithFormFill = false, bool WithAspectRatio = false, PDFtoImage.PdfRotation Rotation = PDFtoImage.PdfRotation.Rotate0, PDFtoImage.PdfAntiAliasing AntiAliasing = PDFtoImage.PdfAntiAliasing.All, SkiaSharp.SKColor? BackgroundColor = null, System.Drawing.RectangleF? Bounds = null) -> void
PDFtoImage.RenderOptions.RenderOptions(int Dpi = 300, int? Width = null, int? Height = null, bool WithAnnotations = false, bool WithFormFill = false, bool WithAspectRatio = false, PDFtoImage.PdfRotation Rotation = PDFtoImage.PdfRotation.Rotate0, PDFtoImage.PdfAntiAliasing AntiAliasing = PDFtoImage.PdfAntiAliasing.All, SkiaSharp.SKColor? BackgroundColor = null, System.Drawing.RectangleF? Bounds = null, bool UseTiling = false) -> void
PDFtoImage.RenderOptions.Rotation.get -> PDFtoImage.PdfRotation
PDFtoImage.RenderOptions.Rotation.init -> void
PDFtoImage.RenderOptions.Width.get -> int?
Expand All @@ -22,6 +22,8 @@ PDFtoImage.RenderOptions.WithAspectRatio.get -> bool
PDFtoImage.RenderOptions.WithAspectRatio.init -> void
PDFtoImage.RenderOptions.WithFormFill.get -> bool
PDFtoImage.RenderOptions.WithFormFill.init -> void
PDFtoImage.RenderOptions.UseTiling.get -> bool
PDFtoImage.RenderOptions.UseTiling.init -> void
PDFtoImage.PdfAntiAliasing
PDFtoImage.PdfAntiAliasing.None = 0 -> PDFtoImage.PdfAntiAliasing
PDFtoImage.PdfAntiAliasing.Text = 1 -> PDFtoImage.PdfAntiAliasing
Expand Down
4 changes: 3 additions & 1 deletion src/PDFtoImage/PublicAPI/net462/PublicAPI.Shipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ PDFtoImage.RenderOptions.Dpi.init -> void
PDFtoImage.RenderOptions.Height.get -> int?
PDFtoImage.RenderOptions.Height.init -> void
PDFtoImage.RenderOptions.RenderOptions() -> void
PDFtoImage.RenderOptions.RenderOptions(int Dpi = 300, int? Width = null, int? Height = null, bool WithAnnotations = false, bool WithFormFill = false, bool WithAspectRatio = false, PDFtoImage.PdfRotation Rotation = PDFtoImage.PdfRotation.Rotate0, PDFtoImage.PdfAntiAliasing AntiAliasing = PDFtoImage.PdfAntiAliasing.All, SkiaSharp.SKColor? BackgroundColor = null, System.Drawing.RectangleF? Bounds = null) -> void
PDFtoImage.RenderOptions.RenderOptions(int Dpi = 300, int? Width = null, int? Height = null, bool WithAnnotations = false, bool WithFormFill = false, bool WithAspectRatio = false, PDFtoImage.PdfRotation Rotation = PDFtoImage.PdfRotation.Rotate0, PDFtoImage.PdfAntiAliasing AntiAliasing = PDFtoImage.PdfAntiAliasing.All, SkiaSharp.SKColor? BackgroundColor = null, System.Drawing.RectangleF? Bounds = null, bool UseTiling = false) -> void
PDFtoImage.RenderOptions.Rotation.get -> PDFtoImage.PdfRotation
PDFtoImage.RenderOptions.Rotation.init -> void
PDFtoImage.RenderOptions.Width.get -> int?
Expand All @@ -22,6 +22,8 @@ PDFtoImage.RenderOptions.WithAspectRatio.get -> bool
PDFtoImage.RenderOptions.WithAspectRatio.init -> void
PDFtoImage.RenderOptions.WithFormFill.get -> bool
PDFtoImage.RenderOptions.WithFormFill.init -> void
PDFtoImage.RenderOptions.UseTiling.get -> bool
PDFtoImage.RenderOptions.UseTiling.init -> void
PDFtoImage.PdfAntiAliasing
PDFtoImage.PdfAntiAliasing.None = 0 -> PDFtoImage.PdfAntiAliasing
PDFtoImage.PdfAntiAliasing.Text = 1 -> PDFtoImage.PdfAntiAliasing
Expand Down
4 changes: 3 additions & 1 deletion src/PDFtoImage/PublicAPI/net471/PublicAPI.Shipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ PDFtoImage.RenderOptions.Dpi.init -> void
PDFtoImage.RenderOptions.Height.get -> int?
PDFtoImage.RenderOptions.Height.init -> void
PDFtoImage.RenderOptions.RenderOptions() -> void
PDFtoImage.RenderOptions.RenderOptions(int Dpi = 300, int? Width = null, int? Height = null, bool WithAnnotations = false, bool WithFormFill = false, bool WithAspectRatio = false, PDFtoImage.PdfRotation Rotation = PDFtoImage.PdfRotation.Rotate0, PDFtoImage.PdfAntiAliasing AntiAliasing = PDFtoImage.PdfAntiAliasing.All, SkiaSharp.SKColor? BackgroundColor = null, System.Drawing.RectangleF? Bounds = null) -> void
PDFtoImage.RenderOptions.RenderOptions(int Dpi = 300, int? Width = null, int? Height = null, bool WithAnnotations = false, bool WithFormFill = false, bool WithAspectRatio = false, PDFtoImage.PdfRotation Rotation = PDFtoImage.PdfRotation.Rotate0, PDFtoImage.PdfAntiAliasing AntiAliasing = PDFtoImage.PdfAntiAliasing.All, SkiaSharp.SKColor? BackgroundColor = null, System.Drawing.RectangleF? Bounds = null, bool UseTiling = false) -> void
PDFtoImage.RenderOptions.Rotation.get -> PDFtoImage.PdfRotation
PDFtoImage.RenderOptions.Rotation.init -> void
PDFtoImage.RenderOptions.Width.get -> int?
Expand All @@ -22,6 +22,8 @@ PDFtoImage.RenderOptions.WithAspectRatio.get -> bool
PDFtoImage.RenderOptions.WithAspectRatio.init -> void
PDFtoImage.RenderOptions.WithFormFill.get -> bool
PDFtoImage.RenderOptions.WithFormFill.init -> void
PDFtoImage.RenderOptions.UseTiling.get -> bool
PDFtoImage.RenderOptions.UseTiling.init -> void
PDFtoImage.PdfAntiAliasing
PDFtoImage.PdfAntiAliasing.None = 0 -> PDFtoImage.PdfAntiAliasing
PDFtoImage.PdfAntiAliasing.Text = 1 -> PDFtoImage.PdfAntiAliasing
Expand Down
Loading

0 comments on commit b105a25

Please sign in to comment.