Skip to content

Commit

Permalink
feat(PdfDocument): Inport initial implementation from PR unoplatform#…
Browse files Browse the repository at this point in the history
  • Loading branch information
artemious7 authored and workgroupengineering committed Dec 14, 2022
1 parent 1a9b3a1 commit 0d2f7c3
Show file tree
Hide file tree
Showing 9 changed files with 403 additions and 111 deletions.
81 changes: 81 additions & 0 deletions src/Uno.UWP/Data/Pdf/PdfDocument.Android.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#if __ANDROID__
using System;
using System.Threading.Tasks;
using Android.Graphics.Pdf;
using Uno;

namespace Windows.Data.Pdf
{
public partial class PdfDocument : IDisposable
{
private readonly PdfRenderer pdfRenderer;

private PdfDocument(PdfRenderer pdfRenderer)
{
this.pdfRenderer = pdfRenderer ?? throw new ArgumentNullException(nameof(pdfRenderer));
}

[NotImplemented]
public bool IsPasswordProtected => false;

public uint PageCount => (uint)pdfRenderer.PageCount;

public PdfPage GetPage(uint pageIndex)
{
if (pageIndex >= pdfRenderer.PageCount)
{
throw new ArgumentOutOfRangeException(nameof(pageIndex), $"In this document the page index cannot be {pageIndex}. Page count is {pdfRenderer.PageCount}");
}

var pdfPage = pdfRenderer.OpenPage((int)pageIndex);
return new PdfPage(pdfPage);
}

public static Foundation.IAsyncOperation<PdfDocument> LoadFromFileAsync(Storage.IStorageFile file)
{
if (file is null)
{
throw new ArgumentNullException(nameof(file));
}

#pragma warning disable Uno0001 // Uno type or member is not implemented
var localpath = file.Path;
#pragma warning restore Uno0001 // Uno type or member is not implemented
var javaFile = new Java.IO.File(localpath);
var fileDescriptor = Android.OS.ParcelFileDescriptor.Open(javaFile, Android.OS.ParcelFileMode.ReadOnly);
var pdfRenderer = new PdfRenderer(fileDescriptor);
var pdfDocument = new PdfDocument(pdfRenderer);

return Task.FromResult(pdfDocument).AsAsyncOperation();
}


[NotImplemented]
public static Foundation.IAsyncOperation<PdfDocument> LoadFromFileAsync(Storage.IStorageFile file, string password)
{
// password protected PDF files are not supported by PdfRenderer in Android
throw new NotImplementedException("The member IAsyncOperation<PdfDocument> PdfDocument.LoadFromFileAsync(IStorageFile file, string password) is not implemented in Uno.");
}

[NotImplemented]
public static Foundation.IAsyncOperation<PdfDocument> LoadFromStreamAsync(Storage.Streams.IRandomAccessStream inputStream)
{
throw new NotImplementedException("The member IAsyncOperation<PdfDocument> PdfDocument.LoadFromStreamAsync(IRandomAccessStream inputStream) is not implemented in Uno.");
}


[NotImplemented]
public static Foundation.IAsyncOperation<PdfDocument> LoadFromStreamAsync(Storage.Streams.IRandomAccessStream inputStream, string password)
{
// password protected PDF files are not supported by PdfRenderer in Android
throw new NotImplementedException("The member IAsyncOperation<PdfDocument> PdfDocument.LoadFromStreamAsync(IRandomAccessStream inputStream, string password) is not implemented in Uno.");
}

public void Dispose()
{
pdfRenderer.Dispose();
}
}
}

#endif
173 changes: 173 additions & 0 deletions src/Uno.UWP/Data/Pdf/PdfPage.Android.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
#if __ANDROID__
using System;
using System.IO;
using System.Threading.Tasks;
using Android.Graphics;
using Android.Graphics.Pdf;
using Uno;
using Windows.Foundation;
using Windows.Storage.Streams;
using Windows.UI;
using Color = Windows.UI.Color;
using Rect = Windows.Foundation.Rect;

