Skip to content

Instantly share code, notes, and snippets.

@stevemu
Last active November 4, 2024 22:42
Show Gist options
  • Save stevemu/279a6efb1c20cc16ae53881318255da0 to your computer and use it in GitHub Desktop.
Save stevemu/279a6efb1c20cc16ae53881318255da0 to your computer and use it in GitHub Desktop.
Avery5160PdfRenderer with pdf-lib
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;
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);
}
}
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);
}
}
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