Failed to convert PDF to PNG in DotNet core 3.0 Windows Docker container

Hi,
We are getting the following error from GroupDocs library when converting a PDF to PNG. We are using AspDotNet core 3.0 in a Windows 2016 Docker container (mcr.microsoft.com/dotnet/core/aspnet:3.0-nanoserver-sac2016)

Exception -

GroupDocs.Viewer.Exceptions.GroupDocsViewerException: The type initializer for ‘Gdip’ threw an exception.
at . ​(Int32 , IPageStreamFactory )
at .(Int32[] , , PngViewOptions , IPageStreamFactory )
at . ​(Func2 , ViewOptions , Int32[] ) at .(Func2 , Int32[] , PngViewOptions )
at . ​(Func`2 , ViewOptions , Int32[] )
at GroupDocs.Viewer.Viewer.View(ViewOptions options, Int32[] pageNumbers)

This is a show-stopper for us. Any ideas to resolve / work around this?

I should mention that the same code works well when running outside a Docker container. We have pretty much copied the conversion code from the examples given in your website.

1 Like

@devam

Do you face this issue in case of a particular PDF? Or it happens for every other PDF file?
Can you please specify the GroupDocs.Viewer API version that you are evaluating (e.g. 20.1, 20.9)?

Are you working with this code? We’ll appreciate if you share your actual code (with code changes, if there are any).

@atirtahir3 Thanks for responding to the query. Here are my answers -

Do you face this issue in case of a particular PDF? Or it happens for every other PDF file?

It is failing for every file. Our use case is to convert all file types to PNG only.
This morning I tried converting some XML and JS files to PNG and the conversions failed with the following exception -

System.TypeInitializationException: The type initializer for '  ' threw an exception.
 ---> System.TypeInitializationException: The type initializer for 'SkiaSharp.SKImageInfo' threw an 
exception.
 ---> System.DllNotFoundException: Unable to load DLL 'libSkiaSharp' or one of its dependencies: 
The specified module could not be found. (0x8007007E)
at SkiaSharp.SkiaApi.sk_colortype_get_default_8888()
at SkiaSharp.SKImageInfo..cctor()
--- End of inner exception stack trace ---
at SkiaSharp.SKBitmap..ctor(Int32 width, Int32 height, Boolean isOpaque)
at   ..cctor()
--- End of inner exception stack trace ---
at   ..ctor(Int32 , Int32 , Single , Single ,     )
at  ​ . ()
at  ​ . (Stream )
at  ​ .(   , SizeF , Stream , ImageSaveOptions , IWarningCallback ,    )
at  ​ . (Stream )
at  ​ . ​   (    )
at    .     (    )
at Aspose.Words.Document.  (    )
at Aspose.Words.Document.​(Stream , String , SaveOptions )
at Aspose.Words.Document.Save(Stream stream, SaveOptions saveOptions)
at   .(Stream ,    )
at   .(Stream ,    )
at   .()
at   .()
at   .     ​()
at GroupDocs.Viewer.Viewer.(ViewInfoOptions )
at GroupDocs.Viewer.Viewer..()
at   .[TEntry](ICache , String , Func`1 )
at GroupDocs.Viewer.Viewer.GetViewInfo(ViewInfoOptions options)

Can you please specify the GroupDocs.Viewer API version that you are evaluating (e.g. 20.1, 20.9)?

We have installed version 20.8.0 via Nuget package manager.

Example code -

We have used the ThreadSafeFileCache from this example code, and the other bits from this example in GitHub. You can ignore the MinimumPagesToConvert bit as that is specific logic to our application.

            string outputDir = Path.Combine(Utility.GroupDocsOutputPath, data.DocumentId);
            string cacheDir = Path.Combine(outputDir, "cache");
            IKeyLockerStore keyLockerStore = new ConcurrentDictionaryKeyLockerStore(KeyLockerMap, cacheDir);
            ICache threadSafeCache = new ThreadSafeFileCache(new FileCache(cacheDir), keyLockerStore);
            var settings = new ViewerSettings(threadSafeCache);

            // Convert the file to PNG
            var retVal = new Dictionary<string, string>();
            string pageFilePathFormat = Path.Combine(outputDir, "page{0}.png");
            using (var viewer = new Viewer(filePath, settings))
            {
                ViewInfo vi = viewer.GetViewInfo(ViewInfoOptions.ForPngView(false));
                var options = new PngViewOptions(pageFilePathFormat);
                if (!string.IsNullOrEmpty(data.Watermark))
                    options.Watermark = new Watermark(data.Watermark);

                // MinimumPagesToConvert or entire document whichever is smaller
                viewer.View(
                    options, 
                    Enumerable.Range(1, vi.Pages.Count < MinimumPagesToConvert ? 
                        vi.Pages.Count : MinimumPagesToConvert).ToArray());

                retVal["TotalPages"] = vi.Pages.Count.ToString();
            }

            return Ok(retVal);

@devam

We are investigating this issue with ticket ID VIEWERNET-2767. As there’s any update, you’ll be notified.

@devam

