简体   繁体   中英

How to reduce memory consumption after printing a pdf file in UWP app

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;
    }
}

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.

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;
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM