diff --git a/PDFiumSharp/PDFium.cs b/PDFiumSharp/PDFium.cs
index 4f8dd2e..9f3acbe 100644
--- a/PDFiumSharp/PDFium.cs
+++ b/PDFiumSharp/PDFium.cs
@@ -122,14 +122,13 @@ public static FPDF_DOCUMENT FPDF_LoadDocument(byte[] data, int index = 0, int co
///
/// Loads a PDF document from '' bytes read from a stream.
///
- ///
///
/// The number of bytes to read from the .
/// If the value is equal to or smaller than 0, the stream is read to the end.
///
- ///
+ /// The FILEREAD structure ready for reading.
/// Pdf password
- public static FPDF_DOCUMENT FPDF_LoadDocument(Stream stream, FPDF_FILEREAD fileRead, int count = 0, string password = null)
+ public static FPDF_DOCUMENT FPDF_LoadDocument(FPDF_FILEREAD fileRead, string password = null)
=> FPDF_LoadCustomDocument(fileRead, password);
//public static string FPDF_VIEWERREF_GetName(FPDF_DOCUMENT document, string key)
diff --git a/PDFiumSharp/PdfDocument.cs b/PDFiumSharp/PdfDocument.cs
index adae6ae..f2dac5f 100644
--- a/PDFiumSharp/PdfDocument.cs
+++ b/PDFiumSharp/PdfDocument.cs
@@ -19,6 +19,8 @@ public sealed class PdfDocument : NativeWrapper
private FPDF_FORMFILLINFO formCallbacks;
private GCHandle formCallbacksHandle;
+
+ private FPDF_FILEREAD fileRead;
private FPDF_FORMHANDLE form;
@@ -138,15 +140,23 @@ public PdfDocument(byte[] data, int index = 0, int count = -1, string password =
/// Loads a from '' bytes read from a .
/// must be called in order to free unmanaged resources.
///
- ///
- ///
+ /// Stream containing the bytes of the PDF document to load. Must remain open and accessible as long as the document is being used.
///
/// The number of bytes to read from the .
/// If the value is equal to or smaller than 0, the stream is read to the end.
///
- ///
- public PdfDocument(Stream stream, FPDF_FILEREAD fileRead, int count = 0, string password = null)
- : this(PDFium.FPDF_LoadDocument(stream, fileRead, count, password)) { }
+ /// Password.
+ public PdfDocument(Stream stream, int count = 0, string password = null)
+ : this(FPDF_FILEREAD.FromStream(stream, count), password) { }
+
+ ///
+ /// Private overload which keeps the FILEREAD reference so that the delegate and its callback stub remain valid for the lifetime of the document.
+ ///
+ private PdfDocument(FPDF_FILEREAD fileRead, string password)
+ : this(PDFium.FPDF_LoadDocument(fileRead, password))
+ {
+ this.fileRead = fileRead;
+ }
///
/// Closes the and frees unmanaged resources.
@@ -209,6 +219,8 @@ protected override void Dispose(FPDF_DOCUMENT handle)
((IDisposable)this.Pages).Dispose();
PDFium.FPDF_CloseDocument(handle);
+
+ this.fileRead = null;
}
}
}
\ No newline at end of file
diff --git a/PDFiumSharp/Types/FPDF_FILEACCESS.cs b/PDFiumSharp/Types/FPDF_FILEACCESS.cs
index c2c5526..044115c 100644
--- a/PDFiumSharp/Types/FPDF_FILEACCESS.cs
+++ b/PDFiumSharp/Types/FPDF_FILEACCESS.cs
@@ -12,9 +12,9 @@
namespace PDFiumSharp.Types
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
- public delegate bool FileReadBlockHandler(IntPtr ignore, int position, IntPtr buffer, int size);
+ public delegate bool FileReadBlockHandler(IntPtr param, int position, IntPtr buffer, int size);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
- public delegate bool FileWriteBlockHandler(IntPtr ignore, IntPtr data, int size);
+ public delegate bool FileWriteBlockHandler(IntPtr self, IntPtr data, int size);
[StructLayout(LayoutKind.Sequential)]
public class FPDF_FILEREAD
@@ -26,11 +26,11 @@ public class FPDF_FILEREAD
readonly IntPtr _param;
- public FPDF_FILEREAD(int fileLength, FileReadBlockHandler readBlock)
+ public FPDF_FILEREAD(int fileLength, FileReadBlockHandler readBlock, IntPtr param = default)
{
_fileLength = fileLength;
_readBlock = readBlock;
- _param = IntPtr.Zero;
+ _param = param;
}
public static FPDF_FILEREAD FromStream(Stream stream, int count = 0)
@@ -38,17 +38,40 @@ public static FPDF_FILEREAD FromStream(Stream stream, int count = 0)
if (count <= 0)
count = (int)(stream.Length - stream.Position);
var start = stream.Position;
+
+ var memoryStream = stream as MemoryStream;
+ if (memoryStream != null) {
+ try {
+ memoryStream.GetBuffer();
+ } catch (UnauthorizedAccessException) {
+ // Buffer is not public, and thus not directly usable
+ memoryStream = null;
+ }
+ }
+
byte[] data = null;
- FPDF_FILEREAD fileRead = new FPDF_FILEREAD(count, (ignore, position, buffer, size) =>
- {
- stream.Position = start + position;
- if (data == null || data.Length < size)
- data = new byte[size];
- if (stream.Read(data, 0, size) != size)
- return false;
- Marshal.Copy(data, 0, buffer, size);
- return true;
- });
+ FPDF_FILEREAD fileRead = memoryStream != null
+ ? new FPDF_FILEREAD(count, (_, position, buffer, size) =>
+ {
+ if (position < 0 || size <= 0 || start+position+size > memoryStream.Length)
+ return false;
+ var streamBuffer = memoryStream.GetBuffer();
+ Marshal.Copy(streamBuffer, (int)start+position, buffer, size);
+ return true;
+ })
+ : new FPDF_FILEREAD(count, (_, position, buffer, size) =>
+ {
+ if (position < 0 || size <= 0)
+ return false;
+ stream.Seek(start + position, SeekOrigin.Begin);
+ if (data == null || data.Length < size)
+ data = new byte[size];
+ if (stream.Read(data, 0, size) != size)
+ return false;
+
+ Marshal.Copy(data, 0, buffer, size);
+ return true;
+ });
return fileRead;
}
}