├── .gitignore ├── FileSaver.js ├── README.md ├── Sanchez-Regular.ttf ├── cert.pdf ├── favicon.ico ├── index.html ├── index.js ├── padhega india.png └── style.css /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .parcel-cache 3 | .cache 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /FileSaver.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | if (typeof define === "function" && define.amd) { 3 | define([], factory); 4 | } else if (typeof exports !== "undefined") { 5 | factory(); 6 | } else { 7 | var mod = { 8 | exports: {} 9 | }; 10 | factory(); 11 | global.FileSaver = mod.exports; 12 | } 13 | })(this, function () { 14 | "use strict"; 15 | 16 | /* 17 | * FileSaver.js 18 | * A saveAs() FileSaver implementation. 19 | * 20 | * By Eli Grey, http://eligrey.com 21 | * 22 | * License : https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md (MIT) 23 | * source : http://purl.eligrey.com/github/FileSaver.js 24 | */ 25 | // The one and only way of getting global scope in all environments 26 | // https://stackoverflow.com/q/3277182/1008999 27 | var _global = typeof window === 'object' && window.window === window ? window : typeof self === 'object' && self.self === self ? self : typeof global === 'object' && global.global === global ? global : void 0; 28 | 29 | function bom(blob, opts) { 30 | if (typeof opts === 'undefined') opts = { 31 | autoBom: false 32 | };else if (typeof opts !== 'object') { 33 | console.warn('Deprecated: Expected third argument to be a object'); 34 | opts = { 35 | autoBom: !opts 36 | }; 37 | } // prepend BOM for UTF-8 XML and text/* types (including HTML) 38 | // note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF 39 | 40 | if (opts.autoBom && /^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) { 41 | return new Blob([String.fromCharCode(0xFEFF), blob], { 42 | type: blob.type 43 | }); 44 | } 45 | 46 | return blob; 47 | } 48 | 49 | function download(url, name, opts) { 50 | var xhr = new XMLHttpRequest(); 51 | xhr.open('GET', url); 52 | xhr.responseType = 'blob'; 53 | 54 | xhr.onload = function () { 55 | saveAs(xhr.response, name, opts); 56 | }; 57 | 58 | xhr.onerror = function () { 59 | console.error('could not download file'); 60 | }; 61 | 62 | xhr.send(); 63 | } 64 | 65 | function corsEnabled(url) { 66 | var xhr = new XMLHttpRequest(); // use sync to avoid popup blocker 67 | 68 | xhr.open('HEAD', url, false); 69 | 70 | try { 71 | xhr.send(); 72 | } catch (e) {} 73 | 74 | return xhr.status >= 200 && xhr.status <= 299; 75 | } // `a.click()` doesn't work for all browsers (#465) 76 | 77 | 78 | function click(node) { 79 | try { 80 | node.dispatchEvent(new MouseEvent('click')); 81 | } catch (e) { 82 | var evt = document.createEvent('MouseEvents'); 83 | evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80, 20, false, false, false, false, 0, null); 84 | node.dispatchEvent(evt); 85 | } 86 | } // Detect WebView inside a native macOS app by ruling out all browsers 87 | // We just need to check for 'Safari' because all other browsers (besides Firefox) include that too 88 | // https://www.whatismybrowser.com/guides/the-latest-user-agent/macos 89 | 90 | 91 | var isMacOSWebView = /Macintosh/.test(navigator.userAgent) && /AppleWebKit/.test(navigator.userAgent) && !/Safari/.test(navigator.userAgent); 92 | var saveAs = _global.saveAs || ( // probably in some web worker 93 | typeof window !== 'object' || window !== _global ? function saveAs() {} 94 | /* noop */ 95 | // Use download attribute first if possible (#193 Lumia mobile) unless this is a macOS WebView 96 | : 'download' in HTMLAnchorElement.prototype && !isMacOSWebView ? function saveAs(blob, name, opts) { 97 | var URL = _global.URL || _global.webkitURL; 98 | var a = document.createElement('a'); 99 | name = name || blob.name || 'download'; 100 | a.download = name; 101 | a.rel = 'noopener'; // tabnabbing 102 | // TODO: detect chrome extensions & packaged apps 103 | // a.target = '_blank' 104 | 105 | if (typeof blob === 'string') { 106 | // Support regular links 107 | a.href = blob; 108 | 109 | if (a.origin !== location.origin) { 110 | corsEnabled(a.href) ? download(blob, name, opts) : click(a, a.target = '_blank'); 111 | } else { 112 | click(a); 113 | } 114 | } else { 115 | // Support blobs 116 | a.href = URL.createObjectURL(blob); 117 | setTimeout(function () { 118 | URL.revokeObjectURL(a.href); 119 | }, 4E4); // 40s 120 | 121 | setTimeout(function () { 122 | click(a); 123 | }, 0); 124 | } 125 | } // Use msSaveOrOpenBlob as a second approach 126 | : 'msSaveOrOpenBlob' in navigator ? function saveAs(blob, name, opts) { 127 | name = name || blob.name || 'download'; 128 | 129 | if (typeof blob === 'string') { 130 | if (corsEnabled(blob)) { 131 | download(blob, name, opts); 132 | } else { 133 | var a = document.createElement('a'); 134 | a.href = blob; 135 | a.target = '_blank'; 136 | setTimeout(function () { 137 | click(a); 138 | }); 139 | } 140 | } else { 141 | navigator.msSaveOrOpenBlob(bom(blob, opts), name); 142 | } 143 | } // Fallback to using FileReader and a popup 144 | : function saveAs(blob, name, opts, popup) { 145 | // Open a popup immediately do go around popup blocker 146 | // Mostly only available on user interaction and the fileReader is async so... 147 | popup = popup || open('', '_blank'); 148 | 149 | if (popup) { 150 | popup.document.title = popup.document.body.innerText = 'downloading...'; 151 | } 152 | 153 | if (typeof blob === 'string') return download(blob, name, opts); 154 | var force = blob.type === 'application/octet-stream'; 155 | 156 | var isSafari = /constructor/i.test(_global.HTMLElement) || _global.safari; 157 | 158 | var isChromeIOS = /CriOS\/[\d]+/.test(navigator.userAgent); 159 | 160 | if ((isChromeIOS || force && isSafari || isMacOSWebView) && typeof FileReader !== 'undefined') { 161 | // Safari doesn't allow downloading of blob URLs 162 | var reader = new FileReader(); 163 | 164 | reader.onloadend = function () { 165 | var url = reader.result; 166 | url = isChromeIOS ? url : url.replace(/^data:[^;]*;/, 'data:attachment/file;'); 167 | if (popup) popup.location.href = url;else location = url; 168 | popup = null; // reverse-tabnabbing #460 169 | }; 170 | 171 | reader.readAsDataURL(blob); 172 | } else { 173 | var URL = _global.URL || _global.webkitURL; 174 | var url = URL.createObjectURL(blob); 175 | if (popup) popup.location = url;else location.href = url; 176 | popup = null; // reverse-tabnabbing #460 177 | 178 | setTimeout(function () { 179 | URL.revokeObjectURL(url); 180 | }, 4E4); // 40s 181 | } 182 | }); 183 | _global.saveAs = saveAs.saveAs = saveAs; 184 | 185 | if (typeof module !== 'undefined') { 186 | module.exports = saveAs; 187 | } 188 | }); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Certificate-Generator 2 | Generate PDF certificate using JavaScript 3 | 4 | This app uses two library PDF-lib.js and FileSaver.js 5 | 6 | ![Screenshot](https://i.imgur.com/H7mcyZ3.png) 7 | 8 | # Certificate Sample 9 | ![Sample pdf](https://i.imgur.com/GFGU3K9.jpg) 10 | -------------------------------------------------------------------------------- /Sanchez-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShivamJoker/Cert-Generator/089050c0821f2126534e0f132ef8d036ad62c4f8/Sanchez-Regular.ttf -------------------------------------------------------------------------------- /cert.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShivamJoker/Cert-Generator/089050c0821f2126534e0f132ef8d036ad62c4f8/cert.pdf -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShivamJoker/Cert-Generator/089050c0821f2126534e0f132ef8d036ad62c4f8/favicon.ico -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Get Your Certificate for PadhegaIndia 8 | 9 | 10 | 11 | 12 | 13 |
14 | Padhega India Logo 15 |

Get your certificate of subscription to

16 |

Padhega India

18 |
19 |
20 | 21 | 22 | 24 | 25 | 26 | 27 |
28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const userName = document.getElementById("name"); 2 | const submitBtn = document.getElementById("submitBtn"); 3 | 4 | const { PDFDocument, rgb, degrees } = PDFLib; 5 | 6 | 7 | const capitalize = (str, lower = false) => 8 | (lower ? str.toLowerCase() : str).replace(/(?:^|\s|["'([{])+\S/g, (match) => 9 | match.toUpperCase() 10 | ); 11 | 12 | submitBtn.addEventListener("click", () => { 13 | const val = capitalize(userName.value); 14 | 15 | //check if the text is empty or not 16 | if (val.trim() !== "" && userName.checkValidity()) { 17 | // console.log(val); 18 | generatePDF(val); 19 | } else { 20 | userName.reportValidity(); 21 | } 22 | }); 23 | 24 | const generatePDF = async (name) => { 25 | const existingPdfBytes = await fetch("./cert.pdf").then((res) => 26 | res.arrayBuffer() 27 | ); 28 | 29 | // Load a PDFDocument from the existing PDF bytes 30 | const pdfDoc = await PDFDocument.load(existingPdfBytes); 31 | pdfDoc.registerFontkit(fontkit); 32 | 33 | //get font 34 | const fontBytes = await fetch("./Sanchez-Regular.ttf").then((res) => 35 | res.arrayBuffer() 36 | ); 37 | 38 | // Embed our custom font in the document 39 | const SanChezFont = await pdfDoc.embedFont(fontBytes); 40 | 41 | // Get the first page of the document 42 | const pages = pdfDoc.getPages(); 43 | const firstPage = pages[0]; 44 | 45 | // Draw a string of text diagonally across the first page 46 | firstPage.drawText(name, { 47 | x: 300, 48 | y: 270, 49 | size: 58, 50 | font: SanChezFont, 51 | color: rgb(0.2, 0.84, 0.67), 52 | }); 53 | 54 | // Serialize the PDFDocument to bytes (a Uint8Array) 55 | const pdfBytes = await pdfDoc.save(); 56 | console.log("Done creating"); 57 | 58 | // this was for creating uri and showing in iframe 59 | 60 | // const pdfDataUri = await pdfDoc.saveAsBase64({ dataUri: true }); 61 | // document.getElementById("pdf").src = pdfDataUri; 62 | 63 | var file = new File( 64 | [pdfBytes], 65 | "Padhega India Subscription Certificate.pdf", 66 | { 67 | type: "application/pdf;charset=utf-8", 68 | } 69 | ); 70 | saveAs(file); 71 | }; 72 | 73 | // init(); 74 | -------------------------------------------------------------------------------- /padhega india.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ShivamJoker/Cert-Generator/089050c0821f2126534e0f132ef8d036ad62c4f8/padhega india.png -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --dark-color: #212121; 3 | } 4 | 5 | body { 6 | font-family: system-ui; 7 | text-align: center; 8 | margin-top: 40px; 9 | font-size: 1.5em; 10 | } 11 | 12 | h4 { 13 | font-weight: normal; 14 | margin-bottom: 5px; 15 | } 16 | h1 { 17 | font-weight: normal; 18 | margin-top: 0; 19 | margin-bottom: 40px; 20 | font-size: 2em; 21 | } 22 | 23 | img { 24 | border-radius: 150%; 25 | height: 120px; 26 | } 27 | 28 | main { 29 | display: flex; 30 | flex-direction: column; 31 | align-items: center; 32 | } 33 | main * { 34 | margin-bottom: 12px; 35 | } 36 | 37 | input { 38 | text-align: center; 39 | font-size: large; 40 | outline: none; 41 | border: none; 42 | border-bottom: 1px solid black; 43 | text-transform: capitalize; 44 | } 45 | 46 | /* for styling the autocomplete style */ 47 | input:-webkit-autofill, 48 | input:-webkit-autofill:hover, 49 | input:-webkit-autofill:focus, 50 | input:-webkit-autofill:active { 51 | transition: background-color 5000s ease-in-out 0s; 52 | -webkit-text-fill-color: var(--dark-color) !important; 53 | } 54 | 55 | button { 56 | background: #8bc34a; 57 | color: #fff; 58 | border: none; 59 | font-size: 0.8em; 60 | padding: 5px 15px; 61 | margin: 20px; 62 | border-radius: 5px; 63 | cursor: pointer; 64 | } 65 | a { 66 | color: #6492f6; 67 | } 68 | 69 | /* dark mode css */ 70 | @media (prefers-color-scheme: dark) { 71 | body { 72 | background: var(--dark-color); 73 | color: #fff; 74 | } 75 | input, 76 | input:active { 77 | background: var(--dark-color); 78 | border-color: #fff; 79 | color: #fff; 80 | } 81 | 82 | /* for styling the autocomplete style */ 83 | input:-webkit-autofill, 84 | input:-webkit-autofill:hover, 85 | input:-webkit-autofill:focus, 86 | input:-webkit-autofill:active { 87 | transition: background-color 5000s ease-in-out 0s; 88 | -webkit-text-fill-color: #fff !important; 89 | } 90 | } 91 | --------------------------------------------------------------------------------