PDF Template Builder - CKEditor to PDF with Vietnamese support, template engine with variables, loops, conditionals
npm install masax-drawpdf{{name}}, loops {{#each}}, conditionals {{#if}}
bash
npm
npm i masax-drawpdf
or from git
npm install github:masax/DrawPDF#main
`
Peer Dependencies:
- jspdf ^2.5.1
- jspdf-autotable ^3.8.1
---
π€ Font Configuration
$3
By default, DrawPDF uses Roboto font (Vietnamese support built-in) with helvetica as fallback.
$3
`javascript
import DrawPDF from "drawpdf";
const pdf = await DrawPDF.create("#editor", {
fonts: {
defaultFont: "MyCustomFont", // Primary font name
fallback: "helvetica", // Fallback if font not found
register: [
// Pre-converted font files (.js)
"/fonts/MyCustomFont-Regular.js",
"/fonts/MyCustomFont-Bold.js",
],
},
});
`
$3
`javascript
// Register font at runtime
await pdf.registerFont("/fonts/AnotherFont.js");
`
$3
1. Download TTF font from Font Squirrel
2. Convert using jsPDF Font Converter
3. Place the .js file in your project
4. Register via fonts.register array
---
π Browser Usage (UMD)
For direct usage in the browser without a bundler, use the standalone build which includes all dependencies (jspdf, jspdf-autotable).
`html
`
---
π Quick Start
$3
`javascript
import DrawPDF from "drawpdf";
// 1. Khα»i tαΊ‘o CKEditor
const pdf = await DrawPDF.create("#editor");
// 2. User soαΊ‘n thαΊ£o trong editor...
// 3. LαΊ₯y JSON Blueprint tα»« editor
const blueprint = pdf.getData();
// 4. Save blueprint Δα» dΓΉng sau
localStorage.setItem("myTemplate", JSON.stringify(blueprint));
// 5. Preview PDF
pdf.preview({ name: "Test", salary: 25000000 });
`
---
$3
ΔΓ’y lΓ use case phα» biαΊΏn nhαΊ₯t: BαΊ‘n ΔΓ£ cΓ³ file blueprint.json vΓ chα» cαΊ§n in ra PDF!
`javascript
import DrawPDF from "drawpdf";
// π CΓ‘ch 1: Load blueprint tα»« file/localStorage
const blueprint = JSON.parse(localStorage.getItem("myTemplate"));
// hoαΊ·c: const blueprint = await fetch('/templates/invoice.json').then(r => r.json());
// π¨οΈ In ngay! KhΓ΄ng cαΊ§n CKEditor
new DrawPDF().setData(blueprint).download("document.pdf", {
name: "Nguyα»
n VΔn An",
salary: 25000000,
items: [
{ name: "SαΊ£n phαΊ©m A", price: 100000 },
{ name: "SαΊ£n phαΊ©m B", price: 200000 },
],
});
`
HoαΊ·c dΓΉng Static Method (1 dΓ²ng):
`javascript
DrawPDF.downloadBlueprint(blueprint, "output.pdf", { name: "Test" });
`
CΓ‘c cΓ‘ch xuαΊ₯t khΓ‘c:
`javascript
const pdf = new DrawPDF().setData(blueprint);
// Render vΓ lαΊ₯y data URL (Δα» preview trong iframe)
const dataUrl = pdf.render(data);
document.getElementById("preview").src = dataUrl;
// LαΊ₯y Blob (Δα» upload lΓͺn server)
const blob = pdf.getBlob(data);
await fetch("/api/upload", { method: "POST", body: blob });
// Mα» preview trong tab mα»i
pdf.preview(data);
`
---
π API Reference - DrawPDF Class
$3
| Method | Description | Returns |
| -------------------------- | ---------------------------------------- | --------------------- |
| init(element, options) | Khα»i tαΊ‘o CKEditor vΓ o element | Promise |
| getData() | Parse HTML tα»« editor β JSON Blueprint | Object (Blueprint) |
| setData(blueprint) | Load blueprint cΓ³ sαΊ΅n Δα» render | DrawPDF (chainable) |
| render(data) | Render PDF tα»« blueprint | string (data URL) |
| download(filename, data) | TαΊ£i PDF xuα»ng | DrawPDF (chainable) |
| preview(data) | Mα» PDF trong tab mα»i | void |
| getBlob(data) | LαΊ₯y Blob Δα» upload | Blob |
| getBlueprint() | LαΊ₯y blueprint hiα»n tαΊ‘i (khΓ΄ng parse lαΊ‘i) | Object or null |
| exportJson() | XuαΊ₯t blueprint dαΊ‘ng JSON string | string |
| importJson(jsonString) | Import blueprint tα»« JSON string | DrawPDF (chainable) |
| registerFont(url) | ΔΔng kΓ½ font tΓΉy chα»nh | Promise |
| destroy() | Hα»§y editor instance | void |
$3
| Method | Description |
| ------------------------------------------------------ | -------------------------------------- |
| DrawPDF.create(element, options) | Factory method: new DrawPDF().init() |
| DrawPDF.parseHtml(html) | Parse HTML β Blueprint |
| DrawPDF.renderBlueprint(blueprint, data) | Render blueprint β data URL |
| DrawPDF.downloadBlueprint(blueprint, filename, data) | Download PDF ngay tα»« blueprint |
$3
`
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β π¨ DESIGN MODE (CΓ³ CKEditor) β
β βββββββββββββββββββββββββββββββ β
β DrawPDF.create('#editor') β
β β User soαΊ‘n thαΊ£o trong editor β
β β pdf.getData() β LαΊ₯y blueprint β
β β LΖ°u blueprint.json β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β blueprint.json
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β π¨οΈ PRINT MODE (KhΓ΄ng cαΊ§n CKEditor) β
β βββββββββββββββββββββββββββββββββ β
β new DrawPDF() β
β .setData(blueprint) β Load blueprint cΓ³ sαΊ΅n β
β .download('file.pdf', { β Truyα»n data vΓ o β
β name: 'Nguyα»
n VΔn An', β
β salary: 25000000 β
β }); β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
`
---
π Advanced API
$3
`javascript
import { CKEditorParser, PDFRenderer } from "drawpdf";
const parser = new CKEditorParser();
const renderer = new PDFRenderer();
const blueprint = parser.parse("Hello
");
renderer.render(blueprint, { name: "World" });
renderer.download("output.pdf");
`
$3
`javascript
import { CKEditorParser, PAGE, FONTS } from "drawpdf";
const parser = new CKEditorParser();
const blueprint = parser.parse(htmlString);
// Constants
console.log(PAGE.WIDTH); // 210 (A4 mm)
console.log(FONTS.DEFAULT_SIZE); // 12
`
$3
`javascript
import { PDFRenderer } from "drawpdf";
const renderer = new PDFRenderer();
// Render blueprint with data
renderer.render(blueprint, data);
// Output methods
renderer.download("file.pdf"); // Download file
renderer.getDataUrl(); // Get data URL for preview
renderer.getBlob(); // Get Blob for upload
renderer.preview(); // Open in new tab
`
$3
Low-level wrapper with 88+ methods for direct PDF manipulation.
`javascript
import { JsPdfService } from "drawpdf";
const pdf = new JsPdfService();
pdf.addTitle("Document Title");
pdf.addText("Hello World", null, null, { fontSize: 14 });
pdf.addTable(
["Col1", "Col2"],
[
["A", "B"],
["C", "D"],
],
);
pdf.addSpace(10);
pdf.addHorizontalLine();
pdf.addNewPage();
pdf.savePDF("output.pdf");
`
$3
Process template syntax independently.
`javascript
import { TemplateEngine } from "drawpdf";
const result = TemplateEngine.process("Hello {{name}}!", { name: "World" });
// "Hello World!"
`
---
π Template Syntax
$3
`html
{{name}}
{{employee.department.name}}
`
$3
`html
{{#each items}} - {{name}}: {{formatCurrency price}}{{br}} {{/each}}
`
Loop variables: {{@index}}, {{@first}}, {{@last}}
$3
`html
{{#if isActive}}Active{{else}}Inactive{{/if}} {{#if salary > 10000000}}High
salary{{/if}}
`
$3
| Helper | Example |
| ------------------------ | ----------------------------- |
| {{formatNumber num}} | 1000000 β 1.000.000 |
| {{formatCurrency num}} | 1000000 β 1.000.000Δ |
| {{formatDate date}} | 2026-01-29 β 29/01/2026 |
| {{uppercase text}} | hello β HELLO |
| {{capitalize text}} | hello world β Hello World |
$3
| Helper | Output |
| ----------- | ------------------- |
| {{today}} | 29/01/2026 |
| {{now}} | 29/01/2026, 13:45 |
| {{year}} | 2026 |
$3
| Tag | Effect |
| --------------- | --------------- |
| {{br}} | Line break |
| {{tab}} | Tab (4 spaces) |
| {{hr}} | Horizontal line |
| {{pageBreak}} | New page |
---
οΏ½ Blueprint JSON Structure
Blueprint lΓ Δα»nh dαΊ‘ng trung gian giα»―a HTML vΓ PDF. ΔΓ’y lΓ output cα»§a getData() vΓ input cα»§a setData().
$3
`json
{
"version": "1.0",
"pageSize": { "width": 210, "height": 297, "unit": "mm" },
"margins": { "top": 20, "bottom": 20, "left": 15, "right": 15 },
"pages": [
{
"pageNumber": 1,
"elements": [
{ "type": "richtext", "x": 15, "y": 20, "segments": [...] },
{ "type": "table", "x": 15, "y": 50, "rows": [...] }
]
}
],
"sourceHtml": "Original HTML...
",
"createdAt": "2026-01-30T07:00:00Z"
}
`
$3
| Type | Description | Key Properties |
| ---------- | ------------------------- | ---------------------------------------------- |
| richtext | ΔoαΊ‘n vΔn bαΊ£n cΓ³ Δα»nh dαΊ‘ng | segments[] (text, style), content, style |
| table | BαΊ£ng vα»i cells | rows[][], style, rowHeight |
| heading | TiΓͺu Δα» H1-H6 | level, content, style |
| list | Danh sΓ‘ch ul/ol | items[], listType |
| image | Hình ảnh | src, width, height |
| code | Code block | code, language |
$3
``json
{
"type": "richtext",
"x": 15,
"y": 20,
"width": 180,
"segments": [
{ "text": "Xin chΓ o ", "style": { "bold": false } },
{ "text": "{{name}}", "style": { "bold": true, "color": "#0000ff" } },
{ "text": "!", "style": { "bold": false } }
],
"style": {
"fontSize": 12,
"align": "left",
"lineHeight": 6.35
}
}
---
οΏ½π₯ Code Block Eval
Execute JavaScript directly in templates with // eval:
`javascript
// eval
const total = sum(data.items, 'price');
pdf.addText('Total: ' + formatCurrency(total));
pdf.addTable(
['Item', 'Price'],
data.items.map(i => [i.name, formatCurrency(i.price)])
);
``
Available in eval:
- pdf - JsPdfService instance
- data - Template data
- formatNumber(), formatCurrency(), sum(), count()
---
π Development
`bash
Clone
git clone https://github.com/masax/DrawPDF.git
cd DrawPDF/pdf-builder
Install
npm install
Dev server
npm run dev
Build library
npm run build
`
---
π Project Structure
`
draw-pdf/
βββ src/
β βββ index.js # Library entry point
β βββ DrawPDF.js # Main Class Wrapper
β βββ parser/
β β βββ CKEditorParser.js # HTML β JSON Blueprint
β β βββ RichTextTokenizer.js
β βββ renderer/
β β βββ PDFRenderer.js # Blueprint β PDF
β βββ service/
β β βββ jspdf-service/main.js # jsPDF wrapper (3000+ lines)
β βββ utils/
β βββ TemplateEngine.js # Template processing
βββ docs/ # Documentation & Examples
β βββ index.html # Main demo
βββ dist/ # Build output
β βββ drawpdf.js # ES Module
β βββ drawpdf.umd.cjs # CommonJS
βββ public/fonts/ # Vietnamese fonts
``