iTextSharp - Drawing shapes and Graphics

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.


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


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

Recent Comments

Gavan McGregor 09/10/2015 13:45
In response to Downloading multiple files in ASP.NET
Hi, (long time reader, first time poster!) Although it's a deliberately simple example, It's /...

Rodrigo 08/10/2015 20:40
In response to MVC 5 with EF 6 in Visual Basic - Creating an Entity Framework Data Model

Warren Caulton 08/10/2015 20:18
In response to Solved - The Microsoft.ACE.OLEDB.12.0 provider is not registered on the local machine
I have a Web Page that uploads and parses through an excel file to upload the latest sheets data to...

Vaclav Elias 07/10/2015 17:26
In response to ASP.NET 5: Dependency Injection and Services in MVC
Hi Mike, this is very nicely explained and elaborated from non dependency to dependency on real I am...

Nizen 07/10/2015 15:07
In response to Bind Data From a SqlDataSource to a Label
Wow thank you very much! I was really stuck and now I can be able to proceed coding as a result of...

Manolo 06/10/2015 21:15
In response to ASP.NET MVC Uploading and Downloading Files
Muchas gracias por esta información, Felicitaciones por su trabajo , me ayudó mucho , Saludos desde...

adam 05/10/2015 14:35
In response to Integrating Web API with ASP.NET Razor Web Pages
Can you re-open this web api project in webmatrix, once you've added web api? Basically I'm looking...

nish 24/09/2015 18:48
In response to Managing Checkboxes And Radios In ASP.NET Razor Web Pages
Very Interresting stuff! it really helped me to send an int value by checking a checkbox!...

Uğur Dinç 24/09/2015 16:45
In response to Scheduled Tasks In ASP.NET With Quartz.Net
Simplest and best explanation on Quartz.NET. Thank you!...

woo 24/09/2015 15:34
In response to Implementing Google's EU End User Consent Policy
Is there any way for the banner to appear only to EU visitors? I am referring to the jQuery code...