3

I'm using Microsoft's PrintHelper(Microsoft.Toolkit.Uwp.Helpers.PrintHelper) to print the pdf for my app. Now printing actually works by rendering the pdf into a BitmapImage and thereby printing the BitmapImage. This works fine for small number of pages. But when the number of pages increases, the memory consumption for rendering the BitmapImages goes high. Although I use garbage collection the memory is not released after the print operation. When the printing is performed a quite more number of times, the app gets crashed.

Is there any way to reduce this memory leak or to make the printing efficient?

Here is my code:

In this, the function GetImages() returns the BitmapImages. PrintPreview is the grid where the BitmapImages are displayed.

    public async Task PrintAsync()
    {
        GC.Collect();
        foreach (var image in await GetImages())
        {
            Grid grid = new Grid();
            var printpreview = new PrintPreview(image);
            grid.Children.Add(printpreview);

            printHelper.AddFrameworkElementToPrint(grid); printpreview = null;
            grid = null;
            GC.Collect();
        }
    }

Even though I force collect GC after the printing operation, the memory is not released.

    private void PrintHelper_OnPrintSucceeded()
    {
        GC.Collect();
        printHelper.ClearListOfPrintableFrameworkElements();
        printHelper.Dispose();
        GC.SuppressFinalize(this);

    }

Edit:

Below is the GetImages() function.

    public async Task<IEnumerable<ImageSource>> GetImages(int qualityRatio = 2)
    {
        GC.Collect();
        if (pdf_stream != null)
        {
            StorageFolder myfolder = ApplicationData.Current.LocalFolder;
            StorageFile pdf_file;
            pdf_file = await myfolder.CreateFileAsync(filename + ".pdf", CreationCollisionOption.GenerateUniqueName);
            using (Stream outputStream = await pdf_file.OpenStreamForWriteAsync())
            {
                await pdf_stream.AsStreamForRead().CopyToAsync(outputStream);
            }
            var result = new List<ImageSource>();
            try
            {
                var document = await PdfDocument.LoadFromFileAsync(pdf_file);
                for (uint i = 0; i < document.PageCount; i++)
                {
                    using (var page = document.GetPage(i))
                    {
                        using (var memoryStream = new MemoryStream())
                        {
                            using (var randomStream = memoryStream.AsRandomAccessStream())
                            {
                                var image = new BitmapImage();
                                var options = new PdfPageRenderOptions
                                {
                                    DestinationWidth = (uint)page.Size.Width * (uint)qualityRatio,
                                    DestinationHeight = (uint)page.Size.Height * (uint)qualityRatio
                                };
                                await page.RenderToStreamAsync(randomStream, options);
                                image.SetSource(randomStream);
                                result.Add(image);
                            }
                        }
                    }
                }
            }
            catch { }
            return result;
        }
        return null;
    }

PrintPreview.xaml

<Page
x:Class="App.src.PrintPreview"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

<Grid>
    <Image Source="{Binding}"   Stretch="Uniform" Margin="0"></Image>
</Grid>

PrintPreview.xaml.cs

public sealed partial class PrintPreview : Page
{
    public PrintPreview(ImageSource image)
    {
        this.InitializeComponent();
        this.DataContext = image;
    }
}
  • Can you show more code snippet about GetImages() and PrintPreview() method? And if you just add Grid in a large number of pages instead of BitmapImage, will the same issue occur? – Faywang - MSFT Nov 18 '19 at 08:03
  • @Faywang-MSFT I have updated the question with the code snippets asked. – Sangeetha M Nov 18 '19 at 10:07

1 Answers1

1

By testing, you can extra create a Image control, first pass BitmapImage to Image.Source, and then use randomStream to set Source, it will save some memory.

MainPage.xaml:

<Image x:Name="Output" Visibility="Collapsed" ></Image>

MainPage.cs:

using (var page = document.GetPage(i))
{
    var stream = new InMemoryRandomAccessStream();
    await page.RenderToStreamAsync(stream);
    BitmapImage src = new BitmapImage();
    Output.Source = src; // pass bitmapImage to Source
    await src.SetSourceAsync(stream);
    stream.Dispose();
    result.Add(src);
}

In addition, I found using urisource will save more memory compared to using stream to load bitmapImages. But you need to convert every page from pdf to StorageFile and save it in temp folder, then use UriSource to convert it to bitmapImage. It is more complicated, but you can also try it to check.

Faywang - MSFT
  • 5,798
  • 1
  • 5
  • 8