PDFsharp Watermark - c#

I am making an application that creates a watermark on a PDF that the user selects and I can't seem to get the watermark to appear on the selected PDF but I also get no errors. Any help would be appreciated.
I am using PDFsharp version 1.50.4000
public void WaterMarkPDF(string sourceFileName)
{
try
{
string watermark = "watermark";
int emSize = 100;
string file ="test.pdf";
File.Copy(sourceFileName, file, true);
File.SetAttributes(file, File.GetAttributes(file) & ~FileAttributes.ReadOnly);
// Take in pdf from the form
PdfDocument document = PdfReader.Open(file);
// change the version cause sometimes newer versions break it
if (document.Version < 14)
document.Version = 14;
XFont font = new XFont("Times New Roman", emSize, XFontStyle.BoldItalic);
for (int idx = 0; idx < document.Pages.Count; idx++)
{
var page = document.Pages[idx];
// Get an XGraphics object for drawing beneath the existing content.
var gfx = XGraphics.FromPdfPage(page, XGraphicsPdfPageOptions.Prepend);
// Get the size (in points) of the text.
var size = gfx.MeasureString(watermark, font);
// Define a rotation transformation at the center of the page.
gfx.TranslateTransform(page.Width / 2, page.Height / 2);
gfx.RotateTransform(-Math.Atan(page.Height / page.Width) * 180 / Math.PI);
gfx.TranslateTransform(-page.Width / 2, -page.Height / 2);
// Create a string format.
var format = new XStringFormat();
format.Alignment = XStringAlignment.Near;
format.LineAlignment = XLineAlignment.Near;
// Create a dimmed red brush.
XBrush brush = new XSolidBrush(XColor.FromArgb(128, 255, 0, 0));
// Draw the string.
gfx.DrawString(watermark, font, brush,
new XPoint((page.Width - size.Width) / 2, (page.Height - size.Height) / 2),
format);
// Save the document...
document.Save(file);
// ...and start a viewer.
Process.Start(file);
}
}
catch (Exception e)
{
throw e;
}
}

Maybe try XGraphicsPdfPageOptions.Appendinstead of XGraphicsPdfPageOptions.Prepend.
Call document.Save and Process.Startoutside the for loop.
Update: Explanation: With XGraphicsPdfPageOptions.Prepend the watermark is drawn below the original PDF page. Most PDF files consist of black text on transparent background and the watermark will be visible there (you can check this by activating the Transparency Grid in Adobe Reader). For PDF pages with a solid background (e.g. images, tables with a background colour, ...) the watermark will not be visible.
The PDFsharp source code includes a Watermark sample:
http://pdfsharp.net/wiki/Watermark-sample.ashx
There are two variants that add a semi-transparent text on top of the existing PDF page. These variants also work for PDF pages without transparency.

Related

issue in adding watermark to pdf file using pdfsharp C#

I am trying to make a orders page and there i have added a button which fetches some data and i am trying to display that data in pdf file and generate a pdf file. the data is being displayed correctly but i cant get the watermark on pdf.
here's the code
using (MemoryStream stream = new MemoryStream())
{
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
PdfDocument document = new PdfDocument();
PdfPage page = document.AddPage();
XFont font = new XFont("Verdana", 20, XFontStyle.Bold);
DrawWatermark(page, font);
XGraphics gfx = XGraphics.FromPdfPage(page);
string total = "Total price: " + ord.Totalprice;
string remark = "Remark: " + ord.Remark;
string status = "Payment status: " + ord.PaymentStatus;
gfx.DrawString(total, font, XBrushes.Black, 10, 100);
gfx.DrawString(remark, font, XBrushes.Black, 10, 135);
gfx.DrawString(status, font, XBrushes.Black, 10, 170);
document.Save(stream, false);
return File(stream.ToArray(), "application/pdf", "Invoice.pdf");
}
another method
void DrawWatermark(PdfPage page, XFont font)
{
string watermark = "CartMart";
// Variation 2: Draw a watermark as an outlined graphical path.
// NYI: Does not work in Core build.
// Get an XGraphics object for drawing beneath the existing content.
var gfx1 = XGraphics.FromPdfPage(page, XGraphicsPdfPageOptions.Append);
// Get the size (in points) of the text.
var size = gfx1.MeasureString(watermark, font);
// Define a rotation transformation at the center of the page.
gfx1.TranslateTransform(page.Width / 2, page.Height / 2);
gfx1.RotateTransform(-Math.Atan(page.Height / page.Width) * 180 / Math.PI);
gfx1.TranslateTransform(-page.Width / 2, -page.Height / 2);
// Create a graphical path.
var path = new XGraphicsPath();
// Create a string format.
var format = new XStringFormat();
format.Alignment = XStringAlignment.Near;
format.LineAlignment = XLineAlignment.Near;
// Add the text to the path.
// AddString is not implemented in PDFsharp Core.
path.AddString(watermark, font.FontFamily, XFontStyle.BoldItalic, 150,
new XPoint((page.Width - size.Width) / 2, (page.Height - size.Height) / 2),
format);
// Create a dimmed red pen.
var pen = new XPen(XColor.FromArgb(128, 255, 0, 0), 2);
// Stroke the outline of the path.
gfx1.DrawPath(pen, path);
gfx1.Dispose();
}
there is no watermark in pdf file. can somebody help me with the code. i want to display data as well and i dont know whats going on. There is no error as well.
Seems you mixed up a bit variations 2 and 3 from here. In variation 2, you should use XGraphicsPdfPageOptions.Prepend to draw beneath instead of above.
Also, if you're using .NET Core, it seems only variation 1 works. Looks like there is a port for Core here but no idea what is the status of this development.

