I am generating watermarked PDFs using the Print to PDF
feature in Windows 10 or later, but I’m facing an issue where the watermark flips or appears mirrored when printed or viewed. I am using the iText7 library to add the watermark to the PDFs, and the following code is used for watermark generation:
try
{
using var inputStream = new MemoryStream(pdfBytes);
using var outputStream = new MemoryStream();
var writerProperties = password != null ? CreateEncryptedWriterProperties(password) : new WriterProperties();
using var reader = new PdfReader(inputStream);
using var writer = new PdfWriter(outputStream, writerProperties);
using var document = new PdfDocument(reader, writer);
var pageCount = document.GetNumberOfPages();
for (int i = 1; i <= pageCount; i++)
{
var page = document.GetPage(i);
var canvas = new PdfCanvas(page.NewContentStreamAfter(), page.GetResources(), document);
DrawWatermark(canvas, watermarkText, document, page);
}
document.Close();
return outputStream.ToArray();
}
catch (Exception ex)
{
_logger?.LogError(ex, "Error watermarking PDF bytes");
return null;
}
private void DrawWatermark(PdfCanvas canvas, string watermarkText, PdfDocument document, PdfPage page)
{
var pageSize = page.GetPageSize();
var (pageWidth, pageHeight) = (pageSize.GetWidth(), pageSize.GetHeight());
var dateText = _options.IncludeDate ? DateTime.Now.ToString(_options.DateFormat) : string.Empty;
var font = string.IsNullOrEmpty(_options.FontPath) ? PdfFontFactory.CreateFont() : PdfFontFactory.CreateFont(_options.FontPath);
var position = CalculateWatermarkPosition(watermarkText, dateText, pageWidth, pageHeight, _options.FontSize, font);
ApplyWatermark(canvas, watermarkText, dateText, position, font, pageWidth, pageHeight);
}
private (float TextX, float TextY, float DateX, float DateY, float TextWidth, float DateWidth) CalculateWatermarkPosition(
string watermarkText,
string dateText,
float pageWidth,
float pageHeight,
float fontSize,
PdfFont font)
{
var textWidth = (font.GetWidth(watermarkText) * fontSize) / 1000;
var dateWidth = (font.GetWidth(dateText) * (fontSize / 2)) / 1000;
var centerX = pageWidth / 2f;
var centerY = pageHeight / 2f;
float verticalSpacing = fontSize / 4;
return (
TextX: centerX - textWidth / 2f,
TextY: centerY,
DateX: centerX - dateWidth / 2f,
DateY: centerY - fontSize - verticalSpacing,
TextWidth: textWidth,
DateWidth: dateWidth
);
}
private void ApplyWatermark(
PdfCanvas canvas,
string watermarkText,
string dateText,
(float TextX, float TextY, float DateX, float DateY, float TextWidth, float DateWidth) position,
PdfFont font,
float pageWidth,
float pageHeight)
{
var centerX = pageWidth / 2f;
var centerY = pageHeight / 2f;
var angleInRadians = (float)(_options.RotationAngle * Math.PI / 180);
canvas.SaveState();
var gs = new PdfExtGState().SetFillOpacity(_options.Opacity);
canvas.SetExtGState(gs);
canvas.BeginText()
.SetFontAndSize(font, _options.FontSize)
.SetFillColor(_options.Color)
.SetTextRenderingMode(PdfCanvasConstants.TextRenderingMode.FILL_STROKE);
var cosAngle = (float)Math.Cos(angleInRadians);
var sinAngle = (float)Math.Sin(angleInRadians);
var tx = position.TextX - centerX;
var ty = position.TextY - centerY;
var finalX = centerX + (tx * cosAngle - ty * sinAngle);
var finalY = centerY + (tx * sinAngle + ty * cosAngle);
canvas.SetTextMatrix(
cosAngle, sinAngle,
-sinAngle, cosAngle,
finalX, finalY
)
.ShowText(watermarkText);
canvas.EndText()
.RestoreState();
}
The issue appears to happen only with PDFs generated using the "Print to PDF" feature of Windows 10 or later. PDFs generated by other means do not exhibit this problem.
You use
var canvas = new PdfCanvas(page.NewContentStreamAfter(), page.GetResources(), document);
to construct the PdfCanvas
.
Consider using
var canvas = new PdfCanvas(page, true);
instead. The bool
parameter value true
causes iText to wrap the existing contents in a save-graphics-state/restore-graphics-state envelope.
(Depending on the exact way you want the watermark to appear, it might be advantageous to use page.SetIgnorePageRotationForContent
with a matching parameter before that.)