Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fixed the pdf flowchart issue #368

Merged
merged 3 commits into from
Jun 26, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 65 additions & 95 deletions src/app/workspace/[fileId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@ import { useConvex } from "convex/react";
import { api } from "../../../../convex/_generated/api";
import { FILE } from "../../dashboard/_components/FileList";
import Canvas from "../_components/Canvas";
import dynamic from 'next/dynamic';
import EditorJS, { OutputData } from "@editorjs/editorjs";
import { useKindeBrowserClient } from "@kinde-oss/kinde-auth-nextjs";
import { useRouter } from "next/navigation";

// Dynamic imports for server-side libraries
const jsPDFPromise = import("jspdf");
const excalidrawPromise = import("@excalidraw/excalidraw");
const jsPDFPromise = import('jspdf');
const excalidrawPromise = import('@excalidraw/excalidraw');

function Workspace({ params }: any) {
const [triggerSave, setTriggerSave] = useState(false);
Expand All @@ -22,49 +21,29 @@ function Workspace({ params }: any) {
const editorRef = useRef<EditorJS | null>(null);
const canvasRef = useRef<any>(null);

const { user, isAuthenticated, isLoading } = useKindeBrowserClient();
const router = useRouter();

console.log(user, isAuthenticated, isLoading);

useEffect(() => {
if (params.fileId && !isLoading) {
if (params.fileId) {
getFileData();
}
}, [params.fileId, isLoading]);
}, [params.fileId]);

const getFileData = async () => {
const result = await convex.query(api.files.getFileById, {
_id: params.fileId,
});

const teamInfo = await convex.query(api.teams.getTeamById, {
_id: result.teamId,
});

if (result.private) {
if (user) {
if (!teamInfo.teamMembers.includes(user.email)) {
router.push("/");
}
console.log(user);
} else {
router.push("/");
}
}
setFileData(result);
};

const saveAsPdf = async () => {
const { default: jsPDF } = await jsPDFPromise;
const { exportToSvg } = await excalidrawPromise;

const editorInstance = editorRef.current;
const canvasInstance = canvasRef.current;

if (editorInstance && canvasInstance) {
const pdf = new jsPDF("p", "mm", "a4");

// Extract text content from the editor
editorInstance.save().then((editorContent: OutputData) => {
const pageWidth = pdf.internal.pageSize.getWidth();
Expand All @@ -73,57 +52,50 @@ function Workspace({ params }: any) {
const textWidth = pageWidth - margin * 2;
const textHeight = pageHeight - margin * 2;
let y = margin;

editorContent.blocks.forEach((block: any) => {
let lines: any[] = [];

switch (block.type) {
case "paragraph":
lines = parseText(block.data.text);
break;
case "header":
pdf.setFontSize(16); // Set font size for header
lines = [{ text: block.data.text, style: "header" }];
lines = [{ text: block.data.text, style: 'header' }];
pdf.setFontSize(12); // Reset font size
break;
case "list":
lines = block.data.items.map((item: string) => ({
text: `• ${item}`,
style: "normal",
}));
lines = block.data.items.map((item: string) => ({ text: `• ${item}`, style: 'normal' }));
break;
// Add more cases if needed for different block types
default:
lines = [{ text: block.data.text, style: "normal" }];
lines = [{ text: block.data.text, style: 'normal' }];
}

lines.forEach((line: any) => {
if (y + 10 > textHeight) {
pdf.addPage();
y = margin;
}

switch (line.style) {
case "bold":
case 'bold':
pdf.setFont("helvetica", "bold");
break;
case "italic":
case 'italic':
pdf.setFont("helvetica", "italic");
break;
case "header":
case 'header':
pdf.setFont("helvetica", "bold");
const headerWidth =
(pdf.getStringUnitWidth(line.text) * 16) /
pdf.internal.scaleFactor;
const headerWidth = pdf.getStringUnitWidth(line.text) * 16 / pdf.internal.scaleFactor;
pdf.text(line.text, (pageWidth - headerWidth) / 2, y);
y += 10;
break;
default:
pdf.setFont("helvetica", "normal");
}

if (line.style !== "header") {
// Split text if it's too wide and handle separately

if (line.style !== 'header') {
const wrappedLines = pdf.splitTextToSize(line.text, textWidth);
wrappedLines.forEach((wrappedLine: string) => {
if (y + 10 > textHeight) {
Expand All @@ -135,87 +107,87 @@ function Workspace({ params }: any) {
});
}
});

// Reset font style and size after each block

pdf.setFont("helvetica", "normal");
pdf.setFontSize(12);
});

// Export flowchart as SVG from Excalidraw
const elements = canvasInstance.getSceneElements();
const appState = canvasInstance.getAppState();
const files = canvasInstance.getFiles();

exportToSvg({
elements: elements,
appState: { ...appState, exportBackground: false }, // No background
appState: { ...appState, exportBackground: false },
files: files,
}).then((svg: SVGSVGElement) => {
// Add heading for the flowchart
pdf.setFont("helvetica", "bold");
pdf.setFontSize(16); // Set font size for the heading
const headingText = "Flowchart";
const headingWidth =
pdf.getStringUnitWidth(headingText) * pdf.internal.scaleFactor;
const headingX = (pageWidth - headingWidth) / 2;
pdf.text(headingText, headingX, y + 10);
pdf.setFontSize(12); // Reset font size
pdf.setFont("helvetica", "normal");
y += 20; // Adjust y position to avoid overlap with the heading

// Convert SVG to PNG using the Canvas API
const svgData = new XMLSerializer().serializeToString(svg);
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d");
const img = new Image();

img.onload = () => {
if (context) {
canvas.width = img.width;
canvas.height = img.height;
context.drawImage(img, 0, 0);

const imgData = canvas.toDataURL("image/png");
const imgProps = pdf.getImageProperties(imgData);
const imgHeight = (imgProps.height * pageWidth) / imgProps.width;

// Add canvas image just below the heading
pdf.addImage(
imgData,
"PNG",
margin,
y,
pageWidth - margin * 2,
imgHeight
);
y += imgHeight;

// Save the PDF
let imgWidth = pageWidth - margin * 2;
let imgHeight = (imgProps.height * imgWidth) / imgProps.width;

// Check if the image height exceeds the remaining page height
if (y + imgHeight + 20 > pageHeight - margin) { // 20 for the heading space
pdf.addPage();
y = margin;
}

// Add heading for the flowchart
pdf.setFont("helvetica", "bold");
pdf.setFontSize(16); // Set font size for the heading
const headingText = "Flowchart";
const headingWidth = pdf.getStringUnitWidth(headingText) * 16 / pdf.internal.scaleFactor;
pdf.text(headingText, (pageWidth - headingWidth) / 2, y);
pdf.setFontSize(12); // Reset font size
pdf.setFont("helvetica", "normal");
y += 20; // Adjust y position to avoid overlap with the heading

// Check if the image height exceeds the page height and scale it down if necessary
if (imgHeight > pageHeight - margin * 2) {
const scaleFactor = (pageHeight - margin * 2) / imgHeight;
imgHeight *= scaleFactor;
imgWidth *= scaleFactor;
}

pdf.addImage(imgData, "PNG", margin, y, imgWidth, imgHeight, undefined, "FAST");

pdf.save("document.pdf");
} else {
console.error("Failed to get canvas context");
}
};

img.src = `data:image/svg+xml;base64,${btoa(svgData)}`;
});
});
} else {
console.error("Unable to find the content to save as PDF");
}
};
};

const parseText = (text: string) => {
const lines: any[] = [];
const parser = new DOMParser();
const parsedHtml = parser.parseFromString(text, "text/html");
const parsedHtml = parser.parseFromString(text, 'text/html');
parsedHtml.body.childNodes.forEach((node: ChildNode) => {
if (node.nodeType === Node.TEXT_NODE) {
lines.push({ text: node.textContent, style: "normal" });
} else if (node.nodeName === "B") {
lines.push({ text: node.textContent, style: "bold" });
} else if (node.nodeName === "I") {
lines.push({ text: node.textContent, style: "italic" });
lines.push({ text: node.textContent, style: 'normal' });
} else if (node.nodeName === 'B') {
lines.push({ text: node.textContent, style: 'bold' });
} else if (node.nodeName === 'I') {
lines.push({ text: node.textContent, style: 'italic' });
}
});
return lines;
Expand All @@ -231,9 +203,7 @@ function Workspace({ params }: any) {
onSaveAsPdf={saveAsPdf}
/>

{!isLoading && <div
className={`grid grid-cols-1 ${fullScreen ? "" : "md:grid-cols-2"} overflow-x-none`}
>
<div className={`grid grid-cols-1 ${fullScreen ? "" : "md:grid-cols-2"} overflow-x-none`}>
<div className={`${fullScreen ? "hidden" : "block"}`}>
<Editor
ref={editorRef as MutableRefObject<EditorJS | null>}
Expand All @@ -250,7 +220,7 @@ function Workspace({ params }: any) {
fileData={fileData}
/>
</div>
</div>}
</div>
</div>
);
}
Expand Down
Loading