PDFsharp DrawString Text Invisible for certain PDF

The system I am working on stamps PDF's with certain information. It does this by creating a lime green text box in the top right corner of the document. It then draws a certain string on top of the green space. This works for thousands of PDFs but for one the text is invisible even though the box is drawn. I can still select the text and copy it to something else, but it is invisible in the PDF.
Unfortunately, I cannot share the PDF but it is a PDF 1.4. What would cause this?
The code for stamping:
private static XGraphics drawString(XGraphics xgr, PdfPage page, string printString, int pageNumber = 0)
{
XFont font = new XFont("Verdana", 10, XFontStyle.BoldItalic);
var textSize = xgr.MeasureString(printString, font);
var width = textSize.Width;
var height = textSize.Height;
double xMin = 0;
double yMin = 0;
if (page.Rotate == 90)
{
xMin = page.Height - textSize.Width;
var state = xgr.Save();
xgr.DrawRectangle(XBrushes.LimeGreen, xMin, yMin, width, height);
xgr.Restore(state);
xgr.DrawString(printString, font, XBrushes.Black, new XRect(0, 0, page.Height, page.Width), topRight());
}
else
{
xMin = page.Width - textSize.Width;
var state = xgr.Save();
xgr.DrawRectangle(XBrushes.LimeGreen, xMin, yMin, width, height);
xgr.Restore(state);
xgr.DrawString(printString, font, XBrushes.Black, new XRect(0, 0, page.Width, page.Height), topRight());
}
return xgr;
}
private static XStringFormat topRight()
{
XStringFormat format = new XStringFormat();
format.Alignment = XStringAlignment.Far;
format.LineAlignment = XLineAlignment.Near;
return format;
}
I have tried using Dipose() on xgr and reinitialising it before each of its draw actions. I have tried saving and restoring the state of xgr between draw actions as seen in the code. I have tried various fonts and font sizes with no luck either.
Let me know what metadata about the PDF is relevant and I will share that.
Using PdfSharp 1.5 GDI, I have been having this issue as well. Some pdfs would be ok, others would have 1 or 2 pages that were ok, and then others would have no pages that were ok. Text could be selected, but text could not be seen. Changing the renderMode fixes the issue.
In PdfGraphicsState.cs, there is a condition to check if _realizedRenderingMode != renderMode. However, _realizedRenderingMode and renderMode is 0 by default, so it never enters codeblock, nor does it look like there is a method for changing the renderMode unless you change the font to italic, bold, strikeout, or underline:
int _realizedRenderingMode; // Reference: TABLE 5.2 Text state operators / Page 398
public void RealizeFont(XFont font, XBrush brush, int renderingMode)
{
const string format = Config.SignificantFigures3;
// So far rendering mode 0 (fill text) and 2 (fill, then stroke text) only.
RealizeBrush(brush, _renderer._colorMode, renderingMode, font.Size); // _renderer.page.document.Options.ColorMode);
// Realize rendering mode.
if (_realizedRenderingMode != renderingMode)
{
_renderer.AppendFormatInt("{0} Tr\n", renderingMode);
_realizedRenderingMode = renderingMode;
}
Removing the condition would suffice in fixing the issue, but the renderingMode seems to only be needed 1 time for the BT (Begin Text) in XGraphicsPdfRenderer.cs.
internal void BeginTextMode()
{
if (_streamMode != StreamMode.Text)
{
_streamMode = StreamMode.Text;
_content.Append("BT\n");
// Text matrix is empty after BT
_gfxState.RealizedTextPosition = new XPoint();
_gfxState.ItalicSimulationOn = false;
}
}
So, I ended up modifying XGraphics to include XGraphicsPdfRendererOptions and passing the variable to the various methods so it can be changed regardless of location:
private XGraphicsPdfRendererOptions _renderOptions { get; set; }
public XGraphicsPdfRendererOptions RenderOptions
{
get
{
if (_renderOptions == null)
{
_renderOptions = new XGraphicsPdfRendererOptions();
}
return _renderOptions;
}
set
{
_renderOptions = value;
}
}
Keep in mind that the renderMode is originally based on whether the font is italic, bold, strikeout, or underline, which I don't really see how those relate to renderMode, in XGraphicsPdfRenderer.cs:
//bool bold = (font.Style & XFontStyle.Bold) != 0;
//bool italic = (font.Style & XFontStyle.Italic) != 0;
bool italicSimulation = (font.GlyphTypeface.StyleSimulations & XStyleSimulations.ItalicSimulation) != 0;
bool boldSimulation = (font.GlyphTypeface.StyleSimulations & XStyleSimulations.BoldSimulation) != 0;
bool strikeout = (font.Style & XFontStyle.Strikeout) != 0;
bool underline = (font.Style & XFontStyle.Underline) != 0;
Realize(font, brush, boldSimulation ? 2 : 0);
Class and enum:
public class XGraphicsPdfRendererOptions
{
public XGraphicsPdfRenderMode RenderMode { get; set; }
public bool IncludeRenderModeForPage { get; set; }
public bool IncludeRenderModeForObject { get; set; }
}
public enum XGraphicsPdfRenderMode
{
Text_Render_Mode_Fill = 0,
Text_Render_Mode_Stroke = 1,
Text_Render_Mode_Fill_Stroke = 2,
Text_Render_Mode_Invisible = 3
}
Usage:
gfx.RenderOptions = new XGraphicsPdfRendererOptions() { RenderMode = XGraphicsPdfRenderMode.Text_Render_Mode_Fill, IncludeRenderModeForPage = true };
https://github.com/zaryk/PDFsharp
I've happened across this issue a couple of times now on separate projects. I found that the simplest fix is to dispose of the XGraphics object and then simply re-instantiate it using your current PdfPage instance.
PdfSharp.Pdf.PdfPage Page = Document.AddPage();
PdfSharp.Drawing.XGraphics gfx = PdfSharp.Drawing.XGraphics.FromPdfPage(Page);
//Build your pdf here until transparency issue occurs
//Issue has occured so re-instantiate gfx object
gfx.Dispose();
gfx = PdfSharp.Drawing.XGraphics.FromPdfPage(Page);
//Continue as normal
Below is the code I use to hide something in document and edit the document and again save. Let me know if it helps you
private PdfDocument FormatPdfDocument(PdfDocument document, List<string> packingTypes, string carrierName)
{
XFont PackingTypeFont = new XFont("Calibri", 10, XFontStyle.Bold);
var i = 0;
foreach (PdfPage page in document.Pages)
{
using (var gfx = XGraphics.FromPdfPage(page))
{
var packingType = packingTypes.ElementAtOrDefault(i++) ?? "PackingType Not Found";
if (carrierName == "xxxx")
{
var packingTypeBounds = new XRect(64, 62, 200, 12);
gfx.DrawRectangle(XBrushes.White, packingTypeBounds);
gfx.DrawString(packingType, PackingTypeFont, XBrushes.Black, packingTypeBounds, XStringFormats.TopLeft);
var logoBounds = new XRect(0, 0, 130, 50);
gfx.DrawRectangle(XBrushes.White, logoBounds);
}
else if (carrierName == "yyyy")
{
var packingTypeBounds = new XRect(200, 0, 200, 12);
gfx.DrawString(packingType, PackingTypeFont, XBrushes.Black, packingTypeBounds, XStringFormats.TopLeft);
}
else if (carrierName == "zzzz")
{
var packingTypeBounds = new XRect(410, 20, 200, 12);
var state = gfx.Save();
gfx.RotateAtTransform(90, new XPoint { X = 410, Y = 20 });
gfx.DrawString(packingType, PackingTypeFont, XBrushes.Black, packingTypeBounds, XStringFormats.TopLeft);
gfx.Restore(state);
}
}
}
return document;
}
This works fine for me till date without any issues
Some possible causes to answer your question "What would cause this?":
Are you using the latest version PDFsharp 1.50 beta 3b?
IIRC there is a bug in 1.32 that can lead to unexpected behavior because some properties are not reset.
Since you see the rectangle this might be the cause.
Since you see the rectangle, this probably does not apply:
There are two ways of modifying existing pages: "append" and "prepend". Your code snippet does not show how you do it.
With "prepend" your lime rectangle could be hidden under a white filled rectangle. Watch the PDF in Adobe Reader with active Transparency Grid and check that you see the grid where the rectangle should be.
Since you see the rectangle, this probably does not apply:
Maybe your text goes to the wrong position. Check the MediaBox and CropBox settings of the PDF page you are modifying. Normally pages start at (0,0), but you cannot be sure.
Locate your text in the PDF file and compare the text position with MediaBox and CropBox.
It could be an unknown bug in PDFsharp. If you do not find a PDF that allows to replicate the issue which you can share then it will be very difficult to fix the bug. But maybe one of the options above leads to success.
So I managed to find a way to make it work. I ran the stamping process twice on the document and it worked as expected. Fortunately, stamping twice does not affect regular documents which actually work normally.
Still an issue in 1.50 for some PDF files...
As a workaround, I create a PNG file using System.Drawing.DrawString.
Bitmap bmp = new Bitmap((int)p.Width.Point, 30);
Graphics gra = Graphics.FromImage(bmp);
gra.DrawString("Hello world", new Font("Verdana", 20), Brushes.Red, new PointF(0, 0));
bmp.Save("test.png", System.Drawing.Imaging.ImageFormat.Png);
Then I use XGraphics.DrawImage from this PNG file.
XGraphics xg = XGraphics.FromPdfPage(p, XGraphicsPdfPageOptions.Append);
XImage xi = XImage.FromFile("test.png");
// Add it at the bottom of the page
xg.DrawImage(xi, 0, p.Height-32, p.Width, 30);
This always ends up on top.

Pdf landscape orientation using MeasureString

I am using wkHTMLtoPDF and I want to change orientation on the PDF based on the length of the headers in the HTML, but I'm not sure if it's the right way to do this.
private const double A4Width = 2480; // A4 pixel width
Landscape detection method
private bool IsLandscape(string html)
{
int start = html.IndexOf("<th>");
int end = html.LastIndexOf("</th>") - start;
string tableHeadings = html.Substring(start, end).Replace("<th>", string.Empty).Replace("</th>", string.Empty);
FontFamily fontFamily = new FontFamily("Arial");
Font font = new Font(fontFamily, 13);
var size = MeasureString(tableHeadings, font);
if(size.Width > A4Width)
{
return true;
}
return false;
}
Method for font calculus
private SizeF MeasureString(string content, Font font)
{
SizeF result = SizeF.Empty;
using (var image = new Bitmap(1,1))
{
using (var g = Graphics.FromImage(image))
{
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
result = g.MeasureString(content, font, int.MaxValue, StringFormat.GenericTypographic);
}
}
return result;
}
Note: This code is in a library so that is the reason I have used this question to measure string.
Update
To be short:
I am looking for a best practice of implementing the problem.
The problem summary:
I am building from HTML a PDF that has a table inside.
I get the table columns (eg: Col1, Col2) and remove the tags (eg: Col1Col2).
Next I want to calculate the size (in px) of that string result (using a Font with a specified text size), and if that size excedes the A4 portrait size than rotate the PDF to the landscape orientation.
I have found that the best implementation of this problem is to add to wkHTMLtoPDF an attribute to force the PDF to be A4 size.
Attribute used : --page-size A4
Also I have changed the code that checks if the measured width size is grater than the A4 size.
if(size.Width > (A4Width / 4))
{
return true;
}

iText - new lines in ColumnText

I'm working on program that insert some kind of watermark on existing pdf document. The watermark isn't image but text generated during program's work. I need to rotate the text, send it to center of page, and those things are done, but the text has many lines. So I put new line character "\n" in Phrase but PDF prints only characters before first use of "\n". How could I overlay some text on pdf document (mainly scanned docs) and rotate it?
My source code (simplified):
PdfContentByte canvas = stamper.GetOverContent(1);
String message = "some\n multiline \n expression";
Phrase text = new Phrase(text);
ColumnText.ShowTextAligned(canvas, Element.ALIGN_MIDDLE, text, size.Width / 2, size.Height / 2, 30);
That is documented: the ShowTextAligned() method is for single lines only. If you want multiple lines, you need to use a ColumnText object, define a Rectangle using the SetSimpleColumn() method and draw the content using the Go() method.
If you want to rotate such a Rectangle, you need to create a PdfTemplate first. Render the ColumnText to this PdfTemplate and add the template to the document.
Adding the template to the document can be done with the AddTemplate() method. In this case, you need some algebra to rotate the template. This is the algebra you'll need for the parameters:
a = cosine(angle);
b = sine(angle);
c = -sine(angle);
d = cosine(angle);
e = x;
f = y;
If you can't get the algebra working, you can wrap the template inside an Image object:
Image img = Image.getInstance(template);
Now you have different methods to introduce a rotation, including a simple one that merely requires an angle in degrees.
P.S.: wrapping a template inside an image doesn't "rasterize" the text. It creates a Form XObject (as opposed to an Image XObject).
I solved the problem in another way:
using (PdfStamper stamper = new PdfStamper(reader, ms))
{
PdfContentByte canvas = stamper.GetOverContent(1);
String message = String.Format("mutli\nline\text");
}
PdfGState gState = new PdfGState();
gState.FillOpacity = 0.3f;
canvas.SetGState(gState);
using (StringReader stringReader = new StringReader(message))
{
string line;
float y = size.Height / 2 + 200;
while ((line = stringReader.ReadLine()) != null)
{
Phrase p = new Phrase(line,FontFactory.GetFont(FontFactory.HELVETICA, 35));
ColumnText.ShowTextAligned(canvas, Element.ALIGN_CENTER, p, 300, y, 30);
y = y - 70;
}
}
}

