Browse Source

avalonia: PlatformTools.PdfRenderer now can render entire PDFs and give page counts

Kenric Nugteren 1 week ago
parent
commit
1298821674

+ 26 - 2
InABox.Avalonia.Platform.Android/PdfRenderer.Android.cs

@@ -6,12 +6,11 @@ namespace InABox.Avalonia.Platform.Android;
 
 public class Android_PdfRenderer : IPdfRenderer
 {
-        
     public Logger? Logger { get; set; }
     
     public byte[]? PdfToImage(byte[]? pdf, int page, int dpi)
     {
-        if (pdf?.Any() != true)
+        if (pdf is null || pdf.Length == 0)
             return null;
         var result = Conversion.ToImage(pdf, page, options: new RenderOptions(Dpi: dpi));
         using var ms = new MemoryStream();
@@ -22,6 +21,31 @@ public class Android_PdfRenderer : IPdfRenderer
     public Task<byte[]?> PdfToImageAsync(byte[]? pdf, int page, int dpi)
         => Task.Run(() => PdfToImage(pdf, page, dpi));
 
+    public byte[][]? PdfToImages(byte[]? pdf, int dpi)
+    {
+        if (pdf is null || pdf.Length == 0)
+            return null;
+        var result = Conversion.ToImages(pdf, options: new RenderOptions(Dpi: dpi));
+        return result.Select(x =>
+        {
+            using var ms = new MemoryStream();
+            x.Encode(ms, SKEncodedImageFormat.Jpeg, 65);
+            return ms.ToArray();
+        }).ToArray();
+    }
+
+    public Task<byte[][]?> PdfToImagesAsync(byte[]? pdf, int dpi)
+        => Task.Run(() => PdfToImages(pdf, dpi));
+
+    public int? PdfPageCount(byte[]? pdf)
+    {
+        if (pdf is null || pdf.Length == 0)
+            return null;
+        return Conversion.GetPageCount(pdf);
+    }
+
+    public Task<int?> PdfPageCountAsync(byte[]? pdf)
+        => Task.Run(() => PdfPageCount(pdf));
 
     public byte[]? ImageToPdf(byte[]? image) => null;
 

+ 35 - 10
InABox.Avalonia.Platform.Desktop/Desktop.PdfRenderer.cs

@@ -11,22 +11,47 @@ public class Desktop_PdfRenderer : IPdfRenderer
     {
         if (pdf?.Any() != true)
             return null;
-        
-        using (var inData = new MemoryStream(pdf))
-        {
-            using (var converter = new PdfToImageConverter(inData))
-            {
-                var output = converter.Convert(page, true, false) as MemoryStream;
-                return output.ToArray();
 
-            }
-        }
-        
+        using var inData = new MemoryStream(pdf);
+        using var converter = new PdfToImageConverter(inData);
+        var output = converter.Convert(page, true, false) as MemoryStream;
+        return output.ToArray();
+
     }
 
     public Task<byte[]?> PdfToImageAsync(byte[]? pdf, int page, int dpi)
         => Task.Run(() => PdfToImage(pdf, page, dpi));
 
+    public byte[][]? PdfToImages(byte[]? pdf, int dpi)
+    {
+        if (pdf is null || pdf.Length == 0)
+            return null;
+
+        using var inData = new MemoryStream(pdf);
+        using var converter = new PdfToImageConverter(inData);
+        return Enumerable.Range(0, converter.PageCount).Select(i =>
+        {
+            var output = converter.Convert(i, true, false) as MemoryStream;
+            return output.ToArray();
+        }).ToArray();
+    }
+
+    public Task<byte[][]?> PdfToImagesAsync(byte[]? pdf, int dpi)
+        => Task.Run(() => PdfToImages(pdf, dpi));
+
+    public int? PdfPageCount(byte[]? pdf)
+    {
+        if (pdf is null || pdf.Length == 0)
+            return null;
+
+        using var inData = new MemoryStream(pdf);
+        using var converter = new PdfToImageConverter(inData);
+        return converter.PageCount;
+    }
+
+    public Task<int?> PdfPageCountAsync(byte[]? pdf)
+        => Task.Run(() => PdfPageCount(pdf));
+
     public byte[]? ImageToPdf(byte[]? image)
     { 
         if (image?.Any() != true)

+ 90 - 16
InABox.Avalonia.Platform.iOS/PDFRenderer.iOS.cs

@@ -7,22 +7,11 @@ public class iOS_PdfRenderer : IPdfRenderer
 
     public Logger? Logger { get; set; }
 
-    public byte[]? PdfToImage(byte[]? pdf, int pageIndex, int dpi)
+    private byte[] PdfDocToImage(CGPDFDocument document, int pageIndex, int dpi)
     {
-
         float scale = 2F;
 
-        // Step 1: Load the PDF document from byte array
-        using var data = NSData.FromArray(pdf);
-        using var provider = new CGDataProvider(data);
-        using var pdfDoc = new CGPDFDocument(provider);
-
-        // Validate page index
-        int pageCount = (int)pdfDoc.Pages;
-        if (pageIndex < 0 || pageIndex >= pageCount)
-            throw new ArgumentOutOfRangeException(nameof(pageIndex), "Invalid PDF page index.");
-
-        using var page = pdfDoc.GetPage(pageIndex + 1); // Pages are 1-indexed
+        using var page = document.GetPage(pageIndex + 1); // Pages are 1-indexed
         var pageRect = page.GetBoxRect(CGPDFBox.Media);
         var scaledSize = new CGSize(pageRect.Width * scale, pageRect.Height * scale);
 
@@ -43,13 +32,60 @@ public class iOS_PdfRenderer : IPdfRenderer
         // Step 3: Convert UIImage to PNG byte array
         using var pngData = image.AsPNG();
         return pngData.ToArray();
+    }
+
+    public byte[]? PdfToImage(byte[]? pdf, int pageIndex, int dpi)
+    {
+        if (pdf is null || pdf.Length == 0)
+            return null;
+
+        // Step 1: Load the PDF document from byte array
+        using var data = NSData.FromArray(pdf);
+        using var provider = new CGDataProvider(data);
+        using var pdfDoc = new CGPDFDocument(provider);
+
+        // Validate page index
+        int pageCount = (int)pdfDoc.Pages;
+        if (pageIndex < 0 || pageIndex >= pageCount)
+            throw new ArgumentOutOfRangeException(nameof(pageIndex), "Invalid PDF page index.");
 
+        return PdfDocToImage(pdfDoc, pageIndex, dpi);
     }
 
-    public Task<byte[]?> PdfToImageAsync(byte[]? pdf, int page, int dpi)
+    public byte[][]? PdfToImages(byte[]? pdf, int dpi)
+    {
+        if (pdf is null || pdf.Length == 0)
+            return null;
+        float scale = 2F;
+
+        // Step 1: Load the PDF document from byte array
+        using var data = NSData.FromArray(pdf);
+        using var provider = new CGDataProvider(data);
+        using var pdfDoc = new CGPDFDocument(provider);
+
+        // Validate page index
+        int pageCount = (int)pdfDoc.Pages;
+
+        return Enumerable.Range(0, pageCount).Select(pageIndex =>
+        {
+            return PdfDocToImage(pdfDoc, pageIndex, dpi);
+        }).ToArray();
+    }
+
+    public int? PdfPageCount(byte[]? pdf)
     {
+        if (pdf is null || pdf.Length == 0)
+            return null;
+        // Step 1: Load the PDF document from byte array
+        using var data = NSData.FromArray(pdf);
+        using var provider = new CGDataProvider(data);
+        using var pdfDoc = new CGPDFDocument(provider);
+        return (int)pdfDoc.Pages;
+    }
 
-        var tcs = new TaskCompletionSource<byte[]>();
+    public Task<byte[]?> PdfToImageAsync(byte[]? pdf, int page, int dpi)
+    {
+        var tcs = new TaskCompletionSource<byte[]?>();
 
         UIApplication.SharedApplication.BeginInvokeOnMainThread(() =>
         {
@@ -65,8 +101,46 @@ public class iOS_PdfRenderer : IPdfRenderer
         });
 
         return tcs.Task;
+    }
+
+    public Task<byte[][]?> PdfToImagesAsync(byte[]? pdf, int dpi)
+    {
+        var tcs = new TaskCompletionSource<byte[][]?>();
+
+        UIApplication.SharedApplication.BeginInvokeOnMainThread(() =>
+        {
+            try
+            {
+                var result = PdfToImages(pdf, dpi);
+                tcs.SetResult(result);
+            }
+            catch (Exception ex)
+            {
+                tcs.SetException(ex);
+            }
+        });
+
+        return tcs.Task;
+    }
+
+    public Task<int?> PdfPageCountAsync(byte[]? pdf)
+    {
+        var tcs = new TaskCompletionSource<int?>();
 
+        UIApplication.SharedApplication.BeginInvokeOnMainThread(() =>
+        {
+            try
+            {
+                var result = PdfPageCount(pdf);
+                tcs.SetResult(result);
+            }
+            catch (Exception ex)
+            {
+                tcs.SetException(ex);
+            }
+        });
 
+        return tcs.Task;
     }
 
     public byte[]? ImageToPdf(byte[]? image)
@@ -76,7 +150,7 @@ public class iOS_PdfRenderer : IPdfRenderer
 
     public Task<byte[]?> ImageToPdfAsync(byte[]? image)
     {
-        var tcs = new TaskCompletionSource<byte[]>();
+        var tcs = new TaskCompletionSource<byte[]?>();
 
         UIApplication.SharedApplication.BeginInvokeOnMainThread(() =>
         {

+ 15 - 0
InABox.Avalonia.Platform/PDFRenderer/DefaultPdfRenderer.cs

@@ -18,4 +18,19 @@ public class DefaultPdfRenderer : IPdfRenderer
     public Task<byte[]?> ImageToPdfAsync(byte[]? image)
         => Task.Run(() => ImageToPdf(image));
 
+    public byte[][]? PdfToImages(byte[]? pdf, int dpi)
+    {
+        return null;
+    }
+
+    public Task<byte[][]?> PdfToImagesAsync(byte[]? pdf, int dpi)
+        => Task.Run(() => PdfToImages(pdf, dpi));
+
+    public int? PdfPageCount(byte[]? pdf)
+    {
+        return null;
+    }
+
+    public Task<int?> PdfPageCountAsync(byte[]? pdf)
+        => Task.Run(() => PdfPageCount(pdf));
 }

+ 6 - 0
InABox.Avalonia.Platform/PDFRenderer/IPdfRenderer.cs

@@ -5,6 +5,12 @@ public interface IPdfRenderer : ILoggable
     byte[]? PdfToImage(byte[]? pdf, int page, int dpi);
     Task<byte[]?> PdfToImageAsync(byte[]? pdf, int page, int dpi);
 
+    byte[][]? PdfToImages(byte[]? pdf, int dpi);
+    Task<byte[][]?> PdfToImagesAsync(byte[]? pdf, int dpi);
+
+    int? PdfPageCount(byte[]? pdf);
+    Task<int?> PdfPageCountAsync(byte[]? pdf);
+
     byte[]? ImageToPdf(byte[]? image);
     Task<byte[]?> ImageToPdfAsync(byte[]? image);
 

+ 18 - 1
InABox.Avalonia/Components/ZoomPanel/ZoomPanel.cs

@@ -79,10 +79,24 @@ public partial class ZoomPanel : TemplatedControl
         ZoomContentBorder = e.NameScope.Get<Border>("PART_ZoomContentBorder");
 
         OuterCanvas.LayoutUpdated += OuterCanvas_LayoutUpdated;
+        OuterCanvas.SizeChanged += OuterCanvas_SizeChanged;
         OuterCanvas.AddHandler(PanAndZoomGestureRecognizer.PanAndZoomEndedEvent, OuterCanvas_PinchEnded);
         OuterCanvas.AddHandler(PanAndZoomGestureRecognizer.PanAndZoomEvent, OuterCanvas_Pinch);
 
         OuterCanvas.PointerWheelChanged += OuterCanvas_PointerWheelChanged;
+
+        if (IsLoaded)
+        {
+            PositionContent();
+        }
+    }
+
+    private void OuterCanvas_SizeChanged(object? sender, SizeChangedEventArgs e)
+    {
+        if (IsLoaded)
+        {
+            PositionContent();
+        }
     }
 
     private void OuterCanvas_PinchEnded(object? sender, PanAndZoomEndedEventArgs e)
@@ -145,7 +159,10 @@ public partial class ZoomPanel : TemplatedControl
     protected override void OnLoaded(RoutedEventArgs e)
     {
         base.OnLoaded(e);
-        PositionContent();
+        if(OuterCanvas is not null)
+        {
+            PositionContent();
+        }
     }
 
     private void PositionContent()