iTextSharp - Drawing shapes and Graphics

4.76 (37 votes)

The previous iTextSharp article looked at bringing images into a PDF file and working with them. Sometimes, however, you may want to draw shapes and lines within the PDF and not rely on images. iTextSharp includes a lot of functionality that covers simple drawing to quite complex shapes. This article gets you started. Previous articles are listed below:

Create PDFs in ASP.NET - getting started with iTextSharp
iTextSharp - Working with Fonts
iTextSharp - Adding Text with Chunks, Phrases and Paragraphs
Lists with iTextSharp
iTextSharp - Links and Bookmarks
iTextSharp - Introducing Tables
iTextSharp - Working with Images

Up till now, all content added to the PDF documents in previous articles have relied on Simple iText, which takes care of positioning content within the flow of a document. It also looks after creating new pages to accept overflow text etc. Working with graphics needs a slightly different approach, in that we now need to use a PdfContentByte() object explicitly. This is obtained from the PdfWriter object's DirectContent. This also means that instead of just invoking the GetInstance method of the PdfWriter, we actually need to instantiate a PdfWriter object.


string pdfpath = Server.MapPath("PDFs");

  Document doc = new Document();



    PdfWriter writer = PdfWriter.GetInstance(doc, new FileStream(pdfpath + "/Graphics.pdf", FileMode.Create));


    PdfContentByte cb = writer.DirectContent;


Now that we have a working PdfContentByte object, we can use it to start drawing:


cb.MoveTo(doc.PageSize.Width / 2, doc.PageSize.Height / 2);

cb.LineTo(doc.PageSize.Width / 2, doc.PageSize.Height);


cb.MoveTo(0, doc.PageSize.Height/2);

cb.LineTo(doc.PageSize.Width, doc.PageSize.Height / 2);



The first line moves to the x, y coordinates specified in the parameters passed in, which in this case is halfway across the document, and halfway up (or the center point). The next line draws a line from this point to the position specified in the LineTo() method, which is still halfway across the document, but at the top of it. Actually, it doesn't "draw" it, but really only describes our intention. The line is only actually committed to the document when Stroke() is called. The second line is drawn from the lefthand edge, at a position halfway up the document to the righthand edge at the same height - so we end up with the top two quarters of the document outlined.

Using the same methodology, we can add a square to the top left quarter:


cb.MoveTo(100f, 700f);

cb.LineTo(200f, 700f);

cb.LineTo(200f, 600f);

cb.LineTo(100f, 600f);




We didn't need to explicitly specify the coordinates for the final side of the square. ClosePath() automatically provides a line from the current position that we are at to the original coordinates. However, there is a quicker way of delivering a square (or rectangle) using one of the convenience shapes provided by iTextSharp. The next bit shows that is action, placing a square in the top right quarter of the document:


cb.Rectangle(doc.PageSize.Width-200f, 600f, 100f, 100f);



Four more squares are added to the page, each one illustrating the results of different methods other than Stroke(), used to consign them to the document. But first, the Stroke colour and the Fill colour are set. If you have worked with any graphics packages, such as PhotoShop or FireWorks, you will probably know that Stroke is the outline of an object, and Fill represents its internals bounded by the Stroke. I have used CMYK as the colour space, setting the Stroke colour to Cyan and the Fill colour to Yellow:


cb.SetColorStroke(new CMYKColor(1f, 0f, 0f, 0f));

cb.SetColorFill(new CMYKColor(0f, 0f, 1f, 0f));


cb.MoveTo(70, 200);

cb.LineTo(170, 200);

cb.LineTo(170, 300);

cb.LineTo(70, 300);

//Path closed and stroked



cb.MoveTo(190, 200);

cb.LineTo(290, 200);

cb.LineTo(290, 300);

cb.LineTo(190, 300);

//Filled, but not stroked or closed



cb.MoveTo(310, 200);

cb.LineTo(410, 200);

cb.LineTo(410, 300);

cb.LineTo(310, 300);

//Filled, stroked, but path not closed