Calculating an emSize value for a font so that text fills an area

I have been pulling my hair out on this one for ages now, so thought I'd try to get some help...
I'm working with PDFsharp (may or may not be significant) and trying to put a watermark into a pdf document when it gets downloaded via my asp.net web app.
My problem comes with that the user needs to be able to define the text in the watermark, so I can't use a fixed text size, also, the pdf page size can change.
Assuming that I'm using Arial and bold, and can find the page width in cm/mm/inch/pt, how do I calculate the font's emSize that is needed so that whatever text is entered grows/shrinks to fill the width?
The PDFsharp XFont constructor takes font name and emSize.
Edit:
Many thanks for the suggestions guys, this is what I implemented in the end:
PdfDocument doc = PdfReader.Open(stream, PdfDocumentOpenMode.Modify);
foreach (PdfPage page in doc.Pages)
{
double watermarkWidth = (Math.Sqrt(Math.Pow(page.Height.Point, 2) + Math.Pow(page.Width.Point, 2)));
//reduce by 10% so that the wording doesn't go right into the corners
watermarkWidth -= (watermarkWidth / 10);
XGraphics gfx = XGraphics.FromPdfPage(page, XGraphicsPdfPageOptions.Prepend);
double emSize = 150;
XFont font = new XFont("Arial", emSize, XFontStyle.Bold);
XSize size = gfx.MeasureString(watermark, font);
while (size.Width > watermarkWidth && emSize > 10)
{
emSize -= 5;
font = new XFont("Arial", emSize, XFontStyle.Bold);
size = gfx.MeasureString(watermark, font);
}
gfx.TranslateTransform(page.Width / 2, page.Height / 2);
gfx.RotateTransform(-Math.Atan(page.Height / page.Width) * 180 / Math.PI);
gfx.TranslateTransform(-page.Width / 2, -page.Height / 2);
XGraphicsPath path = new XGraphicsPath();
path.AddString(watermark, font.FontFamily, XFontStyle.Bold, emSize,
new XPoint((page.Width - size.Width) / 2, (page.Height - size.Height) / 2),
XStringFormats.Default);
XPen pen = new XPen(XColor.FromArgb(75, 255, 0, 0), 2);
gfx.DrawPath(pen, path);
}
doc.Save(stream, false);
Is the length of the watermark (in characters) limited? If so, you can take the allowed number of characters and calculate the width of strings like "###...", "WWW..." and "MMM..." in a loop with decreasing font sizes until it fits.
Then you will have a single font size that can be used for all texts.
The watermark will then be smaller - if you calculate the width for 30 "#" and the user just enters "Top Secret" ...
Better method: let the user enter the watermark, then use a loop with decreasing font sizes until the desired text fits into the available space.
Maybe this way (i dont know how PDFsharp works exactly, but in a normal Forms or Wpf this code would deliver the biggest possible Font)
public Font GetFont(Panel Wathermark, string textToPaint)
{
Font result = new Font("Bold", 1);
using (Graphics measure = Wathermark.CreateGraphics())
{
SizeF tempSize;
tempSize = measure.MeasureString(textToPaint, result);
while (tempSize.Width < Wathermark.Width && tempSize.Height < Wathermark.Height)
{
tempSize = measure.MeasureString(textToPrint, result);
var tempSizeToIncrease = result.Size; //writeprotected
result = new Font(result.Name, tempSizeToIncrease += 0.1f);
}
}
return result;
}

Categories

Resources