Skip to content

Commit

Permalink
different form fill info allocation to fix mem read issues
Browse files Browse the repository at this point in the history
  • Loading branch information
Modest-as committed Sep 27, 2023
1 parent 03191d3 commit 184a2f0
Show file tree
Hide file tree
Showing 8 changed files with 118 additions and 39 deletions.
32 changes: 0 additions & 32 deletions src/Docnet.Core/Bindings/DocumentWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ internal sealed class DocumentWrapper : IDisposable

public FpdfDocumentT Instance { get; private set; }

private FPDF_FORMFILLINFO _formInfo;
private FpdfFormHandleT _formHandle;

public DocumentWrapper(string filePath, string password)
{
Instance = fpdf_view.FPDF_LoadDocument(filePath, password);
Expand Down Expand Up @@ -47,42 +44,13 @@ public DocumentWrapper(FpdfDocumentT instance)
}
}

public FpdfFormHandleT GetFormHandle()
{
if (_formHandle != null)
{
return _formHandle;
}

_formInfo = new FPDF_FORMFILLINFO();

for (var i = 1; i <= 2; i++)
{
_formInfo.version = i;

_formHandle = fpdf_view.FPDFDOCInitFormFillEnvironment(Instance, _formInfo);

if (_formHandle != null)
{
break;
}
}

return _formHandle;
}