cb.MoveTo(430, 200);

cb.LineTo(530, 200);

cb.LineTo(530, 300);

cb.LineTo(430, 300);

//Path closed, stroked and filled



When using a Rectangle object to represent a square or rectangle instead of drawing it, the first two parameters represent the x and y coordinates of the bottom right hand corner. The final two parameters are the width and height. Other preset shapes include the Circle, but the x and y coordinates that are passed in represent the center point of the shape, followed by a value representing the radius. Looking at the first of the 4 squares above, it is relatively easy to determine that the width and height is 100 points, and that the center point is at 120 x, 250 y. To add a circle to the square so that it fits nicely, the following code will do:


cb.SetCMYKColorStroke(255, 255, 0, 0);

cb.SetCMYKColorFill(0, 255, 255, 0);

cb.Circle(120f, 250f, 50f);



Rather irritatingly, I found you have to guess at the actual values required by the two methods concerning the use of CMYK colour I have used so far. Neither was intuitive, given that generally, CMYK colours are represented as a series of percentages. For example, Warm Red is referenced as C: 0%, M: 100%, Y: 100%, K: 100%. The constructor for CMYKColor() requires four floats. Providing values that represent the percentages works fine. But I have provided values of 1f and 0f. In fact, I can provide any value I want, so long as it is a valid float. The values are treated as being relative to eachother, so for 100% blue (Cyan) i could just have validly provided 3000f, 0f, 0f, 0f. Strange, but workable if you stick to the usual CMYK percentages, and don't accidentally type in an extra zero.

The SetCMYKColorFill() method used just above accepts ints, rather than floats. I thought maybe this method was working on percentages as I would expect with CMYK, so I originally supplied the value 0, 100, 100, 0 to represent Warm Red. All I got was a washy pinky colour. I thought perhaps there was a bug in there somewhere, and that something was being affected by the previous setting of the Fill colour, so I deployed the ResetCMYKColorFill() method in the hope that it did something. Nope. All it did was reset to the default colour of black. Eventually, I found out CMYKColor is built on the ExtendedColor class, and can accept ints up to 255 - which is reminiscent of the RGB colour values and really confusing unless you know. I applied the values you see above, and got my desired result. To build on the oddity of this approach, I also discovered that there is nothing to prevent you entering values above 255. If you do, it seems that iTextSharp subtracts 255 from whatever you supply, so 256 is the equivalent of 1, and 510 is the equivalent of 255 - or at least, that's the results I got when I tried it.

Anyway, enough of the digression. Here's my red circle with a mauve border (2 points in width) in the first square:

Moving on, here's an example of using another preset shape, the ellipse. First, a rectangle is drawn, then an ellipse is added to fit neatly within it to illustrate how the positioning works through the parameters supplied to the constructor:


// x, y of bottom left corner, width, height

cb.Rectangle(100f, 200f, 300f, 100f);


//Bottom left and top right coordinates

cb.Ellipse(100f, 200f, 400f, 300f);



If the differences between the first and third parameter, and the second and fourth parameter are the same, you end up with a circle.

The next example shows another preset shape, the rounded rectangle. When one of these are drawn, the values passed in are the x and y coordinates for the bottom left hand corner, followed by the width and height, and finally the radius of the rounded corners. I have also added a circle in the corner with the same radius as the corner itself to show how the radius of the corner works. I've placed a cross at the center of the circle:


//Bottom left coordinates followed by width, height and radius of corners

cb.RoundRectangle(100f, 500f, 200f, 200f, 20f);



cb.Circle(120f, 520f, 20f);



cb.MoveTo(118f, 520f);

cb.LineTo(122f, 520f);


cb.MoveTo(120f, 518f);

cb.LineTo(120f, 522f);



Triangles are relatively simple to do, just by drawing the three lines they require. Here's an example of a right-angled triangle with the right angle shown:


cb.MoveTo(350f, 450f);

cb.LineTo(350f, 600f);

cb.LineTo(500f, 450f);



