npm i pdf-lib
app.module.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { FormsModule } from '@angular/forms'; import { AppComponent } from './app.component'; import { HelloComponent } from './hello.component'; @NgModule({ imports: [ BrowserModule, FormsModule ], declarations: [ AppComponent, HelloComponent ], bootstrap: [ AppComponent ] }) export class AppModule { } |
app.component.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
import { Component } from '@angular/core'; import { PDFDocument, StandardFonts, rgb } from 'pdf-lib'; @Component({ selector: 'my-app', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { userText: string = ''; selectedImages: File[] = []; async createPdf(event: Event) { event.preventDefault(); const pdfDoc = await PDFDocument.create(); const timesRomanFont = await pdfDoc.embedFont(StandardFonts.TimesRoman); const fontSize = 12; const pageMargin = 50; const lineHeight = fontSize * 1.5; // Adjust line height for better readability // Add a new page to start with let currentPage = pdfDoc.addPage(); const { width, height } = currentPage.getSize(); let yPosition = height - pageMargin; // Split user text into lines and pages if (this.userText) { const lines = await this.splitTextIntoLines(this.userText, width - 2 * pageMargin, fontSize, timesRomanFont); // Loop through lines, adding them to the page for (const line of lines) { if (yPosition - lineHeight < pageMargin) { currentPage = pdfDoc.addPage(); // Create new page if space is insufficient yPosition = height - pageMargin; } currentPage.drawText(line, { x: pageMargin, y: yPosition - fontSize, size: fontSize, font: timesRomanFont, color: rgb(0, 0, 0), }); yPosition -= lineHeight; // Move down for the next line } } // Add selected images to the PDF for (const imageFile of this.selectedImages) { const imageBytes = await imageFile.arrayBuffer(); const pdfImage = await this.embedImage(pdfDoc, imageFile.type, imageBytes); // Get the image's natural width and height const { width: imgWidth, height: imgHeight } = pdfImage; // Calculate the scaling factor to fit the image to the page width const scaleFactor = (width - 2 * pageMargin) / imgWidth; // Calculate the new height based on the scale factor, while maintaining the aspect ratio const imgScaledWidth = imgWidth * scaleFactor; const imgScaledHeight = imgHeight * scaleFactor; // If there's not enough space for the image, create a new page if (yPosition < imgScaledHeight + pageMargin) { currentPage = pdfDoc.addPage(); yPosition = height - pageMargin; } // Draw the image at the top-left corner of the page currentPage.drawImage(pdfImage, { x: pageMargin, y: yPosition - imgScaledHeight, width: imgScaledWidth, height: imgScaledHeight, }); yPosition -= imgScaledHeight + 20; // Adjust for the next image } // Save the PDF to a file const pdfBytes = await pdfDoc.save(); this.saveByteArray('GeneratedPDF.pdf', pdfBytes); } async splitTextIntoLines(text: string, maxWidth: number, fontSize: number, font: any): Promise<string[]> { const lines: string[] = []; const wordSpacing = 2; let currentLine = ''; let currentWidth = 0; const words = text.split(' '); for (const word of words) { const wordWidth = font.widthOfTextAtSize(word, fontSize); // If adding this word exceeds the max width, push the current line and reset if (currentWidth + wordWidth + wordSpacing > maxWidth) { lines.push(currentLine); currentLine = word; currentWidth = wordWidth; } else { if (currentLine) { currentLine += ' ' + word; currentWidth += wordWidth + wordSpacing; } else { currentLine = word; currentWidth = wordWidth; } } } if (currentLine) { lines.push(currentLine); } return lines; } onFileSelected(event: Event) { const input = event.target as HTMLInputElement; if (input.files) { this.selectedImages = Array.from(input.files); } } async embedImage(pdfDoc: PDFDocument, mimeType: string, imageBytes: ArrayBuffer) { if (mimeType.includes('jpeg') || mimeType.includes('jpg')) { return pdfDoc.embedJpg(imageBytes); } else if (mimeType.includes('png')) { return pdfDoc.embedPng(imageBytes); } else { throw new Error('Unsupported image format. Please use JPEG or PNG images.'); } } saveByteArray(reportName: string, byte: Uint8Array) { const blob = new Blob([byte], { type: 'application/pdf' }); const link = document.createElement('a'); link.href = window.URL.createObjectURL(blob); link.download = reportName; link.click(); } } |
app.component.html
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<!-- app.component.html --> <h1>PDF Generator</h1> <form (submit)="createPdf($event)"> <div> <label for="text">Enter your text:</label> <textarea id="text" [(ngModel)]="userText" name="text" rows="4" cols="50"></textarea> </div> <div> <label for="images">Select images:</label> <input type="file" id="images" (change)="onFileSelected($event)" multiple> </div> <button type="submit">Generate PDF</button> </form> |
FULL SOURCE CODE