Click here to Skip to main content
11,583,859 members (60,975 online)
Click here to Skip to main content

PDF for Silverlight

, , 30 Jun 2008 CPOL 78.6K 21
Leveraging the power of Silverlight to view PDF documents and forms

Editorial Note

This article is in the Product Showcase section for our sponsors at CodeProject. These reviews are intended to provide you with information on products and services that we consider useful and of value to developers.

Introduction

Read the latest advertorial from Amyuni here

Using Microsoft Silverlight, developers can provide their users with content-rich web applications that are not limited to text and images, and which can now include complex graphics and better interaction with the users. While reading some MSDN articles about the power of Silverlight and the types of applications that can be built with it, I came across Jeff Prosise’s article in the May 2008 MSDN issue. It struck me that the author had converted his sample documents into poor quality JPEG images in order to demonstrate the "Page-Turn" Silverlight application. A more natural choice for me would be to use high-quality PDF documents that would have been easily readable by the users. Now that PDF has become the standard format for document storage and forms processing in most corporations, I thought that Silverlight applications would benefit from the ability to natively serve PDF documents and forms.

In this article, I will show how to use the Amyuni PDF components to dynamically view PDF documents within a Silverlight control. I will adapt the page turn framework to viewing PDF, XPS or any type of document rather than JPEG images. The documents are located on the server and viewed by the client within a Silverlight control without having to download any files or components to the client. The framework that is presented can be easily expanded to add inter-activity when the PDF files contain interactive form fields.

The Amyuni PDF Creator and Converter versions 4.0 components are needed to run the sample and can be found here. The sample can also be run directly from the same location.

Requirements (Server Side)

The sample that is given is based on ASP, but any programming environment that supports either ActiveX or .NET can be used.

Requirements (Client Side)

Any web browser running on any operating system with the Silverlight control version 1.0 or higher. No additional components need to be downloaded or run on the client PC.

Implementation

At first glance, viewing PDF documents within a Silverlight control looked like a 15-minute job. It would be sufficient to convert the PDF document into an XPS, which is a derivative of XAML, feed the XPS to the Silverlight control, add some bells and whistles and be done. Converting a PDF, or any document for that matter, into an XPS can easily be achieved with the Amyuni PDF Creator; all that was needed is a way to feed the XPS into the Silverlight control. I was, however, faced with a number of challenges:

  1. Silverlight supports reading only a single XAML file. There is no mechanism by which one can feed multiple pages separately, nor is there a way to feed all the resources such as images and fonts used by that page. Silverlight provides no neat way of packaging a document the same way that PDF and XPS do, but requires all the bits and pieces of a document to be created separately on the server and downloaded programmatically using the calling application. This precludes the ability to dynamically stream a document from a server to a client, which is a basic requirement of most applications.
  2. Inconsistencies in the Silverlight object model made trial and error the most time-consuming part of building the sample. Examples of inconsistencies:
    • Accessing the width of a canvas object is done using elem.Width, assuming that elem is an object of type Canvas. Accessing the left position using elem.Left returns an error and should be replaced with elem.GetValue(“Canvas.Left”).
    • With complex objects such as the Path object, it is difficult to figure out how to access a specific attribute. In the following object: <Path Opacity=“1.00”><Path.Fill><ImageBrush ImageSource=“image1.jpeg”/>
      To access the Opacity, one can simply use elem.Opacity. To access the ImageSource, the right syntax is elem.Fill.ImageSource; the ImageBrush element should be skipped altogether, which did not make sense to me.
  3. The image object provided by Silverlight is limited in that it does not allow for inline image data. The only way to specify the source for an image is by using a URL. To extract an image from a PDF and set it as the source for a Silverlight image object would require extracting the image into a temporary file on the server and setting the source of the image as the temporary URL: a solution that is not feasible in any real-life situation.

The basic requirements for building a document viewer using Silverlight would be:

  • The ability to feed each page separately to account for large documents and to prevent having to download the whole document to the client PC before viewing it.
  • The ability to package page resources such as images and fonts within the document without having to store and retrieve the resources from the server.
  • The ability to use fonts that are not installed on the client PC, but are embedded within the source document.
  • The ability to send additional information known as document metadata to the client.

