jspdf-invoice-template
jspdf-invoice-template copied to clipboard
Added Support for Languages and Symbols via Custom Fonts
Currently the project only supports generating an invoice with the default fonts as outline in various issues (and I myself have experienced with currency symbols) there is no way to interact with the jsPDF document prior to the process being run and adding the fonts and setting it as the default after the fact does not help as the document has already been generated.
As documented in the readme this adds the following support:
You can use the onJsPDFDocCreation
property arrow function to hook into configuring the jsPDF functionality as soon as it's initialized.
Allowing the support of multiple languages and currencies for your invoice as outlined in jsPDF documentation:
Use of Unicode Characters / UTF-8: The 14 standard fonts in PDF are limited to the ASCII-codepage. If you want to use UTF-8 you have to integrate a custom font, which provides the needed glyphs.
Example Usage:
jsPDFInvoiceTemplate({
//https://github.com/edisonneza/jspdf-invoice-template/issues/20#issuecomment-1859975854
onJsPDFDocCreation: (doc: jsPDF) => {
//var font = "...";
doc.addFileToVFS('LiberationSans-Regular-normal.ttf', font);
doc.addFont('LiberationSans-Regular-normal.ttf', 'LiberationSans-Regular', 'normal');
doc.setFont('LiberationSans-Regular');
},
});
Related issues that this resolves: #20 #35
@edisonneza when you get a chance can you please review and merge, if looks alright to you. 🙏 Should help those waiting for a fix myself included.
Hi @benjamin-stern, first of all thanks for the contribution. Really appreciate it. I will merge it asap, but firstly i wanted to make some tests. Did the tests on your side work properly? With what fonts did u make the tests? Can u provide some screenshots of the fonts tested?
Thank you!
Hey @edisonneza,
first of all thanks for the contribution. Really appreciate it.
The thanks is mutual, you created this repository and maintained it until now.
I will merge it asap, but firstly i wanted to make some tests.
🙏
Did the tests on your side work properly?
Yes as outlined above (as well as in the modified version of the readme), I was able to get it to work correctly for my instance which was currencies.
But I also tested the text outlined in the issues: #20 #35
Currencies: $₪₠€₨₹£₱
Arabic: نعم ، هذا يعمل يا أخي
Japanese: こんにちは, 簡単なテスト
With what fonts did u make the tests?
I don't have many fonts that I needed to test, so I just used one for the currencies and different ones to make sure the tests passed:
- Arial Unicode Font.ttf
- NotoSansArabic-Regular.ttf
- NotoSansCJKjp-VF.ttf
- NotoSans-Regular.ttf
- NotoSerifLiving-Regular.ttf
- LiberationSans-Regular-normal.ttf
Sources: https://github.com/notofonts/notofonts.github.io https://github.com/notofonts/noto-cjk/blob/main/Sans/Variable/TTF/NotoSansCJKjp-VF.ttf https://github.com/taylor/fonts
Can u provide some screenshots of the fonts tested?
Default (no specific font)
Japanese, Issue #20 (NotoSansCJKjp-VF.ttf)
Arabic, Issue #35 (NotoSansArabic-Regular.ttf)
Currencies, My Issue (unifont-15.1.05.ttf)
Serif (NotoSerifLiving-Regular.ttf)
Most everything (Arial Unicode Font.ttf)
Example
Fonts can be converted using the jsPDF form: https://rawgit.com/MrRio/jsPDF/master/fontconverter/fontconverter.html
Or you can use a stub I used to test the fonts and invoices, using TypeScript for testing as it matches my production environment:
async function fetchFileAsBinaryString(url): Promise<string> {
try {
const response = await fetch(url);
const blob = await response.blob();
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
const arrayBuffer = reader.result;
const binaryString = arrayBufferToBinaryString(arrayBuffer);
resolve(binaryString);
};
reader.onerror = reject;
reader.readAsArrayBuffer(blob);
});
} catch (error) {
console.error('Error fetching file:', error);
throw error;
}
}
function arrayBufferToBinaryString(arrayBuffer) {
const uint8Array = new Uint8Array(arrayBuffer);
return uint8Array.reduce((binaryString, byte) => binaryString + String.fromCharCode(byte), '');
}
var fontFileName = "unifont-15.1.05";
var fetchRequest = await fetchFileAsBinaryString(`/${fontFileName}.ttf`);
var propsTest = {
"outputType": OutputType.Save,
onJsPDFDocCreation: (doc: jsPDF) => {
doc.addFileToVFS(`${fontFileName}.ttf`, fetchRequest);
doc.addFont(`${fontFileName}.ttf`, `${fontFileName}`, 'normal');
doc.setFont(`${fontFileName}`);
},
"returnJsPDFDocObject": true,
"fileName": "Generic Company Receipt",
"orientationLandscape": false,
"compress": true,
"logo": {
"src": "data:image/png;base64,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",
"type": "PNG",
"width": 15,
"height": 15,
"margin": {
"top": 0,
"left": 0
}
},
"business": {
"name": "Generic Company Ltd",
"address": "1121 Tower Rd, Beverly Hills, CA, USA",
"email": "[email protected]",
"website": "https://www.example.com"
},
"contact": {
"label": "Issued to:",
"name": "Customer Name",
"address": null,
"otherInfo": "---"
},
"invoice": {
"label": "Receipt #",
"num": 1234567890,
"invDate": "Payment Date:4/17/2024",
"headerBorder": false,
"tableBodyBorder": false,
"header": [
{
"title": "#",
"style": {
"width": 10
}
},
{
"title": "Title",
"style": {
"width": 80
}
},
{
"title": "Price"
},
{
"title": "Total"
}
],
"table": [
[
1,
"Watermark Removal",
"$33.82",
"$33.82"
],
[
2,
"Currencies",
"$₪₠€₨₹£₱",
"$33.82"
],
[
3,
"Arabic",
"نعم ، هذا يعمل يا أخي",
"$33.82"
],
[
4,
"Japanese",
"こんにちは, 簡単なテスト",
"$33.82"
]
]
},
"footer": {},
"pageEnable": false,
"pageLabel": "Page "
};
const _pdfObject = jsPDFInvoiceTemplate(propsTest);
Hey @edisonneza,
Any progress on evaluating and merging the PR? Having functionality like this (or similar) merged would be greatly appreciated allowing a broader range of functionality and versatility.
Hey @benjamin-stern, really i've been too busy in some other projects.
I'll try to merge it and publish within 2 days.
Thanks!
hi @benjamin-stern just merged and published the new version.
if u have to generate any invoice with it let me know if it is working properly after the publish.
Thanks!
Hey @edisonneza, Sorry for the delay, just a heads up it (as requested) the new build 1.4.4 is working as expected.
Wonderful, thanks for your feedback!