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; } }