All these requirements are accounted for by the XPS specifications, and it might have made sense for Microsoft to support XPS directly into Silverlight. As this is not the case, I had to build a modified XPS package with XAML instructions that are supported by Silverlight and write all the code that is needed to feed the modified XPS into the Silverlight component.

For the complete PDF for Silverlight sample, including a test web page, source-code and the Amyuni PDF Suite product needed to run the sample, please visit this page.

Using the Downloader Object

The Downloader object provided me with the solution to a number of the requirements. This Downloader allows for packaging multiple objects in a ZIP file and extracting each object separately on the client PC. Within the ZIP package, each page description can be stored as a separate XAML file and all resources used by that page stored in a virtual folder. Downloading the objects is also asynchronous, meaning that the user does not have to wait for a large document to be fully downloaded. What I found most useful about the Downloader object is its ability to refer to dynamic pages containing scripts, and not only static files. This means that the ZIP package can be dynamically created on the server and streamed back to the client without resorting to temporary files, which is exactly what I needed. The basic syntax for using the Downloader is as follows:

function onPageLoad(control, context, root)
{
    // Instantiate the downloader object and store a reference for later use
    _downloader = _pdfView.createObject('downloader');
    
    // Add an event listener to detect when data is available
    _token2 = _downloader.addEventListener('completed', downloadCompleted);

    // Request a PDF document from the Server
    // The Amyuni PDF Creator will be used on the server to load the PDF
    // and return it in a ZIP package similar in format to XPS
    _downloader.open('GET', '/PageTurnPdf/pdf.asp?PDFFile=doc.pdf');

    // Begin asynchronous download of the zip package
    _downloader.send();
}
 
