Create High-Quality PDFs With Razor View Engine and LaTeX

by Christoph Menge in .NET, Software

ASP.NET MVC3 is a blast! If you haven’t tried it, you definitely should – it’s really fun to work with and brings so many cool new features! Now that the MVC3 Release Candidate is out, there’s practically nothing missing.

Most importantly however, MVC3 introduces the Razor View Engine and separates MVC and WebPages, so the web rendering is no longer hard-wired into the ASP.NET subsystem.

Apart from its much cleaner syntax, Razor is also more flexible and it’s open source. Matthew Abbot and Ben of BuildStarted.com have written some interesting articles on how to use Razor without MVC and even without ASP.NET at all.

PDFs vs. HTML

Now, what does all that have to do with PDFs? Writing to PDFs itself isn’t necessarily too hard – you can easily draw some text here and there or draw lines, for example using the excellent iTextSharp library. But the real deal is typesetting!

Unfortunately, HTML and paper don’t make a very good fit. Breaking HTML tables across pages, for example, is an insufferable pain. HTML wasn’t made with paper in mind – a good thing for HTML, and a good reason not to abuse it for printing PDFs.

Introducing RazorTex

Now, Razor makes only a few assumptions about the type of data it renders – it doesn’t really have to be HTML… With RazorTex, you can create LaTeX-Views on the fly instead! A word of warning though: RazorTex is extremely young and unstable – more sample code than anything else – so be prepared! There’s a ton of features missing, but it works. If you’re ready for a rather rough ride, go ahead!

So how does it work, behind the scenes? RazorTex will use the Razor engine to compile code into a LaTeX file. A sample section from a .cstex file might look like this:

\frac{@Model.Factor.ToString("F2")}{@Model.Denominator.ToString("F2")} &= @Model.Result.ToString("F2") \\
\int_{@Model.LowerBound}^{@Model.UpperBound} @Model.Expression @Model.IntegrationVariable &= @Model.IntegrationResult \\

The '&=' and '\\' are special LaTeX-Commands, an alignment character and a line break, respectively.

If you don’t know LaTeX, the syntax can be really terrifying at first. However, there is no doubt LaTeX is extremely powerful when it comes to typesetting complicated document layouts. For example, it supports high-quality text rendering with microtypography, breaking tables across pages, typesetting mathematical formulas and rendering vector images, to name only a few. There are many additional packages you can use for practically any type of typesetting problem.

Running the Sample

Unfortunately, there are some smaller obstacles when running the sample. Also, you’ll have to install a bunch of things:

  1. Download and install ASP.NET MVC3 Release Candidate from Microsoft. This includes the Razor view engine
  2. Download and install MiKTeX 2.8
  3. Download and install WinShell for LaTeX, an IDE for LaTeX [recommended]
  4. Before running the sample, open the two included sample .TeX files and open them with WinShell. Run the PDF Compiler. This will install the required packages. If you don’t install the packages, the sample might dead-lock!
  5. Adjust the settings in app.config according to your needs and create the temporary and output folders.
  6. Finally, the application should run and create great pdfs from a console application…

The Architecture

Let’s take a quick look at the architecture of the Razor pipeline. Most importantly, Razor views are compiled – a huge advantage when you need to render a lot files with similar structure. However, we need to care for the compilation ourselves. Fortunately, Andrew Nurse has already accomplished that for us. For details, refer to his article. In short, we read the .cstex file, create a RazorParser and throw in our template string. This will create C# code which in turn can be compiled to binary using CodeDom.GenerateCodeFromCompileUnit. Of course, there are some subtleties involved such as assigning the correct base class, importing some namespaces, and so forth.

Once they are compiled, executing the latex view is blazing fast, but compilation takes some time. Therefore, it’s a good idea to cache these. Right now, there is some caching in RazorFlux, but it’s probably a good idea to compile all the latex-views in one go (and into a single assembly) – at least, that’s the way ASP.NET does it.

Additional Features, LatexHelper

Two particularly helpful features in HTML are the HtmlHelper and UrlHelper extensions. Of course, they don’t make a lot of sense in LaTeX, but there are some LaTeX-features where a custom helper would come in handy – a LatexHelper!?

Indeed, we can inject our own helpers through the base class:

Type baseType = (modelType == null)
            ? typeof(LatexTemplate)
            : typeof(LatexTemplate<>).MakeGenericType(modelType);

generator.GeneratedClass.BaseTypes.Add(baseType);

public abstract class LatexTemplate : ILatexTemplate
{
    public LatexHelper Latex { get; set; }
    public bool SuppressEmptyLines { get; set; }
}

So far, the LatexHelper is used primarily for “display templates”. Also, the LatexHelper certainly contains the worst hacks in the code. More about that later.

Drawbacks

Some disadvantages shouldn’t go unmentioned:

  • While Razor doesn’t seem to make too many assumptions, it does assume that line breaks don’t play a role – true for HTML but damn wrong for LaTeX. I have had some trouble with that, but nothing that can’t be solved in the .cstex file. Still looking for a better solution though.
  • Pdflatex isn’t too fast – there might be faster solutions for creating PDFs, but the quality of pdflatex is very high.
  • It seems impossible to get pdflatex to work with streams instead of files, so you’ll have to write temporary files – no big deal, but a little painful. If anybody knows how to it, I’d be glad to know!

Post to Twitter Post to Delicious Post to Digg Post to Facebook

No related posts.

Tags: , , ,

← Previous

Next →

5 Comments

  1. Hi!,

    Nice to see Razor’s use in anything other than html. Funnily enough, I was just about to blog about some updates I have done to the code Ben originally started, it now supports custom base templates, as well as support for anonymous (dynamic models). Thanks for the pingback!

  2. Hi Matt,
    thanks for the quick reply! I just heard that MVC3 RC was released today, plus I saw Ben released two additional articles today… Wow, gotta catch up ;) Not a day old and already outdated – gotta love software!

  3. Gamlor says:

    Cool! Really nice that you can use the same template engine for something different. And generating PDF is not that unusual.

  4. [...] This post was mentioned on Twitter by Roman Stoffel, Christoph Menge. Christoph Menge said: Just Blogged: Creating High-Quality PDFs using #Razor and #Latex: http://bit.ly/aN0veI [...]

  5. [...] Christoph @ emphess.net might find this useful if the HtmlMarkupParser is causing problems with his RazorLaTex [...]

Leave a Comment