Use React Components to generate a pdf on Server Side - javascript

I have an application on React that shows some information based on a json stored in my Server. So when the user opens my website it gets the JSON and renders it. I want to implement a new function that enables the user also to download a PDF (related to that specific JSON). And my idea is when the JSON is available on my server I generate the PDF (completely server side) and store it. When someone goes on the front and pushes the button to Download the PDF, it goes on my storage and downloads the PDF. If I can generate the pdf based on React Components it would be much easier (because they are already implemented on the React Application).
I am newbie to node but little experienced with React and don't know if it is possible to solve the problem like this but basically my idea was providing React a JSON, then it will generate the MainComponent based on it, then I get the html of this Component and finally generate the PDF to store on the server. On pseudo code:
const fs = require('fs');
let required_data = JSON.parse(fs.readFileSync('myJson.json'));
html = renderToStaticMarkup(<MainComponent initialJson={required_data})
And then with this html I create the pdf with some library like html-pdf, jspdf etc.. and save it to the server. Is it possible somehow to use this approach to solve my problem?

I could solve my problem using Puppeteers headless Chrome.
First I generated the html of my application
html = ReactDOMServer.renderToStaticMarkup(<MainComponent initialJson={required_data} />)
then I opened Puppeteers' Chrome, used the html on page, and printed it to PDF. I got really satisfatory results:
const browser = await Puppeteer.launch(....)
let page = await browser.newPage()
await page.setContent(html)
let pdf = await page.pdf(...); ///This is the PDF File
await browser.close();

Related

Generate a PDF from HTML and merge another existing PDF

What I have now
An Angular - NestJS app. The client makes a request to the backend, the response is a blob and it's a whole HTML page formatted using Paper CSS (multiple pages). The blob url is used to set the src of an iframe. The iframe is a sort of preview. The user checks that everything is okay and triggers the native print functionality:
print(): void {
this.iframe.nativeElement.contentWindow?.focus();
this.iframe.nativeElement.contentWindow?.print();
}
Saves it as PDF and everything is fine.
New requirement
Generate programmatically this PDF and add a page from another PDF file. This other PDF is a byte array from the database.
What I tried
I used jsPDF, simply
fetch(this.iframe.nativeElement.src)
.then((response) => response.text())
.then(async (html) => {
const doc = new jsPDF();
doc.html(html, {
callback: function (doc) {
doc.save('file.pdf');
},
});
});
But the result is a giant PDF (font size wise), the table borders and the formatting are not maintained. An 8 pages doc becomes a 50 pages one.
I also tried html2cavans but I get some error because I have to create the HTML element, it's not an actual DOM.
I would like to avoid to do it in the backend via headless browser or similar because I am not sure I can install other software on the production server (customer managed server).
Do you a suggestion about how to achieve this?

Cannot download pdf from file-saver.js react

I am trying to download the pdf from this url :
http://www.africau.edu/images/default/sample.pdf
I followed the example and wrote the code below.
import { saveAs } from "file-saver";
const downloadPDF = ()=>{
var FileSaver = require("file-saver");
FileSaver.saveAs(
"http://www.africau.edu/images/default/sample.pdf",
"somehthing.pdf"
);
}
However, when the downloadPDF function is invoked on the button pressed. The file is not being saved. The pdf is simply being opened in the new tab.
The screenshot of what the pdf looks like in the new tab is shown below.
How do I save the pdf file?
Also, is this approach to get the pdf even valid in the first place or is axios.get() more preferred approach to get the file, then save the response file (response.body) via FileSaver.saveAs()
If the question is unclear, please let me know in the comment before flagging - I will make the necessary update. Thank you
seems like the FileSaver does not help.
However if the file is coming from the server we recommend you to first try to use Content-Disposition attachment response header as it has more cross-browser compatiblity.
as far as I know, there are 2 ways to download file in browser.
server returns a response with header Content-Disposition with value attachment or header Content-Type with value application/octet-stream. Browser will promote the SaveDialog and handle this download for you. This is preferred way to download but this requires you to have control over the server.
you just use ajax or axios to get the data of any file at anywhere. then you create a dummy link to download (like this one). then browser will promote for SaveDialog and then save file to disk. This is just fine for small file but not for large files because you have to store entire file in memory before saving it to local disk.
I think option 2 is appropriate for you.
Example here. In this example, I place a file named abc.json in public folder. Note that the server must enable cors for your website origin. otherwise, there's no way for you to access that file in javascript code.

How do I let user save a file and keep editing that file in browser Javascript only?