function downloadCompleted(sender, args)
{    
    // Retrieve the XAML description of the document from the downloaded package file
    var Document = sender.getResponseText('Document/document_1.fdoc');

    // Doanload the XAML description of a specific page
    var Xaml = _downloader.getResponseText('Pages/page_' + page + '.fpage');

    ...

Extracting and Rendering Images

Downloading the XAML description of the page to the client is not sufficient to display the images. An additional step is needed to loop through all the image objects and set the source for every image to one of the images in the ZIP package. Each time a new page is loaded, ProcessElements is called. This function searches for all images on a page and sets their ImageSource attribute:

function ProcessElements(elems)
{
// process all page elements to set:
//  - the source of all image elements
//  - the font of all text elements
//
    for ( i = 0; i < elems.Count; i++ )
    {
        var elem = elems.getItem(i);
        var src = elem.Fill.ImageSource;
        if ( src != null && src != &quot;&quot; )
        {
            elem.Fill.setSource(_downloader, src.substring(1));
        }

        // loop recursively through all children of the current element
        ProcessElements(elem.children);

    ...
    }
}

Try/Catch blocks are not shown here, but are needed as we do not know in advance if an object has an ImageSource or a Children attribute.

Rendering Text

Accurate positioning and rendering of text elements proved to be trickier than graphics and images. PDF provides a number of ways for defining text encodings and font types. Both XPS and XAML are limited to Unicode text and XAML provides much less flexibility than its counterparts for rendering text. To alleviate text issues, Amyuni PDF components provide the developers with methods to optimize the text content of a page and convert all text to Unicode. The OptimizeDocument method is used on the server to optimize text content before attempting to render it in a Silverlight control:

' Optimize the document to line level in order to improve the XAML export
objPdf.OptimizeDocument 1

When the font used in the source document is not available on the client PC, Silverlight would substitute with a different font which usually gives results that are not satisfactory. Silverlight provides a mechanism for specifying the font used to display a specific element, as long as the font is in OpenType or TrueType format. The Downloader object can be used in this case to package all the fonts used by the document. The fonts are embedded in the ZIP package as partially embedded fonts that are not usable by the end-user in order to avoid font licensing issues. Some font manufacturers might still limit the use of licensed fonts, in which case it is recommended that the documents do not use any licensed fonts.

In parallel to the image processing described above, the sample viewer loops through all text objects and sets the font source for each object to be one of the fonts in the ZIP package:

// set the font source for all text elements to fonts retrieved from the ZIP package
if ( elem.toString() == &quot;TextBlock&quot; )
{
    elem.setFontSource( _downloader );
}

The text element’s FontFamily attribute maps the text’s font to one of the fonts packaged in the ZIP file. This is done automatically by the Silverlight component when the requested font is not located on the end-user’s system.

Server Side Scripts

The server-side code that converts the document into XAML and builds the ZIP package is very straightforward and uses the Amyuni PDF Creator ActiveX:

<%
' Set the returned content type to be a ZIP package
Response.ContentType = “application/x-zip-compressed”

' Get the PDF filename requested by the user
strFilePath = Request.QueryString( “PDFFile” )
        
' Create the PDF Creator ActiveX object and open the document
' This will throw an exception if the control is not installed
' or the document not found
Set objPdf = Server.CreateObject(“PDFCreactiveX.PDFCreactiveX”)
objPdf.Open Server.MapPath( strFilePath ), ““

' Optimize the document to line level in order to improve the XAML export
objPdf.OptimizeDocument 1

' return the XAML attribute of the document object which is a ZIP package
Response.BinaryWrite objPdf.ObjectAttribute( “Document”, “XAML” )

Set objPdf = Nothing
%>

Expanding the Sample to View Documents other than PDF

The server-side script can easily be expanded in order to render other types of documents. To render XPS documents, the document should be loaded by the PDF Creator control and saved back into XAML to make it compatible with Silverlight. This can be done by replacing the PDF file with an XPS in the server-side script, i.e. no code changes are needed.

To render other types of documents, they should be converted first into PDF using the Amyuni PDF Converter, then streamed back to the client as a ZIP package. The PDF Converter product is available for download from this page. A sample for converting documents into PDF on a server can be found here.

  • For complete source code and an updated version of the article and the sample, please visit this page.

More from Amyuni

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Authors

Amyuni Tech Inc.
Web Developer Amyuni Technologies
Canada Canada
For over ten years Amyuni Technologies has been providing developers and end-users with powerful, reliable software tools that facilitate daily document management.

Our PDF technologies are:

- Behind thousands of applications and millions of desktops worldwide
- The most high-performing PDF development products on the market today
- Proprietary–enabling us to provide PDF solutions of the highest standard

Visit our website at www.amyuni.com to learn more

Dany Amiouny
Chief Technology Officer Amyuni Technologies
Canada Canada
Dany Amiouny started the development of the Amyuni PDF tools back in 1998. These tools are now embedded into thousands of applications and millions of desktops worldwide, and maintained by a team of experienced software developers. An expert in document conversion and processing, Dany is the CTO of Amyuni Technologies and provides consulting services to corporations worldwide. Dany holds a "Bachelor of Engineering" degree from the American University of Beirut and a "Masters in Business Administration" degree from McGill University.

You may also be interested in...

Comments and Discussions

 
GeneralVersion for ASPX Pin
rishj15-May-10 4:20
memberrishj15-May-10 4:20 
GeneralThat's what i'm looking for! Pin
tmypawa21-Mar-10 22:35
membertmypawa21-Mar-10 22:35 
Generalthanks Pin
prawinkumarpauldavid14-Aug-09 23:38
memberprawinkumarpauldavid14-Aug-09 23:38 
Generalexactly what we need Pin
YKhalil23-Sep-08 21:57
memberYKhalil23-Sep-08 21:57 
GeneralI'm really pleased to see this. Pin
HowardKeziah16-Jul-08 8:10
memberHowardKeziah16-Jul-08 8:10 
QuestionWhy do we need that? Pin
Michael Sync14-Jul-08 20:15
memberMichael Sync14-Jul-08 20:15 
AnswerRe: Why do we need that? Pin
The Dogcow Farmer12-Aug-08 12:49
memberThe Dogcow Farmer12-Aug-08 12:49 
GeneralRe: Why do we need that? Pin
Dmitri Nesteruk24-Aug-09 19:06
memberDmitri Nesteruk24-Aug-09 19:06 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.150603.1 | Last Updated 30 Jun 2008
Article Copyright 2008 by Amyuni Tech Inc., Dany Amiouny
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid