Delphi pdf reading components. Adding a PDF viewer to the program using ActiveX

Component capabilities declared by the developers:

  • Support for vector and raster graphics in PDF documents
  • Protect PDF documents with a password
  • AcroForms/PDF forms support
  • Watermarks
  • Generate PDF documents with internal and external links, web links and bookmarks
  • Full Unicode support
  • Support for various fonts, text formatting, multi-column text layout
  • TCanvas support
  • Built-in archiver for compressing text and graphic information in a document
  • Convert TIFF to PDF

As an example, I decided to build a demo application that comes bundled with the component THotPDF. The application is quite simple - it generates a PDF with two links and simple text. The code is also simple and clear:

procedure TForm1. HelloWorldButtonClick(Sender: TObject); beginHPDF. BeginDoc ; HPDF. CurrentPage. PrintHyperlink(20, 35, "Website: " + MainEdit. Text, MainEdit. Text); HPDF. CurrentPage. SetFont("Times New Roman", , 16); HPDF. CurrentPage. SetRGBHyperlinkColor(clRed); HPDF. CurrentPage. PrintHyperlink (20, 50, "Order page: " + BuyEdit. Text, BuyEdit. Text); HPDF. CurrentPage. SetFont("Times New Roman", , 14); HPDF. CurrentPage. TextOut(20, 75, 0, "Click the link to navigate"); HPDF. EndDoc ; end ;

Even without going into the intricacies of the component's operation, you can understand what and how is happening in this demo example. Let’s run it and look at the appearance of the generated document:

These two blue rectangles in the document are links to two pages... and the simple text “Click the link to navigate” is not visible anywhere at all. Although, we must pay tribute, when you click on the rectangle, the link actually opens. In general, either the developers were in a hurry to announce support for Delphi up to XE3, or the trial version is so interesting, but after running this demo I somehow lost the desire to use THotPDF even for free. Let's move on.

4. PDF Creator Pilot

Price: from $450 without source codes to $9950 (!!!) with source codes
5-XE4
http://www.colorpilot.com/pdflibrary.html#download

The library costs almost the same as Delphi XE3 Professional... Well, okay, let's see what this library is.

Features stated by the developers:

  • An expanded set of methods and properties for easy PDF creation;
  • Read and merge existing PDF documents;
  • Adding and removing pages of a PDF document;
  • Unicode support;
  • Creating watermarks for each page;
  • Adding thumbnails to a PDF document;
  • Using and embedding fonts (TrueType, OpenType, Type1, etc.);
  • Create interactive PDF documents using JavaScript and hyperlinks;
  • Support for interactive AcroForm elements: text input fields, buttons, radio buttons, drop-down lists, checkboxes;
  • encryption and password protection of created PDF documents;
  • Creation and management of document content;
  • Access HDC for drawing on PDF pages using WinAPI functions.
  • Use of images in various formats (JPEG, TIFF, PNG, BMP, GIF);
  • Creating and using annotations;
  • Create PDF documents on disk or in memory;
  • Extract text from PDF documents;

All in all, a pretty impressive list of features. Let's see how some of these features work in practice. Download the demo version and install it.

After installation, launch Delphi (in my case it’s Delphi XE3) and go to the menu:

Component - Import Component - Import Type Library

We look for a library in the list

We import, create a new project and include the module in the uses PDFCreatorPilotLib_TLB.

Now we can test the library’s operation using some live example. First, let's try to generate a document with simple text:

procedure TForm1. Button1Click(Sender: TObject); var fnt: integer; begin ( initialization ) PDF := TPDFDocument4. Create(nil); PDF. SetLicenseData ("demo" , "demo" ) ; fnt:=pdf. AddFont ("Verdana" , false , false , false , false , fcANSI) ; PDF. UseFont(fnt, 14); PDF. ShowTextAt(20, 40, "HELLO, PDF!"); (save) PDF. SaveToFile("HelloPDF.pdf", true); PDF. Destroy ; end ;

Launch the application and look at the created PDF document:

Now let's try to write Russian text to a file:

procedure TForm1. Button1Click(Sender: TObject); begin ( initialization ) (...) PDF. ShowTextAt(20, 40, "Hello, PDF!"); ( save ) (...) end ;


