So I'm rendering a partial view in memory in order to direct the XSL-FO data in the view to a PDF renderer.
The PDF renderer can take a string, XmlDocument or Stream as input.
Here is what I'd like to do: (Edited for clarity)
Solution taken from: http://thriftybliss.spaces.live.com/blog/cns!58DA805F37F31F20!170.entry?wa=wsignin1.0&sa=362921628
var viewPage = new ViewPage();
var viewData = new ViewDataDictionary(model);
viewPage.ViewData = viewData;
var control = viewPage.LoadControl(viewName);
viewPage.Controls.Add(control);
using (var inStream = new MemoryStream())
{
using (var sw = new StreamWriter(inStream))
{
using (var tw = new HtmlTextWriter(sw))
{
viewPage.RenderControl(tw);
}
using (var outStream = new MemoryStream())
{
driver.Render(inStream, outStream);
return outStream.ToArray();
}
}
}
Here's what works, but what I'd rather not do:
var sb = new StringBuilder();
using (var sw = new StringWriter(sb))
{
using (var tw = new HtmlTextWriter(sw))
{
vp.RenderControl(tw);
}
}
var xml = new XmlDocument();
xml.LoadXml(sb.ToString());
using (var ms = new MemoryStream())
{
driver.Render(xml, ms);
return ms.ToArray();
}
Unfortunately the HtmlTextWriter doesn't seem to want to write to the StreamWriter, or perhaps I just messed something else up.
If I replace StreamWriter with StringWriter it works fine, but I can't use StringWriter because when I call .ToString() I get all kinds of escape characters that mess up the XSL-FO and the renderer throws an "Illegal Characters" exception.
I got around this by creating an XmlDocument and loading the string into that, but it seems like a very roundabout way of doing things. I'd really just like to just capture the output from the HtmlTextWriter and feed it directly into the Renderer. Is there any way to do this?
Thanks!
When you say "HtmlTextWriter doesn't seem to want to write to the StreamWriter" does that mean that driver.Render
can't get any bytes out of inStream? Two possibilities I can think of...
First is that HtmlTextWriter isn't flushing the stream after it's done writing, in which case you might need a sw.Flush()
in there before you hand the inStream to driver.Render
.
Second is that after writing to the MemoryStream, the location pointer will be at the end of the stream, and any attempts to read from it will yield zero bytes (since it's at the end).
In that case, setting inStream.Position
to 0, or calling inStream.Seek(0, SeekOrigin.Begin)
before calling driver.Render
should fix it.
I've ran into the second situation a few times before, so that's my best guess.