diff --git a/bun.lockb b/bun.lockb index 5bc1240..c2c51a9 100644 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index e513de4..4cfb6ee 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "@tiptap/pm": "^3.20.1", "@tiptap/react": "^3.20.1", "@tiptap/starter-kit": "^3.20.1", + "html2pdf.js": "^0.14.0", "lucide-react": "^0.511.0", "react": "^19.2.0", "react-dom": "^19.2.0", diff --git a/src/components/Toolbar.tsx b/src/components/Toolbar.tsx index 65cbd22..ed5e9eb 100644 --- a/src/components/Toolbar.tsx +++ b/src/components/Toolbar.tsx @@ -22,10 +22,12 @@ import { Link, ImageIcon, MinusIcon, + FileDown, } from "lucide-react"; import ColorPicker from "./ColorPicker"; import LinkPopover from "./LinkPopover"; import ImagePopover from "./ImagePopover"; +import { exportToPdf } from "../utils/exportToPdf"; const FONT_FAMILIES = [ "Arial", @@ -519,6 +521,16 @@ function Toolbar({ editor }: ToolbarProps) { > + + + + {/* Export to PDF */} + exportToPdf(editor)} + > + + ); diff --git a/src/utils/exportToPdf.ts b/src/utils/exportToPdf.ts new file mode 100644 index 0000000..c7eed8a --- /dev/null +++ b/src/utils/exportToPdf.ts @@ -0,0 +1,68 @@ +import type { Editor } from "@tiptap/react"; +import html2pdf from "html2pdf.js"; + +const EDITOR_STYLES = ` + .pdf-export-container { + font-family: Arial, sans-serif; + font-size: 11pt; + line-height: 1.5; + color: #000; + } + .pdf-export-container ul { + list-style-type: disc; + padding-left: 1.5em; + margin: 0.5em 0; + } + .pdf-export-container ol { + list-style-type: decimal; + padding-left: 1.5em; + margin: 0.5em 0; + } + .pdf-export-container ul ul { + list-style-type: circle; + } + .pdf-export-container ul ul ul { + list-style-type: square; + } + .pdf-export-container li { + margin: 0.2em 0; + } + .pdf-export-container a { + color: #1a73e8; + text-decoration: underline; + } + .pdf-export-container img { + max-width: 100%; + height: auto; + } + .pdf-export-container hr { + border: none; + border-top: 1px solid #dadce0; + margin: 1em 0; + } +`; + +export function exportToPdf(editor: Editor): void { + const editorHtml = editor.getHTML(); + + const wrapper = document.createElement("div"); + + const styleElement = document.createElement("style"); + styleElement.textContent = EDITOR_STYLES; + wrapper.appendChild(styleElement); + + const content = document.createElement("div"); + content.className = "pdf-export-container"; + content.innerHTML = editorHtml; + wrapper.appendChild(content); + + const options = { + margin: [0.5, 1, 0.5, 1] as [number, number, number, number], + filename: "document.pdf", + image: { type: "jpeg" as const, quality: 0.98 }, + html2canvas: { scale: 2, useCORS: true }, + jsPDF: { unit: "in", format: "letter", orientation: "portrait" as const }, + }; + + html2pdf().set(options).from(wrapper).save(); +}