Maybe somewhere in the class properties you need to configure something, call some method that will enable Unicode support, BUT for that kind of money I would like to get a library that will work right out of the box without any hassles with settings and tweaks... By the way, the method for inserting a link (AddHyperLink) also did not work - the document remained pristine despite the fact that the link was supposedly inserted. But, we must pay tribute, an attempt to insert a previously created PDF into a new document worked with a bang - the document was inserted onto the new page as it should, without mistakes.

5. PDFtoolkit VCL v4.0.1.293

Price: from $349 without sources to $499 with sources
Supported Delphi versions: 6-XE4
Trial version download page: http://www.gnostice.com/PDFtoolkit_VCL.asp?show=downloads

There are quite a lot of positive reviews on the Internet about this component library from Gnostice.

According to possibilities PDFtoolkit almost 1 to 1 corresponds PDF Creator Pilot, those. can “glue” PDFs, recognize text, insert links, search documents, etc. But, unlike PDF Creator Pilot, PDFtoolkit if gtPDFDocument1. IsLoaded then begin gtPDFDocument1. TextOut( "Hello, World!" , IntToStr (gtPDFDocument1. PageCount ) , //range of pages into which the text will be inserted gtPDFDocument1. GetPageSize(gtPDFDocument1. PageCount, muPixels) . Width/2 //insert text in the center of the page gtPDFDocument1. GetPagesize(gtPDFDocument1. PageCount, muPixels) . Height/2); (save the document) gtPDFDocument1. SaveToFile("modified_doc.pdf"); end ; finally gtPDFDocument1. Free end ;

Of course, in relation to my task, PDFtoolkit turns out to be practically useless, because... I'm interested in creating PDFs from scratch, but for those who write their own PDF viewer, I think this library should be more than suitable.

So, what do we have in the end? There are 5 different solutions for creating and working with PDF documents in Delphi. Each solution has both its advantages (free, sophisticated) and disadvantages (exorbitant cost, problems with Unicode, etc.). In relation to my problem, all solutions will have to be “worked with a file.” On the other hand, there are a bunch of paid and free services on the Internet for generating PDFs, but, remembering that such services tend to suddenly take up and die, I somehow don’t feel inclined to get involved with them. There is, of course, one more solution - a homemade one and not entirely in the Delphi theme, but more on that some other time, but for now I’ll go think about what to do with the client for DelphiFeeds


See you online!


publication date 09/23/2005 07:00 Essay on creating PDF files

Recently, a lot of PDF converters, readers and writers have been discovered on the Internet. And the vast majority of them are offered for money. The program itself ranges from $10 to $300. And the source code for much more money, the price starts from $200 and in one place (after getting interested in this I surfed the Internet) for as much as 900 euros.

This problem interested me in terms of programming and I’m bringing the results to your attention. (These results were obtained by me while studying the insides of a PDF file when you open it in total commander via F3)

A typical PDF file consists of four parts :=

What is it

? This is a common reference to the PDF specification version. Which is present in the first line of the PDF file. For example, "%PDF-1.3" In the seventh version of Acrobat, which was released somewhere in the early summer of this year, this number is "%PDF-1.7", but this is not a product version, this is a specification version. The second line of the PDF is a small abbreviation (apparently intended for future use) "%vgPU"

Everyone has figured out the first part of the PDF.

What is the second part called ?

The answer is very simple: this is a sequence of objects, the description of which, as well as the header, are presented in text form.

Each object is a text fragment with a serial number in the name, for example "4 0 obj"

  • 4 this is the object's serial number
  • 0 this is the number of (re)generation of the file, that is, when the file is updated (edited), this number increases
  • obj this is a code word meaning that we encountered an object in the body of the document

All objects are divided into indirect and direct. All are indirect, and most of them are after the word obj have a delimeter in their body"<<", означающее начало данных объекта. И в конце данных закрывающий делиметер ">>" and code word endobj

Direct objects should not have opening and closing delimiters in their body"<<", ">>>"All indirect objects are accessible through cross-reference table. It represents references in the form of an offset from the beginning of the file to the beginning of the object (Data (lines) in the object are separated #13#10 or #13 )

The type of the “most important” object in the body of a PDF file has a proud name "/Catalog"

4 0 obj<< /Type /Catalog /Pages 2 0 R /OpenAction [ 5 0 R /XYZ null 364 1 ] /PageMode /UseNone >>endobj

In fact, there should be 3 “main” objects in the body of a minimal “Hello world” PDF file. Let me list them by type:

  • "/Catalog" contains a link: to the page tree ( /Pages)
  • "/Pages" contains a link to a group of document pages (For example 2 0 obj > endobj)
  • "/Page" contains a link to objects related to a specific page. (For example 3 0 obj > /Rotate 0 >>)
And a few minor ones

Let's analyze the page object:

  • /Rotate a field indicating how many degrees the page image should be rotated when displayed in the program
  • /MediaBox And /CropBox fields describing page size
  • /Parent reference to parent object "/Pages"
  • /Resources this field describes which font should be used when displaying the page (a font is a separate object) and the ProcSet setting this setting indicates what content is in the data stream of this page (can also be defined as an object, not as a field)
  • /Contents The most interesting field in the “page” object gives a link to the content object of this page, and: if this field is absent in the “page” object, then the page is empty
Page content:
Object "stream" 4 0 obj > stream BT /F12 9 Tf 10 782 TD 0 -12.5 TD (Max Fokin) Tj 0 -12.5 TD (mnb) Tj 0 -12.5 TD () Tj 0 -12.5 TD (Max Privet) Tj 0 -12.5 TD (1) Tj 0 -12.5 TD (1) Tj 0 -12.5 TD (2) Tj 0 -12.5 TD (3) Tj 0 -12.5 TD (45) Tj ET endstream endobj /Length 305 this field shows how many bytes are in a word stream to the word endstream

The simplest option is an unencoded and uncompressed data stream in the object stream. It is limited to operators BT And ET

BT Begins a Text Object - characterizes the beginning of the text ET Ends a Text Object. - characterizes the end of the text /F12 9 Tf

  • /F12 this is the code name of the object that characterizes the font used on this page
  • 9 this is the font size
  • Tf this is an operator that characterizes that this line in the steam object is the setting of the font and size
10,782 TD are the numbers where this line begins (counting is done from the upper left corner) Tj is the new line operator Well, in parentheses is our text

I don't explain the coded stream here. It is based on the RC4, RC5, MD5 algorithms.

What is an object Font 12 0 obj >

  • /Type /Font Naturally the type name
  • /Subtype /Type1 subtype name
  • /Name /F7 F7 this is the code name

PDF supports several types of fonts. They are listed below

  • Type 1, including subsets and Multiple Master "snapshots"
  • Type 3
  • TrueType, including subsets
  • Type 0
To be honest, I didn’t understand Type 3, TrueType, including subsets, Type 0, I can’t say anything about them
And Type 1 is the following fonts Courier Courier-Bold Courier-BoldOblique Courier-Oblique Helvetica Helvetica-Bold Helvetica-BoldOblique Helvetica-Oblique Times-Roman Times-Bold Times-Italic Times-BoldItalic Symbol ZapfDingbats

20 0 obj > endobj This is an object with code names for fonts of the first type. Using this code name you can easily get the object itself font. 6 0 obj >

ALL: that is, the minimum Consists of the following objects: "catalog" , "pages", "page", "Resources"(can optionally be present as a field in the page object), untyped object "stream", group of objects "font"

What's happened ? In fact, this is an ordinary text table, it begins with the word xref and its body has links to all indirect objects in the document. Here is an example xref 0 27 0000000021 65535 f 0000000016 00000 n 0000000105 00000 n 0000000169 00000 n 0000000356 00000 n 0000000713 00000 n 00892 00000 n 0000001006 00000 n 0000001125 00000 n 0000001247 00000 n 0000001373 00000 n 0000001486 00000 n 0000001604 00000 n 0001725 00000 n 0000001850 00000 n 0000001967 00000 n 0000002084 00000 n 0000002203 00000 n 0000002326 00000 n 0000002439 00000 n 0000002558 00000 n 0000000024 000 01 f 0000002751 00000 n 0000002831 00000 n 0000000000 00001 f 0000002915 00000 n 0000002955 00000 n 0 27 These numbers mean the following:

  • 0 - first object number in the table
  • 27 - number of table elements

The first table element always has the form " XXXXXXXXXX 65535 f" where X is a digit and 65535 is the default value for the first element in the table. The symbol "f" stands for " free", that is, the object is not used

Let's analyze the element of this table.

  • The first 10 digits are the offset from the beginning of the file to the beginning of the object.
  • 0000000016 means that 16 bytes from the beginning of the file you will encounter the first mention of the object, that is, for example, 4 0 obj

The second five digits are the file generation number. If the file has just been created, then they are always zero. If the file is modified, then this number increases by one. That is, 0000000024 00001 f

The canonical PDF file we just created has only one table. But, if the file is edited, then there can be a lot of such tables.

The relationship of tables is carried out using the last element and code word startxref

The canonical, newly created PDF file has only one table, after the table there is an element trailer And after the trailer comes the code word startxref, indicating the offset from the start of the file to the start of the table, here's an example. trailer > startxref 173 %%EOF This means that after 173 bytes from the beginning of the document, the code word will be present xref. But, if the file has been edited, then the last trailer in the file will look like: xref 0 3 0000000000 65535 f 0000003609 00000 n 0000003832 00000 n trailer<7a15ab3ed3999575ff2f3034104a82c1>] >> startxref 173 %%EOF But, if we refer to the table where the link points startxref 173, then we will find the following table, and behind it a trailer that will have a field /Prev 3896 3 16 0000000016 00000 n 0000000664 00000 n 0000000936 00000 n 0000001106 00000 n 0000001133 00000 n 0000001250 00000 n 000000139 5 00000 n 0000001811 00000 n 0000001992 00000 n 0000002180 00000 n 0000002360 00000 n 0000002760 00000 n 0000003438 00000 n 516 00000 n 0000000776 00000 n 0000000916 00000 n trailer<7a15ab3ed3999575ff2f3034104a82c1>] >> startxref 567 %%EOF

This field /Prev 3896 points us to the previous table, and the link startxref 567 points to the next table and so on almost ad infinitum, until in the next field startxref we won't see 0 . This means we have read all the tables.

This essay, of course, lacks source code. Here it is: two main modules are presented" PDFDocument"and auxiliary" PDFBaseFonts"

The following files are attached to the material:

  • Source code of modules PDFDocument.pas and PDFBaseFonts.pas (16 K) update from 9/23/2005 7:02:00 AM

Discussion of the material [ 07/31/2006 06:33 ] 7 messages

In this article, we'll look at how to create your own pdf-documents, and in the next article we’ll look at how you can view pdf-documents in your projects, as well as print content, navigate through the document, and so on.

For this we need the components from the tab Rave. Let's install the components we need to work, and these are:

  • TRvNDRWriter
  • TRvRenderPdf
  • TButton

Component TRvNDRWriter intended for recording in pdf-file information (text, graphic) via stream.

Component TRvRenderPdf designed for creating and drawing information (text, graphic, etc.). Well, when you press a button, we will enter something into our pdf-file.

We won’t write much about theory, but let’s move straight to programming, but we must first create a regular empty pdf-file and put it in the root of the program. Next to the event OnClick For our button we will write the following code:

procedure TForm1. Button1Click(Sender: TObject); var Streams: TMemoryStream; begin Streams: = TMemoryStream. create ; RvNDrWriter1. Stream := Streams; RvNDRWriter1. Execute ; RvRenderPdf1. PrintRender(Streams, "test.pdf"); ShellExecute(Handle, nil, "test.pdf" , "" , "" , SW_SHOW) ; FreeAndNil(Streams); end ;

We create a stream in which we will draw all the information, then we start rendering, specify the file into which the data will be drawn, and at the end we destroy the stream.

That's it now, we need an event that occurs when drawing data, in which we will display all our information. Selecting the component TRvNDRWriter and go through the list of events of this component, after which we find the event OnPrint and in this event we write the following code:

procedure TForm1. Print(Sender: TObject); begin bmp: = TBitmap. Create ; bmp. LoadFromFile("test.bmp"); with RvNDRWriter1 do begin SetFont("Arial" , 16) ; FontColor: = clGreen; Print("Test Text" ) ; PrintHeader("Begin PDF File" , pjCenter) ; PrintFooter("End PDF File" , pjCenter) ; LineTo(10, 10) ; PrintBitmap(1, 1, 1, 1, bmp) ; end ; FreeAndNil(bmp); end ;

Well, first, we upload the image. *.bmp, naturally variable bmp, we have a class object TBitmap. We download it in order to then draw it in our pdf-document.

And then, I think everything is clear, the procedure PrintHeader- displays the inscription in the title of the document (page), procedure PrintFooter- displays the inscription at the end of the document (page). Procedure PrintBitmap- outputs an image to a document, procedure SetFont- sets the document font, procedure FontColor- sets the document font color.

There are two main ways to organize viewing of PDF documents in the program.

  • Using ActiveX technology;
  • Use of special components.

Components for working with PDF are not available in all Delphi releases and most often they need to be searched for and installed additionally. At the same time, to use ActiveX you only need to have the appropriate software on the user's computer. In this case, Adobe Acrobat Reader (free) or Adobe Acrobat.

Working with ActiveX can be divided into two stages.

  • Importing a type library or components;
  • Actually, the use of imported tools in the application.
Importing componentsActiveX

To import components, use the “Component” – “Import Component” command in the Delphi main menu. The import process itself is implemented as a wizard.

On the first page of the wizard, select “Import ActiveX Control”.

After this, you need to decide what to do with the imported components. Since we plan to use them as Delphi components, select “Install to New Package”.

Now all that remains is to specify the name of the newly created package.

After clicking on the "Finish" button, the PDF viewer ActiveX components will be imported into Delphi.

ViewPDF in attachment

To create a PDF viewer in an application using ActiveX, you need the TAcroPDF component. The process of importing it is described in detail above.

As an example, let's create the following application. Let's place the TAcroPDF, TOpenDialog and TButton components on the form as shown in the screenshot below.

When you click the button, we call up a file open dialog to select a PDF file to view.

For this example, we will need the following components, which are located in the Rave tab. And so, let's start installing the components we need, namely:

  • TRvNDRWriter
  • TRvRenderPdf
  • TButton

I propose to dwell a little on these components and understand what they serve. By the way, I got information about them from a couple of books that I purchased progbook.ru. Therefore, if you want to buy decent books on Delphi, then you are welcome to this store.

So, first we have the TRvNDRWriter component. We need this component to write information to a pdf file, no matter what, text or graphic, through a stream.

Procedure TForm1.Button1Click(Sender: TObject); var Streams:TMemoryStream; begin Streams:=TMemoryStream.create; RvNDrWriter1.Stream:=Streams; RvNDRWriter1.Execute; RvRenderPdf1.PrintRender(Streams, "test.pdf"); ShellExecute(Handle, nil, "test.pdf", "", "", SW_SHOW); FreeAndNil(Streams); end;

The next component is TRvRenderPdf. We need it to create, or rather to draw, information, be it text or graphic. Accordingly, after clicking the button, we will write the information we need into our pdf file.

But let’s not rant too much, but let’s move straight to the topic that interests us, namely programming :)

First, we need to create a regular empty pdf file and place it in the root folder of our program.

Actually, with this code we create a stream where we will draw the information we need, after which we start rendering, indicate our final pdf file into which the data will be drawn, and at the end we close the stream.

After executing this when, we need an event that occurs when drawing data; in this event we will display the information we need.

Let's look at the code. To begin with, I load a *.bmp image, create a bmp variable, and an object of the TBitmap class. I download it so that I can then draw in our pdf file.

I think it’s not worth describing everything further clearly, and so, the PrintHeader procedure adds an inscription to the document header, the PrintFooter procedure adds an inscription to the end of the document. The PrintBitmap procedure adds an image to the document, the SetFont procedure sets the required document font, the FontColor procedure sets the desired document font color.

All subsequent graphic functions and procedures are exactly the same as when displaying graphic information on Canvas of other components.

I use the NewPage function to create a new sheet in a document. Everything that will be added after it to display information will be displayed on a new sheet of the document.

Important so that all modules: RpRender, RpRenderPDF, RpDefine, RpBase, RpFiler, RpRave, RpCon are connected to the project!