Introduction
There’s a trend to implement web
applications as so-called Single Page
Applications or SPA for short.
These are apps that do most of their job in asynchronous and user-responsive
way providing an experience similar to desktop apps, avoiding the need for page
reloading.
In this article, we’ll show how
to create a simple PDF viewer implemented as single page app using ASP.NET Web
API for the server side and AngularJS for the client side. We’ve also used a
bit of CSS to make it more interesting.
Creating the project
Open Visual Studio and select File -> New -> Project…
-> Web -> ASP .NET Web Application as shown on the illustration below:
Pic. 1 Creating new project
|
Click OK when
you’re ready, and you’ll be redirected to template selection wizard.
Template selection
We’ll use the Web API template as
we are going to create a very simple UI based exclusively on HTML / CSS and
AngularJS. In this example we didn’t use any authentication, so turned it off
by selecting the No Authentication
option.
Pic. 2 Finalizing project creation
|
Click OK when
you’re ready to finish the project creation.
Adding packages
Install AngularJS core and Apitron
PDF Rasterizer for .NET packages
via NuGet package manager.
Server side code
The RenderingController shown below, returns document info
and renders pages by request.
public class RenderingController : ApiController
{
private static readonly string filePath;
static RenderingController()
{
filePath = HttpContext.Current.Server.MapPath(
"~/App_Data/Apitron_Pdf_Kit_in_Action.pdf");
}
/// <summary>
/// Gets the rendered page as encoded image.
/// </summary>
/// <param
name="id">Page index, zero-based.</param>
/// <returns>String containing encoded image, or null if the call
fails.</returns>
public string GetRenderedPage(int id)
{
using (Document doc = new Document(File.OpenRead(filePath)))
{
if (id >= 0 && doc.Pages.Count > id)
{
Bitmap bm = doc.Pages[id].Render(new Resolution(72, 72),
new RenderingSettings());
if (bm != null)
{
using (MemoryStream ms = new MemoryStream())
{
bm.Save(ms, ImageFormat.Png);
return string.Format("data:image/png;base64,{0}",
Convert.ToBase64String(ms.ToArray()));
}
}
}
}
return null;
}
/// <summary> Returns document info. </summary>
/// <returns>Valid document info object, or null if the call fails.</returns>
public DocumentInfo GetFileInfo()
{
try
{
using (Document doc = new Document(File.OpenRead(filePath)))
{
return new DocumentInfo() {PageCount = doc.Pages.Count};
}
}
catch (Exception e)
{
Debug.WriteLine(e.Message);
return null;
}
}
}
In order to properly map
requests to our controller, change the default route registered in WebApiConfig
class as folllows:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web
API configuration and services
// Web
API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional
}
);
}
}
UI template
The html provided below is located in the root of the
project and set as startup page for it (see index.html). It references the CSS stylesheet and
scripts needed to run the AngularJS app.
<!DOCTYPE html>
<html ng-app="mainApp" xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<script src="/Scripts/angular.min.js"></script>
<script src="/Scripts/app/app.js"></script>
<script src="/Scripts/app/renderingController.js"></script>
<link rel="stylesheet" type="text/css" href="Content/styles.css">
</head>
<body ng-controller="pageByPageRenderingController" ng-init="setup()">
<div class="navbarContainer">
<ul>
<li ng-click="loadFile()">Load
file</li>
<li ng-click="renderPrev()">Prev</li>
<li ng-click="renderNext()">Next</li>
<li onclick="alert('Apitron PDF Rasterizer usage sample,
implemented using WebAPI
and AngularJS.')">About</li>
</ul>
</div>
<div class="viewArea">
<span id="aligner">{{errorText}}</span>
<img ng-src="/images/loading.gif" class="{{progressBarClass}}"/>
<img ng-src="{{imageSource}}" class="{{pageViewClass}} shadow"/>
</div>
</body>
</html>
AngularJS part
This part consists of app.js and renderingController.js files located in “/Scripts/app” project
folder.
app.js - contains the code needed to configure our app’s module
that depends on renderingController
module that performs the actual interaction with our Web API methods.
var mainApp =
angular.module('mainApp', ['renderingController']);
renderingController.js - contains the code for pageByPageRenderingController object
which implements the methods referenced in UI template, e.g. setup(), loadFile() etc. The complete code for this controller can be found
in corresponding sample project and downloaded from our GitHub
repo.
Results
We kept the app simple, and as it
can be seen from the RenderingController’s code, we didn’t use any caching and
implemented page rendering using the simplest way: open the file and render the
requested page for every request.
The image shown below
demonstrates the resulting PDF viewer single page application:
Pic. 3 PDF Viewer single page app
|
Conclusion
If you plan to implement your web
app as a single page app and need to integrate PDF preview or PDF processing
capabilities, then Apitron’s .NET PDF components such as Apitron PDF Rasterizer or Apitron PDF Kit can be easily
employed to achieve the best results.
In this article we have shown how
to implement basic pdf preview, but it’s also possible to implement full
featured pdf viewer with loading of the document bookmarks, support for
internal navigation, and so on. You can also read and create document fields,
create PDF forms, extract text, sign documents and do whatever you want with
them using our PDF components.
If you have any questions
regarding our products, just let us know and we’ll be happy to answer them.
Downloadable version of this article can be found by the following link [PDF].
Downloadable version of this article can be found by the following link [PDF].