There’s an update on VIEWERNET-2767.
Unfortunately, Nano Server is not supported due to Nano Server limitations. What are the workarounds?

  • Windows Server Core - We’ve found that PDF to PNG rendering is not working when running in Windows Server Core, this issue has been logged. While the image has a significantly larger on-disk footprint comparing to Nano Server it can be used to build and run .NET Framework applications. Please check the docker_viewer_win.zip (708.4 KB) that is targeting .NET Framework and running in Windows Server Core.
  • Linux - Here we still can reproduce issues with rendering PDF files but the image size is much smaller comparing to Windows Server Core. We’re actively improving Linux support. So, we would recommend using Linux containers instead of Windows. The attached .NET Core 3.1 docker_viewer_linux.zip (11.1 KB) contains Dockerfile with all required dependencies installed. We’re using the same Dockerfile to run our tests.

@atirtahir3 - thanks once again for your prompt and detailed response. We are however a Windows shop and would therefore need to head towards server core base image. Can we can use your DLL in a self contained aspnet dotnet core web api application as opposed to using .Net Framework?

@devam

We’re looking into this use-case, you’ll be notified about the outcomes.

@devam

Sure, the GroupDocs.Viewer can be used with self-contained .NET Core apps when running in Windows Server Core Docker Container. Please check the sample .NET Core application web_api.zip (17.2 KB) as a demonstration.

Here are the steps to run the app:

  • Unpack the archive
  • Create artifacts by running dotnet publish -r win10-x64 -o artifacts - the self-contained application will be created in the artifacts folder.
  • Build the container image by running docker build -t groupdocs-viewer:docker_viewer_win_core . . Note instruction EXPOSE 5000 and environment variable ENV ASPNETCORE_URLS=http://*:5000 in the Dockerfile.
  • Run the container image docker run -it --rm groupdocs-viewer:docker_viewer_win_core
  • Inspect container IP docker inspect --format '{{ .NetworkSettings.Networks.nat.IPAddress }}' <container>
  • Test Viewer endpoint <IP>:5000/viewer/1 - the first page of the DOC contained in the web_api.zip (17.2 KB) archive in PNG format will be returned in the response.

Let us know if it’s helpful.

Hi @Atir_Tahir,
Unfortunately our tests with a self contained aspnet core web api app on a microsoft/windowsservercore:ltsc2016 image failed, with the following exception of Unable to load DLL 'gdiplus.dll' or one of its dependencies: The specified module could not be found. (0x8007007E) -

2020-10-26 22:32:24.151 +00:00 [ERR] Failed to process view request
GroupDocs.Viewer.Exceptions.GroupDocsViewerException: The type initializer for 'Gdip' threw an 
exception.
   at   .     ​(Int32 , IPageStreamFactory )
   at   .(Int32[] ,    , PngViewOptions , IPageStreamFactory )
   at   .     ​(Func`2 , ViewOptions , Int32[] )
   at   .(Func`2 , Int32[] , PngViewOptions )
   at   .     ​(Func`2 , ViewOptions , Int32[] )
   at GroupDocs.Viewer.Viewer.View(ViewOptions options, Int32[] pageNumbers)

Details -

System.TypeInitializationException: The type initializer for 'Gdip' threw an exception.
 ---> System.DllNotFoundException: Unable to load DLL 'gdiplus.dll' or one of its dependencies: The 
specified module could not be found. (0x8007007E)
at System.Drawing.SafeNativeMethods.Gdip.GdiplusStartup(IntPtr& token, StartupInput& input, 
StartupOutput& output)
at System.Drawing.SafeNativeMethods.Gdip..cctor()
--- End of inner exception stack trace ---
at System.Drawing.SafeNativeMethods.Gdip.GdipLoadImageFromStream(IStream stream, IntPtr& 
image)
at System.Drawing.Image.FromStream(Stream stream, Boolean useEmbeddedColorManagement, 
Boolean validateImageData)
at System.Drawing.Image.FromStream(Stream stream, Boolean useEmbeddedColorManagement)
at System.Drawing.Image.FromStream(Stream stream)
at   .(Stream , String )
at   .(Stream , Boolean , String )
at   .()
at  .(Object )
at  .(Object )
at  .(MethodBase , Boolean )
at  . ()
at  .(Boolean )
at  .(Object )
at  .(Object )
at  .()
at  .(Object , UInt32 )
at  .(Boolean )
at  .(Object[] , Type[] , Type[] , Object[] )
at  .(Stream , String , Object[] , Type[] , Type[] , Object[] )
at  .(Stream , String , Object[] )
at  .(Stream , String , Object[] )
at   .     ​ (LoadOptions , BaseViewOptions )
at   .     ​(String , Stream , LoadOptions , BaseViewOptions )
at GroupDocs.Viewer.Viewer.(LoadOptions , BaseViewOptions )
at GroupDocs.Viewer.Viewer.(BaseViewOptions )
at GroupDocs.Viewer.Viewer.(ViewInfoOptions )
at GroupDocs.Viewer.Viewer..()
at   .[TEntry](ICache , String , Func`1 )
at GroupDocs.Viewer.Viewer.GetViewInfo(ViewInfoOptions options)

