sungaila / PDFtoImage

A .NET library to render PDF files into images.
https://www.sungaila.de/PDFtoImage/
MIT License
142 stars 14 forks source link

Type initializer exception on Android #59

Closed SashelI closed 5 months ago

SashelI commented 5 months ago

PDFtoImage version

3.0.0

OS

Android

OS version

Android 12+

Architecture

arm64

Framework

(Other)

App framework

.NET 4.6.2 (.NET standard 2.1)

Detailed bug report

Hi,

I am trying to use this lib in a very special context : Android Unity app.

We were, until now, using UWP methods to read a pdf file as a byte array. But now trying to migrate onto Android, this seemed to be the solution. In the editor, everything works great, but in the Quest 3 helmet (Android), we have this exception

The type initializer for 'PDFtoImage.PdfiumViewer.NativeMethods' threw an exception. [line: 105]

When calling this method

pageCount = Conversion.GetPageCount(documentStream, false);

with document stream :

using (UnityWebRequest www = UnityWebRequest.Get(pdfFileName))
{
                www.SendWebRequest();
                while (!www.isDone)
                {
                    if (www.result == UnityWebRequest.Result.ConnectionError)
                    {
                        throw new Exception($"Connection Error while reading File '{pdfFileName}'");
                    }
                }

                if (www.result == UnityWebRequest.Result.ConnectionError)
                {
                    throw new Exception($"Connection Error while reading File '{pdfFileName}'");
                }

                pdfData = www.downloadHandler.data;

                using (var documentStream = new MemoryStream(pdfData));

(the only way to access streamingassets files on android, but this code is also called in the editor and works.)

Do you have any clue on where this is coming from ?

There is no lib or dll error prior to this, and I have the Pdfium .so for Android arm64 imported.

sungaila commented 5 months ago

Hi @SashelI, do you have the full exception message?

Mono (and thus Unity) is not supported by this library. My guess is that the libpdfium.so for android-arm64 was not loaded correctly.

This code path is meant for .NET Framework on Windows. But since Unity behaves like .NET Framework 4.6.2, I would guess this part is executed and causes the Exception. It works in the Unity editor (Windows) but fails on the Quest (Android).

But we need the full exception message and stack trace to be certain.

SashelI commented 5 months ago

Well i don't have any other info in the message... This is just it @sungaila

16:38:11.379
Unity
[16:38:11.378] ASTROLABE - LoadAsync > The type initializer for 'PDFtoImage.PdfiumViewer.NativeMethods' threw an exception. [line: 105]
16:38:11.379
Unity
Astrolabe.Core.Framework.Pdf.<LoadAsync>d__16:MoveNext()
16:38:11.379
Unity
System.Runtime.CompilerServices.AsyncTaskMethodBuilder:Start(TStateMachine&)
16:38:11.379
Unity
Astrolabe.Core.Framework.Pdf.AndroidPdfGenerator:LoadAsync(String)
16:38:11.379
Unity
<LoadAsync>d__15:MoveNext()
16:38:11.379
Unity
System.Runtime.CompilerServices.AsyncTaskMethodBuilder:Start(TStateMachine&)
16:38:11.379
Unity
AndroidPdfOperationInstance:LoadAsync(String)
16:38:11.379
Unity
Astrolabe.Twinkle.<LoadPdfFileAsync>d__42:MoveNext()
16:38:11.379
Unity
System.Runtime.CompilerServices.AsyncTaskMethodBuilder:Start(TStateMachine&)
16:38:11.379
Unity
Astrolabe.Twinkle.LogicalPdfDocument:LoadPdfFileAsync(Uri)
16:38:11.379
Unity
Astrolabe.Twinkle.<LoadPdfFile>d__41:MoveNext()
16:38:11.379
Unity
System.Runtime.CompilerServices.AsyncVoidMethodBuilder:Start(TStateMachine&)
16:38:11.379
Unity
Astrolabe.Twinkle.LogicalPdfDocument:LoadPdfFile(Uri)
16:38:11.379
Unity
System.Reflection.RuntimeMethodInfo:Invoke(Object, BindingFlags, Binder, Object[], CultureInfo)
16:38:11.379
Unity
Astrolabe.Twinkle.IO.LogicalPropertyReflected:SetValue(ILogicalObject
SashelI commented 5 months ago

(Update : the same error occurs on UWP)

sungaila commented 5 months ago

Could you please put this into your code before calling GetPageCount:

Debug.WriteLine($"{RuntimeInformation.OSDescription} - {RuntimeInformation.OSArchitecture} - {RuntimeInformation.ProcessArchitecture} - {RuntimeInformation.FrameworkDescription}");

Please post the output for both the Unity IDE and on the Quest Pro.

SashelI commented 5 months ago

Could you please put this into your code before calling GetPageCount:

Debug.WriteLine($"{RuntimeInformation.OSDescription} - {RuntimeInformation.OSArchitecture} - {RuntimeInformation.ProcessArchitecture} - {RuntimeInformation.FrameworkDescription}");

Please post the output for both the Unity IDE and on the Quest Pro.

Thanks. Unity : Microsoft Windows NT 10.0.19045.0 - X64 - X64 - Mono 6.13.0 (Visual Studio built mono) Quest : Unix 5.10.168.0 - Arm64 - Arm64 - Mono Unity IL2CPP

UPDATE just got another message after this test, it being inside a "try-catch" seemed to have altered the exception. You were right, the code executes in NativeMethods and tries to load windows kernel...

DllNotFoundException: Unable to load DLL 'kernel32.dll'. Tried the load the following dynamic libraries: Unable to load dynamic library 'kernel32.dll' because of 'Failed to open the requested dynamic library (0x06000000) dlerror() = dlopen failed: library "kernel32.dll" not found
14:49:03.027
Unity
at PDFtoImage.PdfiumViewer.NativeMethods.LoadLibrary (System.String lpLibFileName) [0x00000] in <00000000000000000000000000000000>:0
sungaila commented 5 months ago

Thanks for posting the results!

You were right, the code executes in NativeMethods and tries to load windows kernel...

I'll provide you with preview binaries to test once I fix this. Right now the .NET Framework binaries work on Windows only and I have to add support for other platforms.

SashelI commented 5 months ago

I'll provide you with preview binaries to test once I fix this. Right now the .NET Framework binaries work on Windows only and I have to add support for other platforms.

Thanks !! Do you know how much time this will take ? No pressure at all i'm not asking you to be fast, it's just for my personal organization

sungaila commented 5 months ago

Can't estimate the time. Right now I am looking for a solution in SkiaSharp. Their native lib libSkiaSharp loads just fine for Mono/Unity and I wish to see how they have done that.

sungaila commented 5 months ago

Hey @SashelI, could you please give these binaries a try:

PDFtoImage.3.1.0-preview1.nupkg.zip

Make sure to copy libpdfium.so for Android into your app folder. It can be found here: bblanchon.PDFium.Android

Just open the nupkg as an archive and copy the file from runtimes\android-arm64\native\libpdfium.so into your Unity project.

SashelI commented 5 months ago

That was fast ! Will try later on, thanks.

SashelI commented 5 months ago

So I've tried with the new binaries, but it throws the same error

2024/01/18 14:40:00.862 8520 8542 Error Unity [14:40:00.861] ASTROLABE - LoadAsync > The type initializer for 'PDFtoImage.PdfiumViewer.NativeMethods' threw an exception. [line: 104]
2024/01/18 14:40:00.882 8520 8685 Error Unity PlatformNotSupportedException: Current platform is unknown, unable to load library 'pdfium.so'.
2024/01/18 14:40:00.882 8520 8685 Error Unity   at PDFtoImage.PdfiumViewer.LibraryLoader.LoadLibrary (System.String libraryName) [0x00000] in <00000000000000000000000000000000>:0 

Tried with pdfium.so from pdfium and pdfium v8, the lib is in the apk as intended. image

sungaila commented 5 months ago

@SashelI Please give this version a try: ~PDFtoImage.3.1.0-preview2.nupkg.zip~ PDFtoImage.3.1.0-preview3.nupkg.zip

Make sure that libpdfium.so is in the same directory as the executable of your Unity project. Otherwise it might fail to find that file.

Sorry for this trial and error approach. I don't have Unity myself so I need your help for testing. :-)

SashelI commented 5 months ago

Hey sorry for the delay, I'll test once I finish a big projet I'm currently on.

SashelI commented 5 months ago

Hi @sungaila , no lib error ! But still an error :') the message is : 2024/01/26 17:35:06.010 19581 19603 Error Unity [17:35:06.009] ASTROLABE - LoadAsync > To marshal a managed method, please add an attribute named 'MonoPInvokeCallback' to the method definition. The method we're attempting to marshal is: PDFtoImage.Internals.NativeMethods::FPDF_GetBlock [line: 97]

sungaila commented 5 months ago

@SashelI Good news, I setup a small Unity2D game and got this library running on my own Android device! :-) Hopefully this will work on the Meta Quest headset as well.

PDFtoImage.3.1.0-preview4.nupkg.zip

Make sure to include the .NET assemblies PDFtoImage.dll and SkiaSharp.dll in your project. Then include libpdfium.so and libSkiaSharp.so for each architecture you need. For the Meta Quest headset this should be ARM64.

Also set the Scripting Backend to IL2CPP because Mono doesn't work on any architecture but ARMv7.

SashelI commented 5 months ago

I just tested it and it works !! Good job.

sungaila commented 5 months ago

Awesome, thanks for helping in fixing this!

Right now I am looking into creating a package for Unity’s Package Manager. So there will be no need to copy dll and so files around anymore.

sungaila commented 5 months ago

@SashelI This library can now be imported into a Unity project with the Unity Package Manager. No need to copy dll and so files around. Just follow these steps:

  1. Open your project and navigate to WindowPackage Manager.
  2. Click on the + button (top-left corner) and select Add package from git URL....
  3. Enter the following URL and confirm with the Add button:
    https://github.com/sungaila/PDFtoImage.git?path=etc/UnityPackage

Please let me know if this isn't working as expected!