Watermark your PDF in STARLIMS

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?


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;
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);

/* that's it, we save! ;


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 🙂

Circular Network Relationship

Sometimes, one may want to try to find relationships, let’s say, between products. When thinking about this, I thought “Hey! Wouldn’t it be cool if I could sort of create a circle of all the products, and then link them together as a network, so I would see who has both product A and product B?”

And, if you’re familiar with Tableau, that’s the kind of question that leads from one thing to another… Now, don’t get me wrong: I don’t think the below is a good visualization. But it was a great challenge!


The key here is to remember how Sin(), Cos() and the infamous Pi() functions work so you can layout the products in circle. Then, you need to do some self-join to have those relationships

Part 1: Circle Coordinates (back to the basics!)

1. you need to pick a field you wish to layout on your circle. I recommend creating a calculated field named “Radial Field”, so the next steps are easier. In my case, I’ll use [Product Name].

2. Calculate the X position using this formula: COS(INDEX()*2*PI()/window_sum(countd([Radial_Field])))

3. Calculate the Y position using this formula: SIN(INDEX()*2*PI()/window_sum(countd([Radial_Field])))

4. Put your X on Columns, and Y on Rows

5. Change the table calculations of both fields to compute using “Radial Field”

6. Put “Radial Field” on the details pane (and on the label).

You should end up with something like this:

From there, I suggest you play with this a bit. I will write part 2 soon to get to the next step.



Radar Chart in Tableau without R

Hey guys,

I’ve been trying to find out how to do a radar chart in Tableau without using R. I found this this post on The Information Lab which gives a good idea, but is outdated and is not really what I was looking for.

I did some research, and after learning about the new Level Of Details feature in 9.0, I realized I could do it quite easily!


The big idea is you need to first find the max radius fixed on the point – meaning it will find the max value per point and use that for that axis. Then, you just device the value by that, and you get the relative position. Finally, you do some trigonometry, et voilà!

If there is any interest, I’ll get into the details on how to do this soon.

Me vs World Growth

Hi there!

I have been setting up a Tableau Server sandbox on my personal systems, and I wanted to put it to the test. It’s a pretty silly / simple dashboard, but it can be quite interesting and tells a little story about myself.

Obviously, there’s nothing too awesome about the chart itself, but it shows the integration and the simplicity of it. In this case, there’s obviously not much security around it, but for the purpose, I guess it’s all right.

Tableau? What’s that?

Tableau? What’s that?

If you stumbled here by mistake (or out of curiosity or both), then you might be wondering what IS Tableau.

Tableau is a product made by Tableau Software which allows people to visualize their data. This means putting the data on charts; all kind of charts; and to interact with the data. It’s fast, fun to use, can analyze any kind of data (you can even build your own connectors!), interact with the data, share your insights instantly, and even connect to real time data.

If you wish to learn more about the product itself, I recommend you to start there.

There are a few pieces to the whole thing:

  • Tableau Desktop is the tool to build workbooks (content)
  • Tableau Server is the server to host these workbooks
  • Tableau Public is a desktop/server combo that is public, you can host your workbook there (think cloud), but it’s also visible by everyone, so no confidential data there.
  • There’s a mobile version (ipad and Android) which allows you to view /edit server content.
  • There’s also another version in development, called “Elastik”, which is a tool meant to allow users to analyze anything quickly on mobile devices.


I am involved and interested in Tableau products because I integrate an OEM verison of these tools with the flagship product of the company where I work.