namespace Windows.Data.Pdf
{
public partial class PdfPage : IDisposable
{
private readonly PdfRenderer.Page pdfPage;

internal PdfPage(PdfRenderer.Page pdfPage)
{
this.pdfPage = pdfPage ?? throw new ArgumentNullException(nameof(pdfPage));
}

public PdfPageDimensions Dimensions { get; } = new PdfPageDimensions();

public uint Index => (uint)pdfPage.Index;

[NotImplemented]
public float PreferredZoom
{
get
{
throw new NotImplementedException("The member float PdfPage.PreferredZoom is not implemented in Uno.");
}
}

[NotImplemented]
public PdfPageRotation Rotation
{
get
{
throw new NotImplementedException("The member PdfPageRotation PdfPage.Rotation is not implemented in Uno.");
}
}

public Size Size => new Size(pdfPage.Width, pdfPage.Height);

public IAsyncAction RenderToStreamAsync(IRandomAccessStream outputStream)
{
if (outputStream is null)
{
throw new ArgumentNullException(nameof(outputStream));
}

var options = new PdfPageRenderOptions
{
DestinationWidth = (uint)pdfPage.Width,
DestinationHeight = (uint)pdfPage.Height
};
return RenderToStreamAsync(outputStream, options);
}

public IAsyncAction RenderToStreamAsync(IRandomAccessStream outputStream, PdfPageRenderOptions options)
{
#region Validate arguments
if (outputStream is null)
{
throw new ArgumentNullException(nameof(outputStream));
}
if (options is null)
{
throw new ArgumentNullException(nameof(options));
}
if (options.DestinationWidth > int.MaxValue)
{
throw new ArgumentOutOfRangeException(nameof(options), $"PdfPageRenderOptions.DestinationWidth = {options.DestinationWidth}. Must be less than or equal to int.MaxValue");
}
if (options.DestinationHeight > int.MaxValue)
{
throw new ArgumentOutOfRangeException(nameof(options), $"PdfPageRenderOptions.DestinationHeight = {options.DestinationHeight}. Must be less than or equal to int.MaxValue");
}
#endregion

return Task.Run(() =>
{
const int quality = 100;
var bitmap = RenderInternal(options);
bitmap.Compress(Bitmap.CompressFormat.Png, quality, outputStream.AsStream());
return bitmap;
}).AsAsyncAction();
}

private Bitmap RenderInternal(PdfPageRenderOptions options)
{
var destination = new Size(options.DestinationWidth, options.DestinationHeight);

#region handle 0-width and 0-height
if (destination.Width == 0 && destination.Height == 0)
{
// destination size not set - render with the page original size
destination = Size;
}
else if (destination.Width == 0)
{
// destination width not set - calculate it based on height proportion
var scale = destination.Height / Size.Height;
destination = new Size(Size.Width * scale, destination.Height);
}
else if (destination.Height == 0)
{
// destination height not set - calculate it based on width proportion
var scale = destination.Width / Size.Width;
destination = new Size(destination.Width, Size.Height * scale);
}
#endregion

#region scale according to DPI
var dpi = Graphics.Display.DisplayInformation.GetForCurrentView().LogicalDpi / 96.0f;
destination = new Size(destination.Width * dpi, destination.Height * dpi);
#endregion

#region create bitmap
var bitmap = Bitmap.CreateBitmap((int)destination.Width, (int)destination.Height, Bitmap.Config.Argb8888);
var destinationRect = new Rect(new Foundation.Point(), destination);
#endregion

#region fill with background color
using (var canvas = new Canvas(bitmap))
{
var color = options.BackgroundColor.Equals(default(Color)) ? Colors.White : options.BackgroundColor;
canvas.DrawRect(destinationRect, new Paint()
{
Color = color
});
}
#endregion

#region render only a portion of the page, defined by PdfPageRenderOptions.SourceRect, if set
var sourceRect = options.SourceRect;
Matrix matrix = null;
if (sourceRect.Width > 0 && sourceRect.Height > 0)
{
matrix = new Matrix();
matrix.SetRectToRect(AsAndroidRectF(sourceRect), AsAndroidRectF(destinationRect), Matrix.ScaleToFit.Start);
}
#endregion

// Render content
pdfPage.Render(bitmap, null, matrix, PdfRenderMode.ForDisplay);
return bitmap;
}

public IAsyncAction PreparePageAsync()
{
return Task.CompletedTask.AsAsyncAction();
}

public void Dispose()
{
pdfPage.Close();
pdfPage.Dispose();
}

private static RectF AsAndroidRectF(Rect rect) => new RectF(
(float)rect.Left,
(float)rect.Top,
(float)rect.Right,
(float)rect.Bottom);
}
}

#endif
15 changes: 15 additions & 0 deletions src/Uno.UWP/Data/Pdf/PdfPageDimensions.Android.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#if __ANDROID__
using Windows.Foundation;

namespace Windows.Data.Pdf
{
public partial class PdfPageDimensions
{
public Rect ArtBox { get; }
public Rect BleedBox { get; }
public Rect CropBox { get; }
public Rect MediaBox { get; }
public Rect TrimBox { get; }
}
}
#endif
23 changes: 23 additions & 0 deletions src/Uno.UWP/Data/Pdf/PdfPageRenderOptions.Android.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#if __ANDROID__
using System;
using Windows.Foundation;
using Windows.UI;