cb.MoveTo(350f, 460f);

cb.LineTo(360f, 460f);

cb.LineTo(360f, 450f);



Curves and Arcs

Bezier curves are important in Vector Graphics, where they are based on mathematical equations rather than each individual point being specified along the path of the curve. They work by specifying a start point, an end point, and two control points. The first control point is specified in reference to the start point, and the second one is related to the end point. The curve will begin at the start point, and head towards the end point in the direction of the 1st control point. How far it gets near the control point will depend on the distance between the start point and its control point, and the end point and its control point, and the distance between the start and end point themselves. The control points are used for directional purposes, and are rarely hit by the path. If you have ever used a Vector graphics package, like Photoshop or Fireworks, you will have seen these control points, usually with little "handles" on the end of them which you can move to bend the curve.

Here's an example of a curve, that starts at (200, 10), and ends at (350, 150).


//Start Point

cb.MoveTo(200f, 10f);

//Control Point 1, Control Point 2, End Point

cb.CurveTo(150f, 30f, 450f, 70f, 350f, 150f);



The curve heads towards the first control point at (150, 30), then bends away towards the second control point at (450, 70), before arriving at the end point. It looks like this:

I'm not really sure that shows a lot, so I will add in some "handles" that show the control points:


cb.MoveTo(200f, 10f);

cb.LineTo(150f, 30f);



cb.MoveTo(450f, 70f);

cb.LineTo(350f, 150f);



cb.Circle(450f, 70f, 1f);


cb.Circle(150f, 30f, 1f);



The first control point has a relatively short handle, so the curve only begins to move towards it. The second control point has a longer handle, and this starts to exert its influence on the curve along with the ultimate destination at a relatively early point in its journey, which is reflected in the way the line is "pulled" towards it. This effect is probably best illustrated by changing the second control point so that it is further away, and the angle is altered:



//start point (x,y)

cb.MoveTo(200f, 10f);

//control point 1 (x,y), control point 2 (x,y), end point (x,y)

cb.CurveTo(150f, 30f, 550f, 100f, 350f, 150f);



cb.MoveTo(550f, 100f);

cb.LineTo(350f, 150f);



cb.Circle(550f, 100f, 1f);





//Bottom Left(x,y), Top Right(x,y), Start Angle, Extent

cb.Arc(350f, 70f, 550f, 130f, 270f, 90f);

cb.SetLineDash(3f, 3f);




cb.MoveTo(550f, 100f);

cb.LineTo(535f, 95f);


cb.MoveTo(550f, 100f);

cb.LineTo(552f, 88f);



The original curve is shown in black, while the new one is shown in green. Now you can see that the increased length of the second "handle" has exerted its influence right from the start point, in that the green line starts just slightly beneath the original black one. Since the new second control point is further away from the end point, and is at a slightly more obtuse angle, the green line moves much further towards the second control point before finishing up at the end point.

There are a couple of other points to note about the code for the second curve. One is the Arc object, which has been used in this instance to create the curved arrow illustrating the movement of the original and new second control points. An Arc is part of an ellipse. In this case, the ellipse will fit into a rectangle that has a bottom left corner at (350, 70) and a top right corner at (550, 130). The default direction that an ellipse is drawn is counter-clockwise. The angle at which the arc is started is 270°, and only 90° of it are drawn. Secondly, the SetLineDash() method is shown for the first time in this series of articles, which allows me to draw the arrow as a dashed line.


You might also like...

Date Posted:
Last Updated:
Posted by:
Total Views to date: 210589


- Rohit Arora

I want to add a rectangle in a Table's cell, and I dont know the location of cell w.r.t pdf document.
How to do it.


- Tyler

There seems to be no PDFWriter property called "DirectContent" - please advise.

- Mike


I have no idea what you mean. I'm using Version, and it's definitely there:

public virtual PdfContentByte DirectContent

- Bill Palmer

I want to add a line graphic after a variable amount of text. In other words I want to write some text, then draw a line across the page, add more text, draw a line etc. Is there a way to determine where on the page (x,y coordinates) the cursor stops after writing some text?

