Although this post is old, I came across the same problem and found a solution. As @jkraybill mentioned above, Visualforce will not emit binary data. This means we can't retrieve a Blob from Salesforce to our client. A solution to this problem is to change the controller getter to return a (Base64) String and convert it to a Blob via Javascript, then load the Blob as a PDF. A good post explaining how to convert Base64 to a Blob can be found here. I recommend reading that post as it goes more indepth and offers a more efficient method of doing the conversion. A working example of the solution is as follows (code is from the article above):
<apex:page controller="pdfpageController">
<script>
var base64String = "{!pdfData}";
const byteCharacters = atob(base64String);
const byteNumbers = new Array(byteCharacters.length);
for (let i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
const blob = new Blob([byteArray], {type: 'application/pdf'});
const blobUrl = URL.createObjectURL(blob);
window.location = blobUrl;
</script>
</apex:page>
I have a pdfData getter method in my controller that returns a (Base64) String.
Although this solution works, if you open the Visualforce page inside a sub tab (inside a console app) the page will not load because setting window.location is blocked in lightning. Instead, we need to create an iframe and set the source of it to our blobUrl.
<apex:page controller="pdfpageController">
<iframe id="pdf"/>
<script>
var base64String = "{!pdfData}";
const byteCharacters = atob(base64String);
const byteNumbers = new Array(byteCharacters.length);
for (let i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
const blob = new Blob([byteArray], {type: 'application/pdf'});
const blobUrl = URL.createObjectURL(blob);
document.getElementById('pdf').setAttribute('src', blobUrl);
</script>
</apex:page>
This method is slightly better than setting the iframe source directly to the Base64 String since some browsers (like Chrome) have limits on the length an html element can be and if our Base64 String is over a certain length (2mb for Chrome), it won't be rendered. This should support rendering PDFs up to 6mb as that is the Apex variable size limit.