Inconsistent Character Rendering in PDF Page Images Using PdfPage.RenderToStreamAsync in .NET MAUI

Bharathi Selvam 0 Reputation points
2025-10-09T18:14:39.9666667+00:00

We are using the PdfDocument API to access PDF pages and convert them into images using the PdfPage.RenderToStreamAsync method, in our .NET MAUI application. However, we are facing an issue where some characters are missing in the rendered images. Specifically, in our case, the first letter of the customer name in the SHIPTO section is missing.

Upon further investigation, we found that the rendered image stream size varies for the same page when rendered multiple times, which seems to be causing the inconsistency. This issue occurs only with specific PDFs and is not consistently reproducible, it typically appears after viewing one PDF, and then affects others.

We have attached a sample project with PDFs, issue screenshots, and stream size details to help replicate the issue.

Please find the sample and screenshots from the link below:

https://www.syncfusion.com/downloads/support/directtrac/general/ze/Sample

Steps to Reproduce:

  1. Launch the sample and click the PDF 3 button.
    • Check the stream size and verify if the image is rendered correctly.
    1. Then click PDF 1 and PDF 2.
      • These may show the issue (missing characters).
  2. Relaunch the sample and click PDF 1 and PDF 2 first.
    • if the images are render correctly.
  3. Then click PDF 3.
    • It may now show the issue.

Could you please review this issue and provide guidance or a resolution? Let us know if you need any additional details for further investigation.

Developer technologies | .NET | .NET MAUI
{count} votes

1 answer

Sort by: Most helpful
  1. Michael Le (WICLOUD CORPORATION) 7,795 Reputation points Microsoft External Staff Moderator
    2025-10-10T07:23:26.6+00:00

    Hello @Bharathi Selvam ,

    Thank you for the detailed question and all the context you provided.

    After reviewing your code and testing the sample, the issue stems from improper resource management and stream handling in the PDF conversion process.

    Quickest Fix

    If you want to keep most of your existing code, just add garbage collection before rendering:

    private async void ConvertAndLoadImage(Stream? pdfStream)
    {        
        if (pdfStream != null)
        {
            // ... your existing code stays the same ...
           
            PdfPage page = m_document.GetPage(0);
           
            // Add this line to clear any lingering state
            GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect();
           
            PdfPageRenderOptions? pdfPageRenderOptions = new PdfPageRenderOptions();
            // ... rest of your code unchanged
        }
    }
    

    I'd recommend trying this first since it requires minimal changes. Keep in mind this is more of a workaround than a proper fix - it might resolve the character rendering issue, but some of the underlying problems could still persist.

    If You Want a More Complete Fix

    If the quick fix doesn't work or you want to address the root causes, I have tried to break it down:

    Stream Handling Issue:

    Your current approach creates multiple intermediate streams which can cause memory fragmentation:

    InMemoryRandomAccessStream randomAccessStream = new InMemoryRandomAccessStream();
    pdfStream.Position = 0;
    MemoryStream contentStream = new MemoryStream();
    pdfStream.CopyTo(contentStream);
    

    A cleaner approach is to read directly into a byte array:

    var pdfBytes = new byte[resourceStream.Length];
    await resourceStream.ReadAsync(pdfBytes, 0, pdfBytes.Length);
     
    var randomAccessStream = new InMemoryRandomAccessStream();
    using (var outputStream = randomAccessStream.GetOutputStreamAt(0))
    {
        using (var dataWriter = new DataWriter(outputStream))
        {
            dataWriter.WriteBytes(pdfBytes);
            await dataWriter.StoreAsync();
            await dataWriter.FlushAsync();
        }
    }
    

    Async Method Pattern Issue:

    The current fire-and-forget pattern can mask exceptions and make debugging harder:

    private async void ConvertAndLoadImage(Stream? pdfStream) // async void is mainly for event handlers
    

    It is better to implement proper error handling:

    private async Task<bool> ConvertAndLoadImage(string resourceName, string outputName)
    {
        try
        {
            await resourceStream.ReadAsync(pdfBytes, 0, pdfBytes.Length);
            return true; // lets you know if it worked
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine($"Error: {ex.Message}");
            return false;
        }
    }
    

    Hardcoded Dimensions Issue:

    Your current approach uses some magic numbers:

    pdfPageRenderOptions.DestinationWidth = 956;  // where do these come from?
    pdfPageRenderOptions.DestinationHeight = 1237;
    

    Using consistent dimensions might help with the rendering inconsistency:

    GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect();
     
    var renderOptions = new PdfPageRenderOptions
    {
        DestinationWidth = 1200,  // consistent fixed dimensions
        DestinationHeight = 1600
    };
    

    Complete Revised Implementation:

    If you want to implement all the improvements, this is what the full method would look like:

    private async Task<bool> ConvertAndLoadImage(string resourceName, string outputName)
    {
        try
        {
            var assembly = typeof(App).GetTypeInfo().Assembly;
            using var resourceStream = assembly.GetManifestResourceStream(resourceName);
           
            if (resourceStream == null) return false;
     
            var pdfBytes = new byte[resourceStream.Length];
            await resourceStream.ReadAsync(pdfBytes, 0, pdfBytes.Length);
     
            var randomAccessStream = new InMemoryRandomAccessStream();
            using (var outputStream = randomAccessStream.GetOutputStreamAt(0))
            using (var dataWriter = new DataWriter(outputStream))
            {
                dataWriter.WriteBytes(pdfBytes);
                await dataWriter.StoreAsync();
                await dataWriter.FlushAsync();
            }
     
            var pdfDocument = await PdfDocument.LoadFromStreamAsync(randomAccessStream);
            var page = pdfDocument.GetPage(0);
     
            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();
     
            var renderOptions = new PdfPageRenderOptions
            {
                DestinationWidth = 1200,
                DestinationHeight = 1600
            };
     
            var renderStream = new InMemoryRandomAccessStream();
            await page.RenderToStreamAsync(renderStream, renderOptions);
     
            var filePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), $"{outputName}.png");
            using (var fileStream = new FileStream(filePath, FileMode.Create))
            {
                renderStream.Seek(0);
                using (var readStream = renderStream.AsStreamForRead())
                {
                    await readStream.CopyToAsync(fileStream);
                }
            }
     
            renderStream?.Dispose();
            randomAccessStream?.Dispose();
           
            return true;
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine($"Error: {ex.Message}");
            return false;
        }
    }
    

    Please test this implementation and let me know:

    1. If the stream sizes are now consistent across renders
    2. Whether the character rendering issue persists

    I've tested it on my side and it seems to work properly. However, if the character clipping continues after these changes, it might indicate a deeper issue within the WinRT PDF rendering pipeline that requires further investigation.

    I hope this helps resolve the issue.


Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.