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();
}
}
}
Result
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);
}
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.
- Call Windows Runtime APIs in desktop apps - Windows applications | Microsoft Docs
- Calling Windows APIs in .NET5 - Windows Developer Blog
- Windows.Data.Pdf Namespace - Windows UWP applications | Microsoft Docs
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>
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();
}
...
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();
}
}
}
}
}
Before
Result
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();
}
}
}
}
}
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
Printed data
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 :)
Top comments (0)