Ok, since I did find out that I can use iTextSharp to generate a PDF, I went that route.
I\'ve got it to where it will save HTML with basic formatting to a PDF. That\'s
The question applies to any JavaScript Framework that does MVC
or MVVM
. Mentioned above:
I need to pass the DOM that was generated by knockout, as a string, into my C# controller, so that I can build the PDF.
So am going to go with a simple working example to get this done in ASP.NET MVC. Never used knockout.js
before, so going to get the DOM
element and send Ajax
request with jQuery
.
The view, based on the example you referenced above: 1. Gets outerHTML
of ul
; 2. sends an Ajax
request to the controller:
<h4>People</h4>
<ul id='wanted' data-bind="foreach: people">
<li>
Name at position <span data-bind="text: $index"> </span>:
<span data-bind="text: name"> </span>
<a href="#" data-bind="click: $parent.removePerson">Remove</a>
</li>
</ul>
<button data-bind="click: addPerson">Add</button>
<button data-bind="click: getPdf">Get PDF</button>
@section scripts
{
<script src="~/Scripts/libs/knockout-3.4.0.js"></script>
<script src="~/Scripts/ajax/FileSaver.js"></script>
<script src="~/Scripts/ajax/jquery.binarytransport.js"></script>
<script src="~/Scripts/ajax/jquery-binary.js"></script>
<script type="text/javascript">
function AppViewModel() {
var self = this;
self.getPdf = function (data, event) {
$(this).downloadFile(
'@Url.Action("Index", "DomToPdf")',
{ xHtml: $('#wanted').prop('outerHTML') }
);
}
self.people = ko.observableArray([
{ name: 'Bert' },
{ name: 'Charles' },
{ name: 'Denise' }
]);
self.addPerson = function () {
self.people.push({ name: "New at " + new Date() });
};
self.removePerson = function () {
self.people.remove(this);
}
}
ko.applyBindings(new AppViewModel());
</script>
}
Notes:
jQuery
- sadly the framework doesn't support this feature out of the box last I checked. Other frameworks like AngularJS
do support binary. Reference Sending and Receiving Binary Data.jquery-binary.js
is a simple jQuery
Plugin I wrote for a couple of internal projects:
(function ($) {
$.fn.downloadFile = function(url, data, requestType) {
$.ajax({
url: url,
data: data,
type: requestType || 'POST',
dataType: 'binary'
})
.done(function(data, textStatus, jqXHR) {
var type = jqXHR.getResponseHeader('Content-Type');
var filename = jqXHR.getResponseHeader('Content-Disposition');
filename = filename && filename.indexOf('attachment') > -1
? filename.replace(/(?:[^=])+=/, '')
: 'file.bin';
var blob = new Blob([data], { type: type });
saveAs(blob, filename);
})
.fail(function(jqXHR, textStatus, errorThrown) {
alert(errorThrown);
})
;
return false;
};
}(jQuery));
And the controller: 1. parses the HTML
string with XMLWorkerHelper
; 2. returns the PDF.
[HttpPost] // some browsers have URL length limits
[ValidateInput(false)] // or throws HttpRequestValidationException
public ActionResult Index(string xHtml)
{
Response.ContentType = "application/pdf";
Response.AppendHeader(
"Content-Disposition", "attachment; filename=test.pdf"
);
using (var stringReader = new StringReader(xHtml))
{
using (Document document = new Document())
{
PdfWriter writer = PdfWriter.GetInstance(
document, Response.OutputStream
);
document.Open();
XMLWorkerHelper.GetInstance().ParseXHtml(
writer, document, stringReader
);
}
}
return new EmptyResult();
}
Will let you decide how to deal with the inline styles. ;)