This question was posed to me by my dad about a year ago. Back then, he manually wrote out his invoices on template paper sheets. It was wasteful and expensive. Seriously, these stupid sheets of invoice templates are surprisingly expensive. As his first and only son, I was posed a challenge. A force to be reckoned with. (It's not that serious, I know)
Online invoicing software
In Slovakia, the place where I live, we have a handful of accounting and finance handling apps that also make invoices for you. We used one of them. It was great for 7 days. That is about enough for one invoice to go through. After those 7 days, the app is locked out until you pay up. I understand businesses need money to operate, but come on! A simple thing which takes in numbers and spits out a pretty PDF shouldn't need to be paid for.
Making my own!
I'm predominantly a front end web developer. On occasion, I find myself working with back end stuff. The language I picked out is Node.js. Why? Because it works and is the only back end language I fully grasp. Now... How do I make an app that takes in numbers and spits out pretty PDFs? My first thought was to use Puppeteer, a headless Chromium browser, since it handles exporting PDFs quite well. I can already hear some face palms. Yes, there are better options out there (I'll get to it later) but in the rush of trying to find a quick solution, I overlooked the faster, and in my opinion, better option out there.
Because Puppeteer is a browser, I had to write my code in HTML. Problem #1: I couldn't get Puppeteer to apply a style sheet from a file. Solution: Use inline styles. This made my code awful as I had a huge block of HTML in a template string in my otherwise very clean JS code. Problem #2: Dev tools doesn't offer a page size simulator. In this case, I mean page size as in a sheet of paper. I had to guess which resolution looks like A4 the most. Problem #3: Fonts just refused to import if they were locally stored. Therefore, I had to request the font (Poppins in this case) from Google Fonts every time the request was launched. Last but not least: I couldn't use flex boxes. This is because of the way Chromium handles converting a view port from desktop size to A4 size. It just gets very messy. Everything had to be positioned relative it its parent and the parent div's were positioned relative to the body.
By the end of this, one request takes about 2 seconds to process and the final output looks like a child drew it on a piece of paper. Below, you can see an example of it. (with private stuff covered up of course)

There's all sorts of boxes with different thicknesses and inconsistent spacing on text. It works for my dad as he's not an IT guy - he doesn't need the fanciest invoices with QR codes and whatever. I've been meaning to change it up for a while and last month, I finally got the courage for it.
Revision #2
Clearly, Puppeteer wasn't a good choice. I'm sure with a little bit of tweaking I could've solved all of those issues without having to rewrite anything but either way.. The speed and design was bugging me.
I had one goal. Make daddy proud. I deleted the entire server.js file and made a new one. This time, throwing out Puppeteer and replacing it with PDFKit. This library is very special because it works somewhat like the canvas in HTML5. As such, I already had a level of familiarity with it. PDFKit also doesn't use a browser for rendering stuff. I could've also gone with LaTeX as I know how to use it and I even have used it in the past but it's not like I'm writing entire formulas to calculate exchange rates. PDFKit was simply the better option here.
With these changes, I don't have ugly code anymore and the invoices look very neat in my opinion. I switched out the font for something a little bit more professional and authoritative, organized stuff with lines rather than boxes and finally implemented automatic line total and grand total calculations. Sometimes we have to address invoices to different companies and before revision #2, I had to manually edit the source code every time I wanted to change it. Not anymore! I just specify which company I want in the request.

My father is proud but the cash flow hasn't changed.
✌️