windows-runtimewinrt-xamlwinui-3xps

Print PDF or XPS with WinRT Print API


I decided to modernize our product and replace the old printer code with WINRT printing. In order to send something to the printer you need to call

printDoc.AddPage(printElement);

https://learn.microsoft.com/en-us/windows/windows-app-sdk/api/winrt/microsoft.ui.xaml.printing.printdocument.addpages?view=windows-app-sdk-1.4

Note tht this method doesn't have overload that accept anything other than UIElement. This means if you want to print something with the new print API you need to obtain UIElement instance. XPS or PDF format are not supported.

To get a UIElement from a pdf file document, I used the WINRT and rasterized a pdf page to a BitmapSource like that

        private async Task<Microsoft.UI.Xaml.UIElement> GetPdfImage(uint pageNumber)
        {
            PdfPageRenderOptions options = new PdfPageRenderOptions();

            using PdfPage page = pdfDoc.GetPage(Math.Max(0, pageNumber - 1));

            InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream();
            await page.RenderToStreamAsync(stream, options);
            BitmapImage src = new BitmapImage();



            await src.SetSourceAsync(stream);

            Microsoft.UI.Xaml.Controls.Image image = new Microsoft.UI.Xaml.Controls.Image();
            image.Source = src;
            image.Stretch = Microsoft.UI.Xaml.Media.Stretch.None;
            image.Width = (int)page.Size.Width;
            return image;
        }

As you can guess the printed text looks awful and blurry. The only way to be able to print high quality text is to pass XAML that looks like this

    <Grid x:Name="PrintableArea">
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="3*"/>
            <RowDefinition Height="3*"/>
            <RowDefinition Height="4*"/>З
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="6*" />
            <ColumnDefinition Width="4*"/>
        </Grid.ColumnDefinitions>

        <StackPanel x:Name="Header" Grid.Row="0" Grid.ColumnSpan="2" Height="75"  Visibility="Collapsed">
            <StackPanel Orientation="Horizontal" >
                <Image Source="ms-appx:///Assets/smallTile-sdk.png" HorizontalAlignment="Left" Stretch="None"/>
                <RichTextBlock Foreground="Black"  FontSize="20" TextAlignment="Left" FontFamily="Segoe UI">
                    <Paragraph>Windows SDK Sample</Paragraph>
                </RichTextBlock>
            </StackPanel>
            <RichTextBlock Foreground="Black" x:Name="SdkTitle" FontSize="22" FontWeight="Bold" FontFamily="Segoe UI" >
                <Paragraph>PrintSample</Paragraph>
            </RichTextBlock>
        </StackPanel>

        <RichTextBlock Foreground="Black" x:Name="TextSelection" FontSize="18" Grid.Row="1"  Grid.ColumnSpan="2" OverflowContentTarget="{Binding ElementName=FirstLinkedContainer}"
            IsTextSelectionEnabled="True" TextAlignment="Left" FontFamily="Segoe UI" VerticalAlignment="Top" HorizontalAlignment="Left" Visibility="Collapsed"/>

        <RichTextBlock Foreground="Black" x:Name="TextContent" FontSize="18" Grid.Row="1"  Grid.ColumnSpan="2" OverflowContentTarget="{Binding ElementName=FirstLinkedContainer}"
            IsTextSelectionEnabled="True" TextAlignment="Left" FontFamily="Segoe UI" VerticalAlignment="Top" HorizontalAlignment="Left">
            <Paragraph FontSize="32">Lorem ipsum dolor sit amet, consectetur</Paragraph>
            <Paragraph></Paragraph>
            <Paragraph >...</Paragraph>
            <Paragraph></Paragraph>
            <Paragraph >...</Paragraph>
            <Paragraph></Paragraph>
            <Paragraph >...</Paragraph>
            <Paragraph></Paragraph>
            <Paragraph>... inceptos himenaeos.</Paragraph>
            <Paragraph></Paragraph>
            <Paragraph >...</Paragraph>
        </RichTextBlock>
        <RichTextBlockOverflow x:Name="FirstLinkedContainer" OverflowContentTarget="{Binding ElementName=ContinuationPageLinkedContainer}" Grid.Row="2" Grid.Column="0"/>
        <RichTextBlockOverflow x:Name="ContinuationPageLinkedContainer" Grid.Row="3" Grid.ColumnSpan="2"/>
        <Image Source="ms-appx:///Assets/print_1.png" x:Name="ScenarioImage" HorizontalAlignment="Center" Grid.Row="2" Grid.Column="1" Margin="10"/>

        <StackPanel x:Name="Footer" Grid.Row="4" Grid.ColumnSpan="2">
            <TextBlock Foreground="Black" FontSize="16" TextAlignment="Left" FontFamily="Segoe UI">
                Copyright © Microsoft Corporation. All rights reserved.
            </TextBlock>
        </StackPanel>
    </Grid>
</Page>

My question is - am I do something wrong or the only way to print PDF, XPS with WinRT print API is to convert it to XAML?


Solution

  • There is a great sample posted by Simon Mourier in the comment section that contains the answer https://github.com/smourier/XpsPrintSamples