namespace Windows.Data.Pdf
{
public partial class PdfPageRenderOptions
{
public Rect SourceRect { get; set; }

public bool IsIgnoringHighContrast { get; set; }

public uint DestinationWidth { get; set; }

public uint DestinationHeight { get; set; }

public Guid BitmapEncoderId { get; set; }

public Color BackgroundColor { get; set; }
}
}
#endif
48 changes: 24 additions & 24 deletions src/Uno.UWP/Generated/3.0.0.0/Windows.Data.Pdf/PdfDocument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,67 +2,67 @@
#pragma warning disable 114 // new keyword hiding
namespace Windows.Data.Pdf
{
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
#if false || __IOS__ || NET461 || __WASM__ || __MACOS__
[global::Uno.NotImplemented]
#endif
public partial class PdfDocument
#endif
public partial class PdfDocument
{
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "NET461", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
#if false || __IOS__ || NET461 || __WASM__ || __MACOS__
[global::Uno.NotImplemented]
public bool IsPasswordProtected
{
get
{
throw new global::System.NotImplementedException("The member bool PdfDocument.IsPasswordProtected is not implemented in Uno.");
}
}
#endif
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "NET461", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
#endif
#if false || __IOS__ || NET461 || __WASM__ || __MACOS__
[global::Uno.NotImplemented]
public uint PageCount
{
get
{
throw new global::System.NotImplementedException("The member uint PdfDocument.PageCount is not implemented in Uno.");
}
}
#endif
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "NET461", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
#endif
#if false || __IOS__ || NET461 || __WASM__ || __MACOS__
[global::Uno.NotImplemented]
public global::Windows.Data.Pdf.PdfPage GetPage( uint pageIndex)
{
throw new global::System.NotImplementedException("The member PdfPage PdfDocument.GetPage(uint pageIndex) is not implemented in Uno.");
}
#endif
#endif
// Forced skipping of method Windows.Data.Pdf.PdfDocument.PageCount.get
// Forced skipping of method Windows.Data.Pdf.PdfDocument.IsPasswordProtected.get
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "NET461", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
#if false || __IOS__ || NET461 || __WASM__ || __MACOS__
[global::Uno.NotImplemented]
public static global::Windows.Foundation.IAsyncOperation<global::Windows.Data.Pdf.PdfDocument> LoadFromFileAsync( global::Windows.Storage.IStorageFile file)
{
throw new global::System.NotImplementedException("The member IAsyncOperation<PdfDocument> PdfDocument.LoadFromFileAsync(IStorageFile file) is not implemented in Uno.");
}
#endif
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "NET461", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
#endif
#if false || __IOS__ || NET461 || __WASM__ || __MACOS__
[global::Uno.NotImplemented]
public static global::Windows.Foundation.IAsyncOperation<global::Windows.Data.Pdf.PdfDocument> LoadFromFileAsync( global::Windows.Storage.IStorageFile file, string password)
{
throw new global::System.NotImplementedException("The member IAsyncOperation<PdfDocument> PdfDocument.LoadFromFileAsync(IStorageFile file, string password) is not implemented in Uno.");
}
#endif
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "NET461", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
#endif
#if false || __IOS__ || NET461 || __WASM__ || __MACOS__
[global::Uno.NotImplemented]
public static global::Windows.Foundation.IAsyncOperation<global::Windows.Data.Pdf.PdfDocument> LoadFromStreamAsync( global::Windows.Storage.Streams.IRandomAccessStream inputStream)
{
throw new global::System.NotImplementedException("The member IAsyncOperation<PdfDocument> PdfDocument.LoadFromStreamAsync(IRandomAccessStream inputStream) is not implemented in Uno.");
}
#endif
#if __ANDROID__ || __IOS__ || NET461 || __WASM__ || __SKIA__ || __NETSTD_REFERENCE__ || __MACOS__
[global::Uno.NotImplemented("__ANDROID__", "__IOS__", "NET461", "__WASM__", "__SKIA__", "__NETSTD_REFERENCE__", "__MACOS__")]
#endif
#if false || __IOS__ || NET461 || __WASM__ || __MACOS__
[global::Uno.NotImplemented]
public static global::Windows.Foundation.IAsyncOperation<global::Windows.Data.Pdf.PdfDocument> LoadFromStreamAsync( global::Windows.Storage.Streams.IRandomAccessStream inputStream, string password)
{
throw new global::System.NotImplementedException("The member IAsyncOperation<PdfDocument> PdfDocument.LoadFromStreamAsync(IRandomAccessStream inputStream, string password) is not implemented in Uno.");
}
#endif
#endif
}
}
Loading

0 comments on commit 0d2f7c3

Please sign in to comment.