Last active
November 4, 2024 22:42
-
-
Save stevemu/279a6efb1c20cc16ae53881318255da0 to your computer and use it in GitHub Desktop.
Avery5160PdfRenderer with pdf-lib
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { PdfRenderer, PdfRendererFileReader } from './PdfRenderer'; | |
export abstract class Avery5160PdfRenderer extends PdfRenderer { | |
constructor(reader: PdfRendererFileReader) { | |
super(reader); | |
} | |
public async runDrawLabels(labels: unknown) { | |
await this.init(); | |
await this.drawLabels(labels); | |
return this.getPdfBytes(); | |
} | |
protected abstract drawLabels(labels: unknown): Promise<void>; | |
// return bottom-left corner position of the label | |
protected getLabelPosition(index: number) { | |
return LABEL_POSITIONS[index]!; | |
} | |
protected drawDebugRectangle(index: number) { | |
const position = this.getLabelPosition(index); | |
this.drawRectangle(position.x, position.y, LABEL_WIDTH, LABEL_HEIGHT); | |
} | |
} | |
// bottom-left corner position of each label | |
// from from top left corner of the page | |
// to bottom right corner of the page | |
const LABEL_POSITIONS = [ | |
{ x: 16, y: 692 }, // row 0, column 0 | |
{ x: 214, y: 692 }, // row 0, column 1 | |
{ x: 412, y: 692 }, // row 0, column 2 | |
{ x: 16, y: 620 }, // row 1, column 0 | |
{ x: 214, y: 620 }, // row 1, column 1 | |
{ x: 412, y: 620 }, // row 1, column 2 | |
{ x: 16, y: 548 }, | |
{ x: 214, y: 548 }, | |
{ x: 412, y: 548 }, | |
{ x: 16, y: 476 }, | |
{ x: 214, y: 476 }, | |
{ x: 412, y: 476 }, | |
{ x: 16, y: 404 }, | |
{ x: 214, y: 404 }, | |
{ x: 412, y: 404 }, | |
{ x: 16, y: 332 }, | |
{ x: 214, y: 332 }, | |
{ x: 412, y: 332 }, | |
{ x: 16, y: 260 }, | |
{ x: 214, y: 260 }, | |
{ x: 412, y: 260 }, | |
{ x: 16, y: 188 }, | |
{ x: 214, y: 188 }, | |
{ x: 412, y: 188 }, | |
{ x: 16, y: 116 }, | |
{ x: 214, y: 116 }, | |
{ x: 412, y: 116 }, | |
{ x: 16, y: 44 }, | |
{ x: 214, y: 44 }, | |
{ x: 412, y: 44 }, | |
]; | |
// width and height of each label | |
const LABEL_WIDTH = 186; | |
const LABEL_HEIGHT = 72; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export class ChineseFontFileReader { | |
async readChineseFont() { | |
const res = await fetch( | |
'https://YOUR-S3.s3.amazonaws.com/NotoSansTC-Regular.ttf', | |
); | |
const buffer = await res.arrayBuffer(); | |
return Buffer.from(buffer); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { PdfRendererFileReader } from './PdfRenderer'; | |
import { ChineseFontFileReader } from './ChineseFontFileReader'; | |
export class EmptyPdfFileReader extends ChineseFontFileReader implements PdfRendererFileReader { | |
async readBasePDF(): Promise<Buffer> { | |
// How to generate the empty.pdf: | |
// Using Word, save a Letter size document to pdf | |
const res = await fetch('https://YOUR-S3.s3.amazonaws.com/empty.pdf'); | |
const buffer = await res.arrayBuffer(); | |
return Buffer.from(buffer); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { PDFDocument, PDFFont, PDFPage, rgb, StandardFonts } from 'pdf-lib'; | |
import fontkit from '@pdf-lib/fontkit'; | |
export interface PdfRendererFileReader { | |
readBasePDF(): Promise<Uint8Array>; | |
readChineseFont(): Promise<Uint8Array>; | |
} | |
export abstract class PdfRenderer { | |
private pdfDoc!: PDFDocument; | |
private page!: PDFPage; | |
private englishFont!: PDFFont; | |
private chineseFont!: PDFFont; | |
constructor(private reader: PdfRendererFileReader) {} | |
protected async init() { | |
const existingPdfBytes = await this.reader.readBasePDF(); | |
const pdfDoc = await PDFDocument.load(existingPdfBytes); | |
const pages = pdfDoc.getPages(); | |
const page0 = pages[0]; | |
this.pdfDoc = pdfDoc; | |
this.page = page0!; | |
this.englishFont = await this.pdfDoc.embedFont(StandardFonts.Helvetica); | |
await this.configureChineseFont(); | |
} | |
private async configureChineseFont() { | |
this.pdfDoc.registerFontkit(fontkit); | |
this.chineseFont = await this.pdfDoc.embedFont(await this.reader.readChineseFont()); | |
} | |
protected async getPdfBytes() { | |
return this.pdfDoc.save(); | |
} | |
protected drawRectangle(x: number, y: number, width: number, height: number) { | |
this.page.drawRectangle({ | |
x, | |
y, | |
width, | |
height, | |
color: rgb(1, 1, 1), | |
borderWidth: 0.5, | |
}); | |
} | |
protected drawText(x: number, y: number, text: string) { | |
if (containsChineseCharacters(text)) { | |
this.drawChineseText(x, y, text); | |
return; | |
} | |
this.drawEnglishAndNumberText(x, y, text); | |
} | |
protected drawEnglishAndNumberText(x: number, y: number, text: string) { | |
this.page.drawText(text, { | |
x, | |
y, | |
size: 10, | |
font: this.englishFont!, | |
color: rgb(0, 0, 0), // Black text color | |
}); | |
} | |
protected drawChineseText(x: number, y: number, text: string) { | |
this.page.drawText(text, { | |
x, | |
y, | |
size: 10, | |
font: this.chineseFont, | |
color: rgb(0, 0, 0), // Black text color | |
}); | |
} | |
protected async drawImage( | |
image: Uint8Array, | |
x: number, | |
y: number, | |
width: number, | |
height: number, | |
) { | |
const pngImage = await this.pdfDoc.embedPng(image); | |
this.page.drawImage(pngImage, { | |
x, | |
y, | |
width, | |
height, | |
}); | |
} | |
} | |
function containsChineseCharacters(str: string) { | |
const chineseCharPattern = /[\u4E00-\u9FFF]/; | |
return chineseCharPattern.test(str); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment