mirror of https://github.com/buster-so/buster.git
update pdf download
This commit is contained in:
parent
bcc5566f27
commit
b7e0862042
|
@ -37,6 +37,7 @@ const createCspHeader = (isEmbed = false) => {
|
|||
(() => {
|
||||
const connectSources = [
|
||||
"'self'",
|
||||
'data:', // Allow data URLs for PDF exports and other data URI downloads
|
||||
localDomains,
|
||||
'https://*.vercel.app',
|
||||
'https://*.supabase.co',
|
||||
|
|
|
@ -19,12 +19,14 @@ import {
|
|||
|
||||
import { EditorStatic } from './EditorStatic';
|
||||
import { ToolbarButton } from '@/components/ui/toolbar/Toolbar';
|
||||
import { useBusterNotifications } from '@/context/BusterNotifications';
|
||||
|
||||
const siteUrl = 'https://platejs.org';
|
||||
|
||||
export function ExportToolbarButton({ children, ...props }: DropdownMenuProps) {
|
||||
const editor = useEditorRef();
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const { openErrorMessage, openInfoMessage } = useBusterNotifications();
|
||||
|
||||
const getCanvas = async () => {
|
||||
const { default: html2canvas } = await import('html2canvas-pro');
|
||||
|
@ -69,80 +71,121 @@ export function ExportToolbarButton({ children, ...props }: DropdownMenuProps) {
|
|||
};
|
||||
|
||||
const exportToPdf = async () => {
|
||||
const canvas = await getCanvas();
|
||||
try {
|
||||
const canvas = await getCanvas();
|
||||
const PDFLib = await import('pdf-lib');
|
||||
const pdfDoc = await PDFLib.PDFDocument.create();
|
||||
|
||||
const PDFLib = await import('pdf-lib');
|
||||
const pdfDoc = await PDFLib.PDFDocument.create();
|
||||
const page = pdfDoc.addPage([canvas.width, canvas.height]);
|
||||
const imageEmbed = await pdfDoc.embedPng(canvas.toDataURL('PNG'));
|
||||
const { height, width } = imageEmbed.scale(1);
|
||||
page.drawImage(imageEmbed, {
|
||||
height,
|
||||
width,
|
||||
x: 0,
|
||||
y: 0
|
||||
});
|
||||
const pdfBase64 = await pdfDoc.saveAsBase64({ dataUri: true });
|
||||
// Standard A4 dimensions in points (595 x 842)
|
||||
const standardWidth = 595;
|
||||
const standardHeight = 842;
|
||||
|
||||
await downloadFile(pdfBase64, 'plate.pdf');
|
||||
// Calculate scaling to fit content within standard width with margins
|
||||
const margin = 40; // 40 points margin on each side
|
||||
const availableWidth = standardWidth - margin * 2;
|
||||
const scaleFactor = Math.min(availableWidth / canvas.width, 1); // Don't scale up, only down
|
||||
|
||||
const scaledWidth = canvas.width * scaleFactor;
|
||||
const scaledHeight = canvas.height * scaleFactor;
|
||||
|
||||
// Calculate required page height based on scaled content
|
||||
const requiredHeight = Math.max(standardHeight, scaledHeight + margin * 2);
|
||||
|
||||
const page = pdfDoc.addPage([standardWidth, requiredHeight]);
|
||||
const imageEmbed = await pdfDoc.embedPng(canvas.toDataURL('PNG'));
|
||||
|
||||
// Center the content horizontally within the standard width
|
||||
const xPosition = (standardWidth - scaledWidth) / 2;
|
||||
|
||||
page.drawImage(imageEmbed, {
|
||||
height: scaledHeight,
|
||||
width: scaledWidth,
|
||||
x: xPosition,
|
||||
y: requiredHeight - scaledHeight - margin // Position from top with margin
|
||||
});
|
||||
|
||||
const pdfBase64 = await pdfDoc.saveAsBase64({ dataUri: true });
|
||||
|
||||
await downloadFile(pdfBase64, 'plate.pdf');
|
||||
openInfoMessage('PDF exported successfully');
|
||||
} catch (error) {
|
||||
openErrorMessage('Failed to export PDF');
|
||||
}
|
||||
};
|
||||
|
||||
const exportToImage = async () => {
|
||||
const canvas = await getCanvas();
|
||||
await downloadFile(canvas.toDataURL('image/png'), 'plate.png');
|
||||
try {
|
||||
const canvas = await getCanvas();
|
||||
await downloadFile(canvas.toDataURL('image/png'), 'plate.png');
|
||||
openInfoMessage('Image exported successfully');
|
||||
} catch (error) {
|
||||
openErrorMessage('Failed to export image');
|
||||
}
|
||||
};
|
||||
|
||||
const exportToHtml = async () => {
|
||||
const BaseEditorKit = await import('../editor-base-kit').then((module) => module.BaseEditorKit);
|
||||
try {
|
||||
const BaseEditorKit = await import('../editor-base-kit').then(
|
||||
(module) => module.BaseEditorKit
|
||||
);
|
||||
|
||||
const editorStatic = createSlateEditor({
|
||||
plugins: BaseEditorKit,
|
||||
value: editor.children
|
||||
});
|
||||
const editorStatic = createSlateEditor({
|
||||
plugins: BaseEditorKit,
|
||||
value: editor.children
|
||||
});
|
||||
|
||||
const editorHtml = await serializeHtml(editorStatic, {
|
||||
editorComponent: EditorStatic,
|
||||
props: { style: { padding: '0 calc(50% - 350px)', paddingBottom: '' } }
|
||||
});
|
||||
const editorHtml = await serializeHtml(editorStatic, {
|
||||
editorComponent: EditorStatic,
|
||||
props: { style: { padding: '0 calc(50% - 350px)', paddingBottom: '' } }
|
||||
});
|
||||
|
||||
const tailwindCss = `<link rel="stylesheet" href="${siteUrl}/tailwind.css">`;
|
||||
const katexCss = `<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.18/dist/katex.css" integrity="sha384-9PvLvaiSKCPkFKB1ZsEoTjgnJn+O3KvEwtsz37/XrkYft3DTk2gHdYvd9oWgW3tV" crossorigin="anonymous">`;
|
||||
const tailwindCss = `<link rel="stylesheet" href="${siteUrl}/tailwind.css">`;
|
||||
const katexCss = `<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.18/dist/katex.css" integrity="sha384-9PvLvaiSKCPkFKB1ZsEoTjgnJn+O3KvEwtsz37/XrkYft3DTk2gHdYvd9oWgW3tV" crossorigin="anonymous">`;
|
||||
|
||||
const html = `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="color-scheme" content="light dark" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Inter:wght@400..700&family=JetBrains+Mono:wght@400..700&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
${tailwindCss}
|
||||
${katexCss}
|
||||
<style>
|
||||
:root {
|
||||
--font-sans: 'Inter', 'Inter Fallback';
|
||||
--font-mono: 'JetBrains Mono', 'JetBrains Mono Fallback';
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
${editorHtml}
|
||||
</body>
|
||||
</html>`;
|
||||
const html = `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="color-scheme" content="light dark" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Inter:wght@400..700&family=JetBrains+Mono:wght@400..700&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
${tailwindCss}
|
||||
${katexCss}
|
||||
<style>
|
||||
:root {
|
||||
--font-sans: 'Inter', 'Inter Fallback';
|
||||
--font-mono: 'JetBrains Mono', 'JetBrains Mono Fallback';
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
${editorHtml}
|
||||
</body>
|
||||
</html>`;
|
||||
|
||||
const url = `data:text/html;charset=utf-8,${encodeURIComponent(html)}`;
|
||||
const url = `data:text/html;charset=utf-8,${encodeURIComponent(html)}`;
|
||||
|
||||
await downloadFile(url, 'plate.html');
|
||||
await downloadFile(url, 'plate.html');
|
||||
openInfoMessage('HTML exported successfully');
|
||||
} catch (error) {
|
||||
openErrorMessage('Failed to export HTML');
|
||||
}
|
||||
};
|
||||
|
||||
const exportToMarkdown = async () => {
|
||||
const md = editor.getApi(MarkdownPlugin).markdown.serialize();
|
||||
const url = `data:text/markdown;charset=utf-8,${encodeURIComponent(md)}`;
|
||||
await downloadFile(url, 'plate.md');
|
||||
try {
|
||||
const md = editor.getApi(MarkdownPlugin).markdown.serialize();
|
||||
const url = `data:text/markdown;charset=utf-8,${encodeURIComponent(md)}`;
|
||||
await downloadFile(url, 'plate.md');
|
||||
openInfoMessage('Markdown exported successfully');
|
||||
} catch (error) {
|
||||
openErrorMessage('Failed to export Markdown');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
|
@ -12,7 +12,8 @@ import {
|
|||
ArrowUpToLine,
|
||||
TextColor2,
|
||||
BucketPaint,
|
||||
TextHighlight2
|
||||
TextHighlight2,
|
||||
ArrowDownFromLine
|
||||
} from '@/components/ui/icons';
|
||||
import { KEYS } from 'platejs';
|
||||
import { useEditorReadOnly } from 'platejs/react';
|
||||
|
@ -66,7 +67,7 @@ export const FixedToolbarButtons = React.memo(() => {
|
|||
|
||||
<ToolbarGroup>
|
||||
<ExportToolbarButton>
|
||||
<ArrowUpToLine />
|
||||
<ArrowDownFromLine />
|
||||
</ExportToolbarButton>
|
||||
|
||||
<ImportToolbarButton />
|
||||
|
|
Loading…
Reference in New Issue