Академический Документы
Профессиональный Документы
Культура Документы
A lightweight C# library to add images and 'round rectangles' to a PDF on the fly and then securely embed the PDF in a web page
Introduction
I needed a method to create PDF documents on the fly,
specifically invoices and similar financial documents.
After searching the web for a long time, I finally came across an
article on CodeProject (PDF Library for creating PDF with tables
and text, in C#). This excellent article by Zainu introduced me to
the concepts behind creating a PDF.
Background
In order to fully understand the code behind this library, you should read the article by Zainu as I will not cover the same topics
again here.
Some of the original code has been modified, these changes are all commented in the code itself.
https://www.codeproject.com/Articles/18623/Add-Images-and-Textboxes-to-PDF?display=Print 1/10
27/03/2019 Add Images and Textboxes to PDF - CodeProject
This is an example of the PDF markup that will be generated if you download and run the attached code.
Don't forget that even though you can read the markup in a text editor, that a PDF is in fact a binary file and (excluding the simplest
case) must be treated as such.
You can find a much more detailed explanation of the PDF by downloading the Adobe PDF Manual. It is only 1300 pages or so....
%PDF-1.5 This is a PDF version 1.5 - the double question mark is simply so that FTP and similar packages
%?? know that this is a binary file when transferring.
8 0 obj The 'X 0 obj' means that this is an object in the PDF - the X is its unique number.
<<
/Type /Page/Parent 2 0 R This object (8 0) describes one page, setting the page size and then defining the resources that
/Rotate 0 will be needed, in this case the fonts called T1, T2, T3 and T4 which are described in the objects 3
/MediaBox [0 0 595 0, 4 0, 5 0 and 6 0 respectively.
842]/CropBox [0 0 595 842]
/Resources<</ProcSet[/PDF/T The XObject refers in this case to an image called I1, the data that describes it can be found in
ext] the object 10 0.
/Font<</T1 3 0 R/T2 4 0 R/T3
5 0 R/T4 6 0 R>> Finally, the 'Contents' (essentially the markup which tells the PDF what to display) can be found
/XObject <</I1 10 0 R >>>> in the object numbered 9 0.
/Contents 9 0 R
>> The 'Parent' reference is to object 2 0 which records the number of pages in the entire document
endobj (in this case only 1) and shows which object describes the contents of each page.
9 0 obj<</Length 989
>>stream
q
144 0 0 100 300 700 cm This object contains markup to describe the actual page and means such things as
1 0 0 1 0 0 cm
/I1 Do place the text "XYZ" at location x,y in font Z
Q or
BT/T3 12 Tf draw an image
105 699 Td located at x,y of size
(Round Rectangle Header) Tj w,h etc In the code itself, is SetStream.
ET
endstream
endobj
1 0 obj<</Type
/Catalog/Lang(EN-US)/Pages
Root of the file, says that the index (or pagetree) to the document can be found in object 2 0
2 0 R>>
endobj
2 0 obj<</Count 1/Kids [ 8 0
The document index - this document has one page (Kids) and the information can be found in
R ]>>
object 8 0
endobj
https://www.codeproject.com/Articles/18623/Add-Images-and-Textboxes-to-PDF?display=Print 2/10
27/03/2019 Add Images and Textboxes to PDF - CodeProject
3 0 obj<</Type/Font/Name
/T1/BaseFont/Times-Roman
/Subtype/Type1/Encoding
/WinAnsiEncoding>>
endobj
4 0 obj<</Type/Font/Name
/T2/BaseFont/Times-Italic
/Subtype/Type1/Encoding
/WinAnsiEncoding>>
endobj
Describes the fonts used in the document.
5 0 obj<</Type/Font/Name
/T3/BaseFont/Times-Bold
/Subtype/Type1/Encoding
/WinAnsiEncoding>>
endobj
6 0 obj<</Type/Font/Name
/T4/BaseFont/Courier
/Subtype/Type1/Encoding
/WinAnsiEncoding>>
endobj
10 0 obj
<</Name /I1
/Type /XObject
/Subtype /Image
/Width 144
/Height 100
/Length 29779
/Filter /DCTDecode Describes an image.
/ColorSpace /DeviceRGB
/BitsPerComponent 8 Note that the actual byte data is missing - you will find out how to add that later.
>> stream
endstream
endobj
70
obj<</ModDate(D:200705010
24237+10'00')
/CreationDate(D:20070501024
237+10'00')
/Title(Title)/Creator(Your App
Properties of the document e.g. who created it and when etc
Name)
/Author(System Generated
/Producer(www.My New
App.com.au)/Company(My
Company Name)>>
endobj
https://www.codeproject.com/Articles/18623/Add-Images-and-Textboxes-to-PDF?display=Print 3/10
27/03/2019 Add Images and Textboxes to PDF - CodeProject
xref
0 11
0000000000 65535 f
0000001275 00000 n
0000001332 00000 n
0000001374 00000 n
0000001473 00000 n The byte offsets of each object in the document. This is explained in the original article by Zainu.
0000001573 00000 n
0000001671 00000 n
0000031745 00000 n
0000000014 00000 n
0000000234 00000 n
0000001766 00000 n
trailer
<</Size 11
/Root 1 0 R
/Info 7 0 R
'Root' refers to the starting point known as the pagetree (object 1 0 in this case) of the
/ID[<5181383ede94727bcb32
document.
ac27ded71c68>
<5181383ede94727bcb32ac2
7ded71c68>]
>>
startxref
31959 End of the file.
%%EOF
PDFLibrary.cs
Default.aspx
Default.aspx.cs
streampdf.aspx
streampdf.aspx.cs
myimage.jpg
The file PDFLibrary.cs should be placed in the App_Code folder, the rest in the root of the application.
Point your browser at the default.aspx file and you should get a button displayed. Clicking this button should create the PDF and
display it within the web page.
https://www.codeproject.com/Articles/18623/Add-Images-and-Textboxes-to-PDF?display=Print 4/10
27/03/2019 Add Images and Textboxes to PDF - CodeProject
These are based on 'Cubic Bezier Curves'. If you have ever used PhotoShop or similar graphical software, you may have used this
method without even knowing it.
Essentially, all we do to create the 'round rectangle' is to use eight paths to form an area. Four of these paths are the radii based on
the Bezier Curve plus four straight lines which connect them. This is then stroked to form the border, and the bounded area is then
coloured in to form the background. A simple rectangle is then drawn on top of the bezier area to form the text box.
If you are interested in the full details, have a look in the Adobe PDF Manual which describes the mathematics behind it. For the rest
of us, all we need to know is that it works!
Let's have a look at the actual code now. First we create a new object to represent the rectangle in code
Finally, as this is only markup as far as the PDF is concerned, we add the markup to the PDF content stream.
LLX
LLY
rrWidth
rrHeight
CornerRadius
Circularity
HeaderHeight
TextBoxHeight
Border
BorderColor
MainBG
TextBoxBG
https://www.codeproject.com/Articles/18623/Add-Images-and-Textboxes-to-PDF?display=Print 5/10
27/03/2019 Add Images and Textboxes to PDF - CodeProject
LLX and LLY are the horizontal and vertical coordinates of the lower left of the box, rrWidth and rrHeight are the width and height of
the box (remember all coordinates are in 1/72" rather than pixels).
The CornerRadius parameter is as shown in Figure 2. The HeaderHeight parameter is the vertical height of the area at the top where
you can later place text. It cannot be less than the radius otherwise the text area rectangle placed over the top will overlap.
TextBoxHeight is the height of the text box and will be centred vertically. The last three colour parameters are the three
ColorSpec values we created earlier.
Finally the Circularity parameter. This is used to change the actual shape of the corners of the box.
As I wanted to make each corner mirror reflections of each other, I decided to calculate the (x2,y2) and (x3,y3) values shown in
Figure 1 (which are the values in PDF markup to describe the curve) based on the radius of the corner and a constant which I called
Circularity. The value for (x1,y1) is the current graphics cursor position in the PDF and the (x4,y4) value is the end point of the curve
and also the new graphics cursor position.
At a value of 0, you get a straight line (in effect, an octagonal shape). If you increase the value to 0.55, you get a perfect radius. As
the value increases towards 1, the corner gets tighter / smaller. Once the value starts to go above 1, some other interesting corner
shapes start to form.
So that's all there is to it. This code assumes that the final document is one page and has a fixed number of lines of text in a text
box, however, it would not be too hard to combine the textAndtable.AddRow method with the DrawRoundRectangle
method to dynamically create the vertical dimensions of the textbox and wrap it across multiple pages if you needed to.
There are three parts to adding an image to a PDF. These are shown in the table describing a simple PDF markup at the start of this
article.
https://www.codeproject.com/Articles/18623/Add-Images-and-Textboxes-to-PDF?display=Print 6/10
27/03/2019 Add Images and Textboxes to PDF - CodeProject
8 0 obj
<<
/Type /Page/Parent 2 0 R
Firstly we need to tell the PDF where to find the
/Rotate 0
data that describes the image.
/MediaBox [0 0 595 842]/CropBox [0 0 595
This is the CreateImageDict method. In this
842] /Resources<</ProcSet
case we are telling the PDF that that the data that
[/PDF/Text] /Font<</T1 3 0 R/T2 4 0
describes the image called 'I1' can be found in the
R/T3 5 0 R/T4 6 0 R>>
object numbered 10 0.
/XObject <</I1 10 0 R >>>>
/Contents 9 0 R >>
endobj
9 0 obj<</Length 989 The next three lines describe where to place the
>>stream<br />q<br/>144 0 0 100 300 700 cm<br />1 image relative to the page, its page width and
0 0 1 0 0 cm<br />/I1 Do<br />Q height.
BT/T3 12 Tf
105 699 Td If you look in the PDF manual, there is also a whole
(Round Rectangle Header) Tj host of transformations that you can apply to an
ET image, for example rotation, scaling, skewing and
endstream many other more advanced features. These would
endobj need to be added to the code if you wished to use
them.
Once we have the created the data, we need to write it to the physical PDF file
https://www.codeproject.com/Articles/18623/Add-Images-and-Textboxes-to-PDF?display=Print 7/10
27/03/2019 Add Images and Textboxes to PDF - CodeProject
Secondly we need to add this data to the PDF in the form of an object (9 0) in the table above. This is perhaps the most complex
part of the process. Luckily for me, Zainu had already done most of the hard work as far as creating a framework which keeps track
of object numbers and the other main parts of a PDF. I have simply added in some more methods specifically to handle images.
In order to create the object which contains the data for the image, we must remember that a PDF file is in fact binary by nature.
The markup is created as unicode (16 bit), whereas the actual data representing the image is only 8 bit in the case of my example.
This means that we have to handle the byte output slightly differently to create the object bytes.
In essence we do this in three parts. Part one is send the first part of the object (obj X 0 .... stream) converted to byte data
imageDictStart followed by the actual byte data of the image imagebytes followed by the last part of the object
(endstream endobj) imageDictEnd to the PDF stream.
CreateImageDict opens the jpg as a bitmap to get the pixel dimensions and then puts the byte data into an array. Finally it
adds the parameters such as the image name, pixel dimensions etc into the string imageDictStart ready for writing to the
page later.
The next thing we need to do is to add the reference to this object into the page index. This is the markup /XObject <</I1
10 0 R >> in the example above.
This is written in AddImageResource to the string imageRef which is later used by GetPageDict to create the PDF page
index.
Now all the hard work is done, we just need to let the PDF know that we would like to display the image on the page. This is done
using markup such as
q
144 0 0 100 300 700 cm
1 0 0 1 0 0 cm
/I1 Do
Q
This could be written direct to the content stream, however, I implemented it as a separate class in case I wanted to add in some
transformations to the image later.
The first parameter is the image name, the second pair are the (x,y) coordinates where the lower left of the image should be placed
and the last pair are the width and height of the image on the page.
https://www.codeproject.com/Articles/18623/Add-Images-and-Textboxes-to-PDF?display=Print 8/10
27/03/2019 Add Images and Textboxes to PDF - CodeProject
In many cases, we can simply do this by using a hyperlink to the file itself. This will be fine for many applications, but what if we
want to restrict the file to certain users of a system?
You could set up file permissions on a network, but this is not an option on a public website (for instance).
I decided to use a very simple technique using an <iframe>. This can easily be used to display an object inside a web page simply
by providing the file name and the details of the application that will open the file (in this case application/PDF).
However, there is another way to use this. Instead of specifying the actual pdf file, we can specify an .aspx file which will serve the
byte data of the file. In this case, we can now determine who the requestor is and determine if they have permission to view the file
before serving it.
If you look towards the bottom of default.aspx, you will see that the <iframe> calls streampdf.aspx for the file data
source.
Have a look at the structure of the html that streampdf.asp generates. It does not send any headers etc, only the application
type followed by the bytes of the file specified.
This version simply sends the bytes from the hardcoded file name, however, you could specify a reference to perhaps a primary key
in a database which contains the actual file name/path to serve. You could even store the binary data of the PDF in the database
itself if you wished. In this way you can control who sees the file.
Note that .NET 2.0 web apps have a special folder called App_Data. This is specifically designed for storage of such files as anyone
browsing to a file in this folder will be returned a message that The system cannot find the file specified.
Further development?
I have tried to use flate compression to reduce the size of the page dictionary, so far unsuccessfully. I gather that the MS
implementation of the flate compression algorithm is not the same as the Adobe version. If anyone manages to work out how to
use it, please post it!
The ability to use other image formats (gif, png, tif etc) would also be a bonus. If anyone manages to add that in successfully, please
post it here.
History
May 2007 - Article first published.
License
This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)
https://www.codeproject.com/Articles/18623/Add-Images-and-Textboxes-to-PDF?display=Print 9/10
27/03/2019 Add Images and Textboxes to PDF - CodeProject
pmpdesign
Web Developer
Australia
PMP Design is based in Newcastle, Australia and specialises in designing and implementing custom business management
systems and websites.
Owner Geoff is currently working on TimeSuite, a business management system for project based organisations.
Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile Article Copyright 2007 by pmpdesign
Web03 | 2.8.190306.1 | Last Updated 2 May 2007 Everything else Copyright © CodeProject, 1999-2019
https://www.codeproject.com/Articles/18623/Add-Images-and-Textboxes-to-PDF?display=Print 10/10