I am fortunate to have the unique experience and opportunity to be the first person at Datalogics, using the DLE layer, to get to write a full-blown C# .Net application for a customer, from absolute scratch (think: Visual Studio, File à New à Project…) so I thought I’d write to tell you all about it.
I like it! It’s great! And this is coming from someone who was tentative about the use of .Net, and someone who’s never written a .Net application from scratch, and has hardly used C#. (Though I’m well-versed in C and C++ and have done some VB/ASP .Net code maintenance work.) I can tell you, using DLE has been a very agreeable experience.
For the uninitiated, DLE is the .Net layer class library that sits on top of the PDF Library, providing an abstraction layer between a .Net application and the PDF Library. Through the magic of object-oriented wizardry, it offers a simpler use of the PDF Library, with less headaches, less “plumbing” code, less worry, and a lot less work.
The Customer’s Application
Let me give you a brief explanation of what this customer’s C# / .Net application does. It’s basically a PDF viewer with some nice extra bells and whistles. With it, you can open a PDF document and view it in the normal way as you would expect, with page navigation, zooming, printing and all the usual stuff. But what makes this application different is that it:
-
in addition to allowing the user to view a document in the traditional Single Page mode (one page at a time), it allows one to view it in Continuous mode, where all the pages appear “stitched together”, seamlessly, as though it’s one long scrollable document
-
allows the user to append any number of other PDF documents to the one they’re currently viewing
-
allows the user to print the document on a roller printer (plotter) as one long document of unlimited length and up to 3 feet wide
-
allows users to view bookmarks in a node-expandable/collapsible tree view and navigate to the pages to which they point
-
allows users to turn PDF layers on and off for both viewing and printing (independently)
-
allows the user to add annotations (I’m not there yet…but I’ll be sure to cover this in a future article)
As you can see, one central theme of the application is handling PDF documents that are very long. (For this particular customer, this is very important, as they have a series of PDF documents that visually describe something physically long, that need to be viewed and printed together, contiguously, as a whole. If I told you more, I’d have to kill you, as they say.)
In this article, I’ll just begin to extol the virtues of DLE with some of the basic parts of the application. In future articles, I’ll get into more how DLE expedites the development of the more exotic aspects of it. (By the way, I’m still writing the application as I write this article.)
The Benefits of DLE
So why is DLE so great, you might ask? Here are the main reasons that I found so far:
-
Many of the single DLE functions each perform the work of many PDF Library functions; these are all encapsulated and optimized in DLE’s class library
-
Because C# and .Net supports automatic “Garbage Collection” (releasing programmatically-allocated memory), the burden on the programmer to write tight code to avoid memory leaks is greatly mitigated
-
It commingles quite well with Visual Studio and the existing .Net framework and standard .Net class library
-
It’s intuitive to use
You probably want hear about examples of all of these. No problem!
DLE In Action
Here are the most basic things my application needs to do:
- Start the PDF Library
- Open a PDF document
- Show a page from the PDF document on a Visual Studio control (a standard PictureBox to be precise)
- Terminate the PDF Library on application termination
Let’s begin with starting the PDF Library. How does one start the PDF Library? Jeez, what if you don’t know anything about DLE?
One way to find out about things is to look at the Object Browser in Visual Studio. If you open any of the included samples (another great way to find out about things) you can see what’s inside DLE in the Object Browser in Visual Studio. Expanding the Datalogics.PDFL assembly node and then the Datalogics.PDFL namespace node, you’ll see the entire class library before your eyes.
Guess what? There’s a Library object. Inside any of the samples you’ll see how this is used:
using (Library lib = new Library())
{
// run the application, utilizing the PDF Library via the DLE class library…
}
This “using” statement does a lot for you under the covers:
-
It starts the PDF Library for you
-
It terminates the PDF Library automatically after the last statement in the code block is executed
-
Upon terminating the Library, it releases memory allocated by the Library object and any objects it may have created
This is a great example of:
-
The Garbage Collector (GC) at work; you don’t have to worry about manually deallocating memory you previously allocated. With C++, you would have to make sure you “delete” any dynamically-allocated memory later in your code to avoid memory leaks
-
How a lot of work is done for you behind the scenes, as initializing and terminating the PDF Library can be a somewhat cumbersome process in the “old school” (pre-DLE) approach
After creating a new project and adding some UI and menu elements, you just need to add the statement above to the code. Step (1) – and step (4): DONE!
Next: So how do you open a PDF document? Check this code snippet out; it’s basically what you need:
Document myDoc;
OpenFileDialog dialog = new OpenFileDialog();
String FileName = dialog.FileName;
myDoc = new Document(FileName);
In DLE, there’s an object called “Document”. In the above code, we declare the object, then use an OpenFileDialog object (a standard object included in .Net) to explore the file system which returns a FileName. Then we construct a new Document using that FileName, and lo and behold, we have a PDF Document from the file system, ready to play with. The constructor did all the work, creating an instance of the object, and associating the object with a document on disk. Cool, huh?
This is a great example of:
-
Encapsulated functionality. Using a single “new” statement, I get a constructed Document object – initialized with a PDF document from disk!
-
Intuitive use (could it be easier than myDoc = new Document(FileName)? What, in ZERO lines of code?)
-
Less memory allocation worries. As soon as myDoc goes out of scope or is set to null, the memory allocated for this object is marked to be freed by the GC. C#.Net doesn’t even have a “delete” keyword as in C++. Though you can do a myDoc.Dispose() call to free memory on the spot if you don’t want to wait for – or don’t trust! – the GC.
So, next we want to display the open PDF on the myPictureBox PictureBox object. There are a few steps to do this. Basically, in a nutshell, you need to:
-
Set a myPage instance of the Page object to the myDoc.GetPage() method, grabbing a single page from the PDF document
- Create a myBitmap Bitmap object sized to the height and width of myPage’s content (i.e., its MediaBox gotten via myPage.MediaBox)
-
Set myPictureBox.Image property to the myBitmap object
-
Create a myGraphics Graphics object (a standard .Net object) and use myGraphics.FromImage(myBitmap) to create it using myBitmap
-
Create a myMatrix Matrix object for scaling, rotation, and translation, and a myDrawParams DrawParms object to specify various drawing parameters
-
Use myPage.DrawContents(myGraphics, DrawParms) method to draw the graphics contents to myBitmap via myGraphics and thus to myPictureBox
Voila! The page’s content is drawn to the standard .Net control.
In the steps above, you can see examples of all of the benefits I listed earlier. All the objects, like the myPage, myBitmap, myGraphics, myMatrix, myDrawParams, myPictureBox don’t require any explicit memory deallocation when you’re done with them. Getting a page from a PDF document is as simple as it could possible be: a single code line using the GetPage() method and storing the results in yet another DLE object, Page. DLE interoperates with standard .Net classes, like the Graphics class, quite well.
Digging Deeper: Transformations Transformed
The matrix operations (in the steps above) in particular are greatly simplified for you when you use DLE vs. the Old School (pre-DLE) approach. With pre-DLE, you would need to construct various matrices to perform scaling, translation, and rotation operations. With DLE, you just create a myMatrix object, using the Scale(), Translate(), and Rotate() methods, and DLE constructs the matrices and performs the matrix mathematics for you, behind the scenes.
With DLE, you express scaling as simply and intuitively as one possibly could, in terms of two numbers: the X scaling and the Y scaling, instead of manipulating the [sX 0 0 sY 0 0] scaling matrix directly, as you would pre-DLE. Same with translation. You supply X and Y translation numbers instead of using the [1 0 0 1 tX tY] translation matrix. Rotation is greatly simplified: you supply a single piece of information, the number of degrees of counter-clockwise rotation, a single parameter, instead of using the [cos T sin T -sin T cos T 0 0] rotation matrix!
The Matrix class still consists of the [A B C D H V] matrix, but represented internally as six properties of the object. But you rarely need to manipulate these properties directly in your code – but you still can, as they’re public and gettable and settable – as the transformation methods do the heavy lifting for you.
To illustrate the differences between the pre-DLE and DLE methods, first, creating a rotation matrix the pre-DLE way, using C:
double pi = 3.14159;
ASFixedMatrix myMatrix;
double rotationDegrees = 45;
double rotationRadians = rotationDegrees * pi / 180.0;
myMatrix.a = cos(rotationRadians);
myMatrix.b = sin(rotationRadians);
myMatrix.c = -sin(rotationRadians);
myMatrix.d = cos(rotationRadians);
myMatrix.h = 0.0;
myMatrix.v = 0.0;
and creating a rotation matrix the DLE way, using C#:
Matrix myMatrix = new Matrix().Rotate(45);
You get the idea. I know which way I’d rather do it.
Furthermore, each of the DLE transformation methods performs work on a matrix and returns a matrix type, so you can perform any number of these methods “dotted” together, like this, for example:
Matrix myMatrix = new Matrix().Scale(3, 4).Rotate(45).Translate(0, 1);
In a single line, we have created a matrix that, when applied in our case to a bitmap in myPage.DrawContents(), will scale, rotate, and translate it (using standard SRT transformation order, as transformation operations are not commutative).
(For those not familiar with the PDF Library transformation matrices don’t worry too much about it. These matrices are a way to mathematically represent transformation operations, and matrix mathematics is a way to implement them. With DLE, these are abstracted away from you with these new transformation methods built into the class.)
Coming Next…
In follow-up articles I’ll write more about the special requirements of this application, like appending one PDF document with another, stitching PDF documents together in one giant, continuous viewing page, getting and displaying bookmarks from the PDF, manipulating PDF layers (a.k.a. Optional Content Groups), and printing to a specialty printer such as a roller printer or plotter with seamless, continuous printing, and how I implemented them all using DLE.
February 15, 2008 at 1:44 am
[...] has mentioned the many advantages of DLE, using C#. C# is an improvement over coding in C or C++, since it [...]