public void Dispose()
{
if (Instance == null)
{
return;
}

if (_formHandle != null)
{
fpdf_view.FPDF_ExitFormFillEnvironment(_formHandle);
}

fpdf_view.FPDF_CloseDocument(Instance);

Marshal.FreeHGlobal(_ptr);
Expand Down
45 changes: 45 additions & 0 deletions src/Docnet.Core/Bindings/FormWrapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using System;
using System.Runtime.InteropServices;

namespace Docnet.Core.Bindings
{
internal sealed class FormWrapper : IDisposable
{
private readonly IntPtr _ptr;

public FormWrapper(DocumentWrapper docWrapper)
{
var formInfo = new FPDF_FORMFILLINFO();

_ptr = Marshal.AllocHGlobal(Marshal.SizeOf(formInfo));

for (var i = 1; i <= 2; i++)
{
formInfo.version = i;

Marshal.StructureToPtr(formInfo, _ptr, false);

Instance = fpdf_view.FPDFDOCInitFormFillEnvironment(docWrapper.Instance, _ptr);

if (Instance != null)
{
break;
}
}
}

public FpdfFormHandleT Instance { get; private set; }

public void Dispose()
{
if (Instance != null)
{
fpdf_view.FPDF_ExitFormFillEnvironment(Instance);
}

Marshal.FreeHGlobal(_ptr);

Instance = null;
}
}
}
4 changes: 2 additions & 2 deletions src/Docnet.Core/Bindings/PdfiumWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1833,7 +1833,7 @@ internal static extern IntPtr FPDF_LoadDocument(
[SuppressUnmanagedCodeSecurity]
[DllImport("pdfium", CallingConvention = CallingConvention.Cdecl,
EntryPoint = "FPDFDOC_InitFormFillEnvironment")]
internal static extern IntPtr FPDFDOC_InitFormFillEnvironment(IntPtr document, FPDF_FORMFILLINFO formInfo);
internal static extern IntPtr FPDFDOC_InitFormFillEnvironment(IntPtr document, IntPtr formInfo);

[SuppressUnmanagedCodeSecurity]
[DllImport("pdfium", CallingConvention = CallingConvention.Cdecl,
Expand Down Expand Up @@ -2325,7 +2325,7 @@ public static FpdfBitmapT FPDFBitmapCreate(int width, int height, int alpha)
return __result0;
}

public static FpdfFormHandleT FPDFDOCInitFormFillEnvironment(FpdfDocumentT document, FPDF_FORMFILLINFO formInfo)
public static FpdfFormHandleT FPDFDOCInitFormFillEnvironment(FpdfDocumentT document, IntPtr formInfo)
{
var __arg0 = ReferenceEquals(document, null) ? IntPtr.Zero : document.__Instance;

Expand Down
1 change: 1 addition & 0 deletions src/Docnet.Core/Models/RenderFlags.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ namespace Docnet.Core.Models
[Flags]
public enum RenderFlags
{
None = 0x00, // None
RenderAnnotations = 0x01, // FPDF_ANNOT: Set if annotations are to be rendered.
OptimizeTextForLcd = 0x02, // FPDF_LCD_TEXT: Set if using text rendering optimized for LCD display. This flag will only take effect if anti-aliasing is enabled for text.
NoNativeText = 0x04, // FPDF_NO_NATIVETEXT: Don't use the native text output available on some platforms
Expand Down
12 changes: 7 additions & 5 deletions src/Docnet.Core/Readers/PageReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,8 @@ private byte[] WriteImageToBufferInternal(RenderFlags flags, byte[] result = nul
throw new DocnetException($"result array length should be greater or equal than {length}");
}

FormWrapper formWrapper = null;

try
{
// | | a b 0 |
Expand All @@ -253,18 +255,16 @@ private byte[] WriteImageToBufferInternal(RenderFlags flags, byte[] result = nul
clipping.Bottom = 0;
clipping.Top = height;

FpdfFormHandleT formHandle = null;

if (flags.HasFlag(RenderFlags.RenderAnnotations))
{
formHandle = _docWrapper.GetFormHandle();
formWrapper = new FormWrapper(_docWrapper);
}

fpdf_view.FPDF_RenderPageBitmapWithMatrix(bitmap, _page, matrix, clipping, (int)flags);

if (flags.HasFlag(RenderFlags.RenderAnnotations) && formHandle != null)
if (flags.HasFlag(RenderFlags.RenderAnnotations) && formWrapper?.Instance != null)
{
fpdf_view.FPDFFFLDraw(formHandle, bitmap, _page, 0, 0, width, height, PageRotate.Normal, flags);
fpdf_view.FPDFFFLDraw(formWrapper.Instance, bitmap, _page, 0, 0, width, height, PageRotate.Normal, flags);
}

var buffer = fpdf_view.FPDFBitmapGetBuffer(bitmap);
Expand All @@ -278,6 +278,8 @@ private byte[] WriteImageToBufferInternal(RenderFlags flags, byte[] result = nul
}
finally
{
formWrapper?.Dispose();

fpdf_view.FPDFBitmapDestroy(bitmap);
}
}
Expand Down
Binary file not shown.
Binary file added src/Docnet.Tests.Integration/Docs/simple_7.pdf
Binary file not shown.
63 changes: 63 additions & 0 deletions src/Docnet.Tests.Integration/PageReaderTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Buffers;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Docnet.Core.Converters;
Expand Down Expand Up @@ -110,6 +111,11 @@ public void PageIndex_WhenCalled_ShouldReturnCorrectIndex(Input type)

ExecuteForDocument(type, "Docs/simple_0.pdf", null, 10, 10, index, pageReader =>
{
if (pageReader == null)
{
throw new ArgumentNullException(nameof(pageReader));
}

Assert.Equal(index, pageReader.PageIndex);
});
}
Expand Down Expand Up @@ -409,6 +415,63 @@ public void WriteImageToBufferWithResultFromArrayPoolNoRenderFlags_WhenCalled_Sh
});
}

[Theory]
[InlineData(RenderFlags.RenderAnnotations, "Docs/annotation_1.pdf", null, 0, 0)]
[InlineData(RenderFlags.RenderAnnotations, "Docs/annotation_1.pdf", null, 0, 1)]
[InlineData(RenderFlags.RenderAnnotations, "Docs/annotation_1.pdf", null, 0, 2)]
[InlineData(RenderFlags.RenderAnnotations, "Docs/annotation_1.pdf", null, 0, 3)]
[InlineData(RenderFlags.RenderAnnotations, "Docs/annotation_1.pdf", null, 0, 4)]
[InlineData(RenderFlags.RenderAnnotations, "Docs/annotation_1.pdf", null, 0, 5)]
public void Parallel_GetImage_WhenCalledWithFlags_ShouldWork(RenderFlags flags, string filePath, string password, int pageIndex, int run)

Check warning on line 425 in src/Docnet.Tests.Integration/PageReaderTests.cs

View workflow job for this annotation

GitHub Actions / Test MacOS

Theory method 'Parallel_GetImage_WhenCalledWithFlags_ShouldWork' on test class 'PageReaderTests' does not use parameter 'run'. Use the parameter, or remove the parameter and associated data. (https://xunit.net/xunit.analyzers/rules/xUnit1026)

Check warning on line 425 in src/Docnet.Tests.Integration/PageReaderTests.cs

View workflow job for this annotation

GitHub Actions / Test Linux

Theory method 'Parallel_GetImage_WhenCalledWithFlags_ShouldWork' on test class 'PageReaderTests' does not use parameter 'run'. Use the parameter, or remove the parameter and associated data.

Check warning on line 425 in src/Docnet.Tests.Integration/PageReaderTests.cs

View workflow job for this annotation

GitHub Actions / Testing Windows

Theory method 'Parallel_GetImage_WhenCalledWithFlags_ShouldWork' on test class 'PageReaderTests' does not use parameter 'run'. Use the parameter, or remove the parameter and associated data.
{
var numbers = Enumerable.Range(1, 1000);

Parallel.ForEach(numbers, i =>
{
ExecuteForDocument(Input.FromFile, filePath, password, 1, pageIndex, pageReader =>
{
var bytes = pageReader.GetImage(flags).ToArray();

Assert.NotEmpty(bytes);

// WriteToPPM($"{run}_{i}_image", bytes, pageReader.GetPageHeight(), pageReader.GetPageWidth());
});
});
}

/// <summary>
/// BGRA data to PPM file
/// Alpha channel discarded
/// https://netpbm.sourceforge.net/doc/ppm.html
/// </summary>
private static void WriteToPPM(string fileName, byte[] data, int height, int width)
{
fileName = $"{fileName}.ppm";

using (var writer = new StreamWriter(fileName))
{
writer.WriteLine("P6");
writer.WriteLine($"{width} {height}");
writer.WriteLine("255");
}

using var writerB = new BinaryWriter(new FileStream(fileName, FileMode.Append));

for (var i = 0; i < data.Length / 4; i++)
{
var j = i * 4;

var blue = data[j];
var green = data[j + 1];
var red = data[j + 2];
var alpha = data[j + 3];

writerB.Write((byte)((green * alpha + byte.MaxValue * (255 - alpha)) >> 8)); // G
writerB.Write((byte)((blue * alpha + byte.MaxValue * (255 - alpha)) >> 8)); // B
writerB.Write((byte)((red * alpha + byte.MaxValue * (255 - alpha)) >> 8)); // R
}
}

private static int GetNonZeroByteCount(Input type, string filePath, LibFixture fixture)
{
using (var reader = fixture.GetDocReader(type, filePath, null, 1000, 1000))
Expand Down

0 comments on commit 184a2f0

Please sign in to comment.