DEV Community

loading...

【.NET 5】【WPF】Edit and print PDF files 1

masanori_msl profile image Masui Masanori ・5 min read

Intro

In this time, I want to edit and print PDF files.

Environments

  • .NET ver.5.0.101
  • NLog ver.4.7.6
  • (For editing PDF) PdfSharpCore ver.1.2.10

Print files

For printing files in .NET, I can use "System.Drawing.Printing" and "System.Printing".

And I choose later one.

In .NET 5, I couldn't use it in Console applications.
And because it hadn't supported for ASP.NET Core, so I created a WPF application.

In this sample, the application gets file path from command line arguments, and print it on rendering MainWindow.
After that, it will close automatically.

MainWindow.xaml.cs

using System;
using System.IO;
using System.Printing;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media.Imaging;
using NLog;

namespace PdfPrintSample
{
    public partial class MainWindow : Window
    {
        Logger logger = LogManager.GetCurrentClassLogger();
        public MainWindow()
        {
            InitializeComponent();
        }        
        protected override async void OnContentRendered(EventArgs e)
        {
            base.OnContentRendered(e);

            // Get command line arguments
            var args = Environment.GetCommandLineArgs();
            if(args.Length <= 1)
            {
                logger.Error("No arguments");
                return;
            }
            var filePath = args[1];
            // TODO: load file.

            // Get print queue by name
            var printServer = new LocalPrintServer();
            var queue = printServer.GetPrintQueue("Microsoft Print to PDF");
            // Use default printer
            var defaultQueue = LocalPrintServer.GetDefaultPrintQueue();

            var samplePage = new FixedPage();
            var canvas = new Canvas();
            var sampleText = new TextBlock();
            sampleText.Text = "Hello";
            sampleText.FontSize = 32;
            canvas.Children.Add(sampleText);
            samplePage.Children.Add(canvas);

            // Print sample page
            var writer = PrintQueue.CreateXpsDocumentWriter(queue); 
            writer.Write(samplePage);

            // Close this application
            this.Close();
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Result

Alt Text

Print PDF file

For printing PDF files, I have to resolve one problem.
This sample only can print XPS files.

Though I can write like this, if the file of "filePath" is JPG or PDF, this code won't work.

var file = File.ReadAllBytes(filePath);
var printQueue = LocalPrintServer.GetDefaultPrintQueue();

using (var job = printQueue.AddJob())
using (var stream = job.JobStream)
{
    stream.Write(file, 0, file.Length);
}
Enter fullscreen mode Exit fullscreen mode

There are some libraries to resolve this, but I choosed using WinRT API.
I can convert from PDF file to "Image".
And I can treat it as XPS.

From Windows 10 version 1809, WinRT API are able to be called by not only UWP, but also WPF and WinForms.

And after .NET 5, all I have to do for using WinAPI are only change the "TargetFramework".

PdfPrintSample.csproj

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net5.0-windows10.0.19041.0</TargetFramework>
    <UseWPF>true</UseWPF>
  </PropertyGroup>
  <ItemGroup>
    <Content Update="Nlog.config" CopyToOutputDirectory="PreserveNewest"/>
  </ItemGroup>
  <ItemGroup>
    <PackageReference Include="NLog" Version="4.7.6"/>
    <PackageReference Include="PdfSharpCore" Version="1.2.10"/>
  </ItemGroup>
</Project>
Enter fullscreen mode Exit fullscreen mode

MainWindow.xaml.cs

        protected override async void OnContentRendered(EventArgs e)
        {
            base.OnContentRendered(e);

            var args = Environment.GetCommandLineArgs();
            if(args.Length <= 0)
            {
                 logger.Error("No arguments");
            }
            var filePath = args[1];
            using(var fileStream = new FileStream(filePath, FileMode.Open))
            using(var stream = fileStream.AsRandomAccessStream())
            {
                var document = await Windows.Data.Pdf.PdfDocument.LoadFromStreamAsync(stream);
                using(var page = document.GetPage(0))
                using(var memoryStream = new MemoryStream())
                using(var outputStream = memoryStream.AsRandomAccessStream())
                {
                    await page.RenderToStreamAsync(outputStream);
                    // Get bitmap from stream
                    var bitmap = BitmapDecoder.Create(memoryStream, BitmapCreateOptions.None,    
                            BitmapCacheOption.OnLoad).Frames[0];
                    // Get print queue by name
                    var printServer = new LocalPrintServer();
                    var queue = printServer.GetPrintQueue("Microsoft Print to PDF");

                    var image = new Image();
                    image.Source = bitmap;
                    // Print Image
                    var writer = PrintQueue.CreateXpsDocumentWriter(queue);                        
                    writer.Write(image);
                }
            }            
            this.Close();
         }
...
Enter fullscreen mode Exit fullscreen mode

Edit PDF

There are some libraries to edit PDF in .NET.
But most of them are But most of them are GPL or paid software.

If I use them in my private programming, I don't need warry so much.
But in my work, I have to think carefully.

This time, I try PDFSharpCore.

Draw rectangles and text

MainWindow.xaml.cs

...
using PdfSharpCore.Drawing;
using PdfSharpCore.Pdf;
using PdfSharpCore.Pdf.IO;

namespace PdfPrintSample
{
    public partial class MainWindow : Window
    {
...       
        protected override async void OnContentRendered(EventArgs e)
        {
...
            var contents = Edit(filePath);
            if(contents == null || contents.Length <= 0)
            {
                logger.Error("Failed Editing");
                return;
            }
            using(var memStream = new MemoryStream())
            {
                memStream.Write(contents, 0, contents.Length);
                using(var stream = memStream.AsRandomAccessStream())
                {
                    var document = await Windows.Data.Pdf.PdfDocument.LoadFromStreamAsync(stream);
...                 
                }
            }
            this.Close();
        }
        private byte[] Edit(string filePath)
        { 
            using(var document = PdfReader.Open(filePath, PdfDocumentOpenMode.Modify))
            {
                var graphics = XGraphics.FromPdfPage(document.Pages[0]);
                // Draw rectangle
                var pen = new XPen(XColors.HotPink, Math.PI);
                graphics.DrawRectangle(pen, 30, 20, 100, 60);
                // Draw text
                var font = new XFont( "Meiryo UI" , 32, XFontStyle.Regular);
                graphics.DrawString( "Hello World" , font, XBrushes.BlueViolet,
                        new XRect (70, 60, document.Pages[0].Width, document.Pages[0].Height), 
                        XStringFormats.Center);
                // Save as a file
                // In this sample, to pass the data to print operations without outputting it,
                // I save as a stream.
                //document.Save(@"C:\Users\example\Documents\PdfPrintSample\sample.pdf");

                // Save as a stream
                using(MemoryStream memStream = new MemoryStream()) 
                { 
                    document.Save(memStream, true);
                    return memStream.ToArray();
                }
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Before

Alt Text

Result

Alt Text

Edit TextField (Failed)

I also can edit TextField values like below.

MainWindow.xaml.cs

...
namespace PdfPrintSample
{
    public partial class MainWindow : Window
    {
...       
        private byte[] Edit(string filePath)
        { 
            using(var document = PdfReader.Open(filePath, PdfDocumentOpenMode.Modify))
            {
                // To display the changed text
                if(document.AcroForm.Elements.ContainsKey("/NeedAppearances") == false)
                {
                    document.AcroForm.Elements.Add("/NeedAppearances", new PdfBoolean(true));
                }
                else
                {
                    document.AcroForm.Elements["/NeedAppearances"] = new PdfBoolean(true);
                }
                // Set value
                document.AcroForm.Fields["SampleField2"].Value = new PdfString("Hello");

                // Save as a file
                document.Save(@"C:\Users\example\Documents\PdfPrintSample\sample.pdf");

                // Save as a stream
                using(MemoryStream memStream = new MemoryStream()) 
                { 
                    document.Save(memStream, true);
                    return memStream.ToArray();
                }
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Problem

Now, I got a problem.
The text field value was changed in the saved file.
But after printing, the value wasn't changed.

Saved file

Alt Text

Printed data

Alt Text

After saving the file, I open it by Adobe Acrobat DC and save.
After that I try printing it again.

The value is changed.

So shall I do anything to save PDFTextField values?
But I haven't found any informations to resolve it yet.

When I change the value by PDF-LIB (JavaScript library), the problem won't be occurred.

So I may be able to find hints from that :)

Discussion (0)

pic
Editor guide