|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
This article is in the Product Showcase section for our sponsors at The Code Project. These reviews are intended to provide you with information on products and services that we consider useful and of value to developers.
IntroductionUsing 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 3.5 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. ImplementationAt 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:
The basic requirements for building a document viewer using Silverlight would be:
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 ObjectThe 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 ImagesDownloading 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, 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 != "" )
{
elem.Fill.setSource(_downloader, src.substring(1));
}
// loop recursively through all children of the current element
ProcessElements(elem.children);
...
}
}
Rendering TextAccurate 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 ' 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 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() == "TextBlock" )
{
elem.setFontSource( _downloader );
}
The text element’s Server Side ScriptsThe 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 PDFThe 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.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||