I believe it would not be possible due to security reason as stated in many other articles on StackOverflow. However, when I use the Diagram app at https://app.diagrams.net/ I realized they could ask me to save a file, and somehow keep that file reference and whenever I click Save on the app, my local file on hard drive changes (no new download).
I know it's only possible to upload/download a file and believe you cannot edit it (using Blob with FileReader etc). How do they achieve that? The app is open source but unfortunately plowing through the source code of their File Handler I still cannot find out what API they are using. I don't remember installing any plugin or app in my browser.
I also notice there is this permission in my browser so I guess it's some standard API, but even using that as keyword, all leads back to StackOverflow articles saying it's not possible.
Is it a new API I am not aware of? What am I looking for?
You can use localStorage to achieve this without needing any other permission from the user.
localStorage.setItem("data", JSON.stringify(data));
If your data is just JSON then this would work, however if you have custom data types, you can take a look here.
Edit:
Since you wanted to save the file directly to the device and edit it, you can take a look at File System Access API. This article here explains it.
You can load the file first by using,
let fileHandle;
butOpenFile.addEventListener('click', async () => {
[fileHandle] = await window.showOpenFilePicker();
const file = await fileHandle.getFile();
const contents = await file.text();
textArea.value = contents;
});
Once you have the file handle you should be able to write to the file without requesting to download a new file everytime there is a change.
async function writeFile(fileHandle, contents) {
// Create a FileSystemWritableFileStream to write to.
const writable = await fileHandle.createWritable();
// Write the contents of the file to the stream.
await writable.write(contents);
// Close the file and write the contents to disk.
await writable.close();
}
The codes are from the article I have linked above and the article explains everything much clearly. It's worth reading.

Is it possible to download an html file from a given webpage using my local network/browser as if I downloaded it myself with javascript or nodejs?

I’m a bit new to javascriipt/nodejs and its packages. Is it possible to download a file using my local browser or network? Whenever I look up scraping html files or downloading them, it is always done through a separate package and their server doing a request to a given url. How do I make my own computer download a html file as if I did right click save as on a google chrome webpage without running into any server/security issues and errors with javascript?
Fetching a document over HTTP(S) in Node is definitely possible, although not as simple as some other languages. Here's the basic structure:
const https = require(`https`); // use http if it's an http url;
https.get(URLString, res => {
const buffers = [];
res.on(`data`, data => buffers.push(data));
res.on(`end`, ()=>{
const data = Buffer.concat(buffers);
/*
from here you can do what you want with the data. You can write it to a file
with fs, you can console.log it using data.toString(), etc.
*/
});
})
Edit: I think I missed the main question you had, give me a sec to add that.
Edit 2: If you're comfortable with doing the above, the way you access a website the same way as your browser is to open up the developer tools (F12 on Chrome) go to the network tab, find the request that the browser has made, and then using http(s).get(url, options, callback), set the exact same headers in the options that you see in your browser. Most of the time you won't need all of them, all you'll need is the authentication/session cookie.

Save HTML page to PDF with print layout

I want to allow my users to save my page as a PDF. I have created a print stylesheet and I can generate the PDF by using Javascript's print function. Here's my problem:
In Chrome, the browser generates a preview and shows it to the user. The user can then either save it or print it.
However, in IE and FF, print calls up a complex menu, and while it can generate a PDF by "printing" to PDFCreator, it's a complex process that many users won't understand.
What I want to do is to somehow duplicate the Chrome functionality for non-Chrome users. Options I have considered:
Screenshot the HTML page and render that image into a PDF with Javascript. There are libraries that do this, but I want my PDF to have the print layout.
Generate the PDF on the server and send it to the user's browser. This can be done, but it seems difficult to use the same HTML as the standard page.
My server is running PHP and the Zend framework. I can't use NodeJS or any headless browser to render on the server. Do I have any options?
I've done exactly what you want to do before using a library called dompdf (https://github.com/dompdf/dompdf). Here is how I used it:
I dropped the dompdf library into the "library" folder in my ZF project. Then, in my application when I'm ready to render the page I created a new Zend_View() object and set it up with whatever view script variables it needed. Then I called the render() function and stored the rendered output into a variable I then provided to dompdf. The code looks like this:
$html = new Zend_View();
$html->setScriptPath(APPLICATION_PATH . '/views/scripts/action_name/');
$html->assign($data); //$data contains the view variables I wanted to populate.
$bodyText = $html->render('pdf_template.phtml');
require_once(APPLICATION_PATH."/../library/dompdf/dompdf_config.inc.php");
$dompdf = new DOMPDF();
$dompdf->load_html($bodyText);
// Now you can save the rendered pdf to a file or stream to the browser:
$dompdf->stream("sample.pdf");
// Save to file:
$dompdf->render();
$pdf = $dompdf->output();
file_put_contents($filename, $pdf);

Categories