The docker file looks like this -

#escape=`
# base image
FROM microsoft/windowsservercore:ltsc2016
# Copy the current directory contents into the container at C:\
COPY . /
RUN mkdir C:\GroupDocsViewer\Output
RUN mkdir C:\GroupDocsViewer\Storage 
# app dir in image
WORKDIR /app
EXPOSE 80
ENTRYPOINT ["SureDropRestAPI.exe"]

Any pointers on how to get this working?

@devam

As per this exception, you are missing gdiplus.dll or this DLL file is not properly added in the project. It doesn’t seem a back-end API related issue. Please try to add gdiplus.dll reference in your project and let us know if issue persists.

Hi @Atir_Tahir,
Thank you for your response. We have resolved this, and other similar exceptions by including the complete aspnetcore-runtime inside our container based on windowsservercore:ltsc2016. However, these DLLs (like gdiplus.dll) are required by GroupDocs and therefore it sounds natural that GroupDocs DLL should include its dependencies in the Nuget package.

Anyway just for the information of anyone else who lands up with the same issue, we solved these dependencies problems by changing our dockerfile to this:

#escape=`
# base image
FROM microsoft/windowsservercore:ltsc2016

RUN powershell -Command "mkdir C:\dotnet"

RUN powershell -Command "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;Invoke-WebRequest https://download.visualstudio.microsoft.com/download/pr/acdf7879-d422-4195-90a1-1f4a6e8550da/e75bde72d8790d478a698ad40f390b3e/aspnetcore-runtime-3.1.9-win-x64.zip -outfile C:\dotnet\aspnetcore-runtime-3.1.9-win-x64.zip"

RUN powershell -Command "Expand-Archive -Path C:\dotnet\aspnetcore-runtime-3.1.9-win-x64.zip -DestinationPath c:\dotnet"

ENV DOTNET_ROOT=c:\dotnet

ENV PATH=c:\dotnet;%PATH%

ENV DOTNET_MULTILEVEL_LOOKUP=0

ENV ASPNETCORE_URLS http://*:80

# app dir in image
COPY . /

COPY icacls.exe /app

WORKDIR /app

EXPOSE 80

ENTRYPOINT ["SureDropRestAPI.exe"]
1 Like

Hi @devam,

Please share the code snippet and the sample file that you’re using for further investigation of this issue.

Hi @vladimir.litvinchik,

TL;DR

It would be dandy if GroupDocs DLL can include all its dependencies into the Nuget package so that when a user installs GroupDocs into their dotnet core project, they don’t get these missing DLL exceptions shown in my previous comments.

Longer response -

The code is the same as my previous comment in this thread. Initially we were using http://mcr.microsoft.com/dotnet/core/aspnet:3.0-nanoserver-sac2016 as our base image. Based on @Atir_Tahir’s comment about being unable to support nano servers, we changed the base image to windows server core but still encountered the same missing DLL problems. Therefore we explicitly added the aspnetcore-runtime into our self contained dotnet core executable as I showed in previous comment.

Now while describing this to you, I can’t help but wonder if the same work-around of including the runtime would work on the nano server image as well. :thinking: We’ll definitely give that a try as we don’t like the enormous on-disk footprint of microsoft/windowsservercore:ltsc2016.

@devam

We’ll look into this scenario and notify you.

@devam

We couldn’t reproduce this issue DLL ‘gdiplus.dll’ or one of its dependencies: The specified module could not be found. (0x8007007E) with the sample code here. Please share the sample application and file to reproduce the issue.

Can you make the Linux docker_viewer_linux.zip available again? It says I have to be topic owner or staff to view.

@lonnie.h

Sure, the link to docker_viewer_linux.zip and the link to the updated version of the same application.

Let me also add a Dockerfile

# docker build -t groupdocs-viewer:sample-app .
# docker run -it --rm  groupdocs-viewer:sample-app

FROM mcr.microsoft.com/dotnet/core/sdk:3.1 as build

# libgdiplus
RUN apt-get update \
    && apt-get install -y apt-transport-https dirmngr gnupg ca-certificates \
    && apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF \
    && "deb https://download.mono-project.com/repo/debian stable-buster main" | tee /etc/apt/sources.list.d/mono-official-stable.list \
    && apt-get update \
    && apt-get install -y libgdiplus

# fonts
RUN sed -i'.bak' 's/$/ contrib/' /etc/apt/sources.list \
    && apt-get update \ 
    && apt-get install -y ttf-mscorefonts-installer fontconfig \
    && fc-cache -f -v

WORKDIR /app
COPY docker_viewer_linux.csproj ./
RUN dotnet restore

COPY . . 
ENTRYPOINT ["dotnet", "run", "docker_viewer_linux.csproj"]

And project file

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="GroupDocs.Viewer" Version="22.1.1" />
    <PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="2.80.3" />
  </ItemGroup>

  <ItemGroup>
    <None Update="sample.docx">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
  </ItemGroup>

</Project>

The details on limitations when running on Linux can be also found at .NET Standard 2.0 API Limitations | Documentation.