@snakeroads-soft/pdf-editor
v1.0.0
Published
Framework-agnostic visual PDF report designer and jsPDF compiler in pure TypeScript with Angular, React, Vue, and Vanilla JS support.
Readme
@snakeroads-soft/pdf-editor 🚀
Framework-Agnostic, High-Performance PDF Studio & Editor for TypeScript, Angular, React, Vue, Svelte, and Vanilla JS.
A modern, glassmorphism-styled, plug-and-play PDF designer and compilation engine built with 100% pure TypeScript and jsPDF.
✨ Features
- 🌐 Framework-Agnostic: Zero framework lock-in. Works as a Web Component
<pdf-studio>, a JavaScript classnew PdfStudio(), or an Angular component/service. - 📄 Multi-Format & Multi-Orientation: Native A4, Letter, Legal, A3, A5 with portrait and landscape modes.
- 📊 Decoupled Dynamic Data Tables & Lists:
- Pass runtime JSON arrays (
datasets: { estudiantes: [...], productos: [...] }) from code without modifying template design. - One-click auto-mapping (
🪄 Auto-Mapear Columnas) of object keys to table columns with balanced 100% widths. #indexvirtual field support for automatic 1-based row numbering.- Interactive table column
% widthdrag-resizing and inspector sliders. - Column visibility toggles and alignment controls.
- Pass runtime JSON arrays (
- 🌊 Smart Dynamic Flow & Multi-Page Interference Engine:
- Automatically shifts elements positioned below multi-page tables or long markdown blocks down to avoid overlaps.
- Multi-column spatial interference detection (only displaces elements sharing horizontal X-coordinate boundaries).
- 📝 Multi-Page Markdown Auto-Breaking:
- Full GitHub-flavored markdown with bold, headers, lists, code, and tables.
- Auto-Split Across Pages: Automatically splits long markdown text across multiple PDF pages when it exceeds page boundaries.
- 🧩 Dynamic Variable Engine:
- Auto-discovery of
{{variable_name}}and{{dataset.length}}tokens across text, tables, and lists. - Variable chip injector in toolbox & inspector.
- Live preview and batch compilation with dynamic runtime data.
- Auto-discovery of
- 📐 Millimeter Precision Workspace:
- Real-time horizontal and vertical mm rulers.
- 8-direction resize handles and 360° rotation handle.
- Grid snap system with configurable millimeter step.
- Backspace-protected deletion (only
Deletekey deletes elements to prevent accidental removal while typing).
- ☁️ Storage & Cloud Ready: Local Storage, REST API, Azure Blob Storage, and AWS S3 integration.
- 🎨 Built-in Business Templates: Modern Invoices, Achievement Certificates, Reports, and Shipping Labels.
📦 Installation
npm install @snakeroads-soft/pdf-editor jspdf jspdf-autotable marked dompurify🅰️ Angular Integration
1. In Standalone Components (Angular 14+)
import { Component, ElementRef, ViewChild, AfterViewInit, OnDestroy } from '@angular/core';
import { PdfStudio, DocumentSchema } from '@snakeroads-soft/pdf-editor';
import '@snakeroads-soft/pdf-editor/style.css';
@Component({
selector: 'app-pdf-designer',
standalone: true,
template: `
<div class="editor-container" #editorContainer style="width: 100%; height: 90vh;"></div>
`
})
export class PdfDesignerComponent implements AfterViewInit, OnDestroy {
@ViewChild('editorContainer', { static: true }) editorContainer!: ElementRef<HTMLElement>;
private studio!: PdfStudio;
ngAfterViewInit(): void {
this.studio = new PdfStudio({
container: this.editorContainer.nativeElement,
variables: {
company_name: 'Acme Corp',
invoice_total: '$1,450.00'
},
onChange: (schema: DocumentSchema) => {
console.log('Schema updated:', schema);
},
onSave: (schema: DocumentSchema) => {
console.log('Document saved:', schema);
}
});
}
exportPdf(): void {
this.studio.downloadPdf('my-document.pdf');
}
ngOnDestroy(): void {
this.studio?.destroy();
}
}2. Using the Web Component <pdf-studio> in Angular
Add CUSTOM_ELEMENTS_SCHEMA to your component or module:
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import '@snakeroads-soft/pdf-editor';
import '@snakeroads-soft/pdf-editor/style.css';
@Component({
selector: 'app-pdf-page',
standalone: true,
schemas: [CUSTOM_ELEMENTS_SCHEMA],
template: `
<pdf-studio
style="width: 100%; height: 100vh; display: block;"
(schemaChange)="onSchemaChange($event)"
(save)="onSave($event)"
></pdf-studio>
`
})
export class PdfPageComponent {
onSchemaChange(event: any) {
console.log('Schema:', event.detail);
}
onSave(event: any) {
console.log('Save:', event.detail);
}
}3. Headless PDF Generation in Angular (Without UI)
Use SnakeroadsPdfService or JsPdfEngine directly to render PDFs in background services:
import { Injectable } from '@angular/core';
import { SnakeroadsPdfService, DocumentSchema } from '@snakeroads-soft/pdf-editor';
@Injectable({ providedIn: 'root' })
export class InvoiceService {
private pdfService = new SnakeroadsPdfService();
async downloadInvoice(schema: DocumentSchema, invoiceData: any) {
await this.pdfService.downloadPdf(schema, invoiceData, 'invoice.pdf');
}
async getInvoiceBlob(schema: DocumentSchema, invoiceData: any): Promise<Blob> {
return await this.pdfService.generatePdf(schema, invoiceData);
}
}⚛️ React Integration
import React, { useEffect, useRef } from 'react';
import { PdfStudio } from '@snakeroads-soft/pdf-editor';
import '@snakeroads-soft/pdf-editor/style.css';
export const PdfEditor: React.FC = () => {
const containerRef = useRef<HTMLDivElement>(null);
const studioRef = useRef<PdfStudio | null>(null);
useEffect(() => {
if (containerRef.current) {
studioRef.current = new PdfStudio({
container: containerRef.current,
onChange: (schema) => console.log('Schema:', schema),
});
}
return () => {
studioRef.current?.destroy();
};
}, []);
return <div ref={containerRef} style={{ width: '100%', height: '100vh' }} />;
};🟢 Vue 3 Integration
<template>
<div ref="editorContainer" style="width: 100%; height: 100vh;"></div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue';
import { PdfStudio } from '@snakeroads-soft/pdf-editor';
import '@snakeroads-soft/pdf-editor/style.css';
const editorContainer = ref<HTMLElement | null>(null);
let studio: PdfStudio | null = null;
onMounted(() => {
if (editorContainer.value) {
studio = new PdfStudio({
container: editorContainer.value,
onChange: (schema) => console.log('Schema:', schema),
});
}
});
onUnmounted(() => {
studio?.destroy();
});
</script>🍦 Vanilla TypeScript / JavaScript
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="stylesheet" href="node_modules/@snakeroads-soft/pdf-editor/dist/style.css" />
</head>
<body>
<div id="pdf-editor" style="width: 100vw; height: 100vh;"></div>
<script type="module">
import { PdfStudio } from './node_modules/@snakeroads-soft/pdf-editor/dist/index.mjs';
const studio = new PdfStudio({
container: '#pdf-editor',
variables: {
user_name: 'Arturo Schloss',
project: 'Cloud Infrastructure'
}
});
</script>
</body>
</html>🛠️ API Reference
new PdfStudio(options)
| Option | Type | Description |
| :--- | :--- | :--- |
| container | HTMLElement \| string | DOM element or CSS selector to mount the studio into. |
| schema | DocumentSchema (optional) | Initial document schema to load. |
| variables | Record<string, any> (optional) | Key-value dictionary for {{variable}} interpolation. |
| datasets | Record<string, any[]> (optional) | Named collections of dynamic JSON objects for tables and lists. |
| storageConfig | StorageConfig (optional) | Storage provider config (local, rest, azure-blob, s3). |
| onChange | (schema: DocumentSchema) => void | Triggered on every element change or layout modification. |
| onSave | (schema: DocumentSchema) => void | Triggered when user clicks Save. |
| onPdfGenerated | (blob: Blob) => void | Triggered when a PDF is compiled. |
Instance Methods
studio.getSchema(): DocumentSchemastudio.loadSchema(schema: DocumentSchema): voidstudio.setVariables(vars: Record<string, string>): voidstudio.getVariables(): Record<string, string>studio.setDatasets(datasets: Record<string, any[]>): voidstudio.setDataset(name: string, data: any[]): voidstudio.getDatasets(): Record<string, any[]>studio.exportPdfBlob(options?: GeneratePdfOptions): Promise<Blob>studio.downloadPdf(filename?: string, options?: GeneratePdfOptions): Promise<void>studio.save(): Promise<{ id: string; success: boolean }>studio.undo(): booleanstudio.redo(): booleanstudio.destroy(): void
Headless Engine (JspdfEngine)
import { JspdfEngine } from '@snakeroads-soft/pdf-editor';
const result = JspdfEngine.generatePdf(schema, {
variables: { empresa: 'Snakeroads' },
datasets: { estudiantes: dataArray }
});
console.log(result.blobUrl);📄 License
MIT © Snakeroads Technologies