- jim walker

To Bill Palmer
I wanted the same thing as you.
This worked for me.
(Your request is the last 7 lines, the additional info is to give you a frame of reference). Good Luck

Dim Chap1 as new iTextSharp.text.Chapter("Chapter 1", 1)

For each dr as datarow in MyDataSet.Tables("MyTable").rows

Dim Sec1 as iTextSharp.text.Section = Chap1.AddSection("Section 1", 1)

dim chnk1 as new iTextSharp.text.Chunk("data from db")

dim chnk2 as new iTextSharp.text.Chunk("data from db")

'The above is a very generic example of populating the Section - I will leave it
to your imagination to populate with Cells, etc...

'To end the section with a dotted line

Dim sepLINE as New iTextSharp.text.pdf.draw.DottedLineSeparator
sepLINE.LineWidth = 1
sepLine.Gap = 2
sepLINE.Percentage = 50
sepLINE.LineColor = New iTextSharp.text.Color(Color.DarkGreen)

Dim chnkLINE as New iTextSharp.text.Chunk(sepLINE)


'Separate each section with a line

- Frank & Walter

Lovely Stuff...very comprehensive

- imperialx

Hi Mike,

Can you help me?

My question is, how to add a vertical position marks? I have attached a sample image found here (


- Mike


For the example you have given, I would consider using tables and setting a border. Otherwise you have to keep track of exactly where you are in the document to calculate the y coordinate.

- Siva

How to set border style in pdfpcells ? i want to set a dotted border.

- jean

hey Mike,

I read your arcticle about drawing on PDF with ITextSharp, it was really interesting ! I was wondering if it's possible to draw on the original PDF, without having to create a new one, as your article propose.

Thank you,

Best regards


- Semil

I have created a rectangle using above methode. Now I want to add a text in the center of this rectangle. How Can I do this?

- Mike


You can center text in a cell like this:
PdfPCell cell = new PdfPCell(new Phrase("Hello World"));

cell.HorizontalAlignment = Element.ALIGN_CENTER;

- bernard

I really appreciate the comprehensive nature of your tutorials. Very appreciated and provided tremendous help.

Recent Comments

Cyrus 16/05/2017 19:55
In response to Razor Pages - Getting Started With The Preview
There is something wrong related to microsoft.dotnetcore.mvc.taghelpers! if you remove it from page...

Cyrus 16/05/2017 10:18
In response to Razor Pages - Getting Started With The Preview
well done mike, it was very useful, I really appreciate that....

Satyabrata Mohapatra 16/05/2017 07:21
In response to Razor Pages - Getting Started With The Preview
Finally!!!! web pages in core!!! Super excited !!!! Thank u sir for sharing.....Awaiting on...

Daniele 14/03/2017 10:24
In response to Working With Zip Files In ASP.NET MVC
is it possible give to the user a progress bar of the zipping process? Thanks in advance. ...

Suraj 13/03/2017 22:20
In response to Working With Zip Files In ASP.NET MVC
Very nice article. Thanks....

Satyabrata Mohapatra 19/02/2017 03:01
In response to Free SSL Certificates On IIS With LetsEncrypt
Thanks for sharing. Learned a lot !!...

Gfw 03/02/2017 09:48
In response to Free SSL Certificates On IIS With LetsEncrypt
I have used WinSimple for about the last 9 months - works great. One thing that you want to make of...

Ted Driver 02/02/2017 13:24
In response to Free SSL Certificates On IIS With LetsEncrypt
This looks great is you have command line access to your web server - what about those of us on Is...

Aghil 16/11/2016 18:16
In response to Server.MapPath Equivalent in ASP.NET Core
Hi, Thanks, it was really good. However, how can we access the in the Classes? Is there any...

Carl T. 06/11/2016 05:43
In response to Server.MapPath Equivalent in ASP.NET Core
Very succinct and easy to follow. Worked perfectly the first time for me. Thanks!!...