I was working with the team on some reports requirements, and we started to get some requests for Watermarks on reports. Some of the requests were to get the same report with a watermark: same time stamp, same data, same printed by, etc.
One option would have been to have the Crystal reports template to support watermarks (using parameters for example), but then it meant implementing the support in every template.
After thinking about it, I thought “Heh! We always print from server, using PDF! There must be some way to put a watermark on a PDF, right?”
…
Right!
Easy said, but not easily done. But I recalled that Abbott Informatics is providing a PDFSharp.dll in the bin folder of the application folder. The dlls in there are typically used by the server runtime and not necessarily accessed by user code… But I did some research, and it should be possible to use that DLL to do exactly what I wanted!
STARLIMS has a few neat functions like LimsNetConnect() and LimsNetCast() which allows us to use DLL directly.
So I started by trying to load a PDF:
document := LimsNetConnect("PDFSharp.dll", "PdfSharp.Pdf.IO.PdfReader",, .T.):Open(sPDF);
Unfortunately, PDFSharp.dll is not loaded/accessible directly by the server side scripting, so I got an error.
Next step: copy the DLL to the Components directory!
Try again: success!
Finally, all I had to do is convert c# code to STARLIMS Scripting Language code (SSL) and magic happened!
Here’s the final code below.
Parameters are the “input” PDF file and the Watermark Text. Returns the Watermarked PDF.
:PARAMETERS sPDF, sWatermark;
:DEFAULT sPDF, "c:\temp\TestWatermark.pdf";
:DEFAULT sWatermark, "COPY";
:DECLARE sOut, sWatermark;
:DECLARE gfx, prependOption, document, page, font, size, format, brush, color,
math, width, height, ratio;
/* Define the output file name. For now, let's just insert _Watermarked at the end ;
sOut:= Left(sPDF, Rat(".", sPDF) -1) + "_Watermarked.PDF";
/* We will need to do math to rotate the text, so let's get ready!;
math := LimsNetConnect("", "System.Math",, .T.);
/* LimsNetCast allows us to get a value from an enum. That's about the simplest way to get to this;
prependOption := LimsNetCast("Prepend", "enum:PdfSharp.Drawing.XGraphicsPdfPageOptions");
fontOption := LimsNetCast("BoldItalic", "enum:PdfSharp.Drawing.XFontStyle");
nearOption := LimsNetCast("Near", "enum:PdfSharp.Drawing.XStringAlignment");
/* create necessary objects to get started, like the document, the font of the watermark, and the color;
document := LimsNetConnect("PDFSharp.dll", "PdfSharp.Pdf.IO.PdfReader",, .T.):Open(sPDF);
font := LimsNetConnect("PDFSharp.dll", "PdfSharp.Drawing.XFont", {"Times New Roman", 100, fontOption});
color := LimsNetConnect("PDFSharp.dll", "PdfSharp.Drawing.XColor",, .T.):FromArgb(64, 0, 0, 0);
/* then generate the watermark for every pages! ;
:FOR i:=1 :TO document:Pages:Count;
page := document:Pages[i - 1];
/* graphic canvas will be the page, and we will put the watermark "under" the text (prepend);
gfx := LimsNetConnect("PDFSharp.dll", "PdfSharp.Drawing.XGraphics",,.T.):FromPdfPage(page, prependOption);
size := gfx:MeasureString(sWatermark, font);
width := page:Width:Value;
height := page:Height:Value;
ratio := width / height;
/* Trigonometry anyone? This is to rotate our text;
gfx:TranslateTransform(width/2, height/2);
gfx:RotateTransform(-math:Atan(ratio) * 180 / math:PI);
gfx:TranslateTransform(-width/2, -height/ 2);
/* Create a string format ;
format := LimsNetConnect("PDFSharp.dll", "PdfSharp.Drawing.XStringFormat");
format:Alignment = nearOption;
format:LineAlignment = nearOption;
/* Create a dimmed red brush ;
brush := LimsNetConnect("PDFSharp.dll", "PdfSharp.Drawing.XSolidBrush", { color });
point := LimsNetConnect("PDFSharp.dll", "PdfSharp.Drawing.XPoint", { (width - size:Width) / 2, (height - size:Height) / 2});
/* Draw the string on page;
gfx:DrawString(sWatermark, font, brush, point, format);
:NEXT;
/* that's it, we save! ;
document:Save(sOut);
:RETURN sOut;
Turns out this is quite fast! If needed, you could then expose this script as a web service (REST anyone?) and you turn your STARLIMS into a PDF Watermark Service.
Uh. What a great idea I just got here 🙂