Add PDF download functionality for stories with images
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { getStoryWithPagesBySlug } from "@/lib/db-actions";
|
||||
import { jsPDF } from "jspdf";
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const { searchParams } = new URL(request.url);
|
||||
const storySlug = searchParams.get("storySlug");
|
||||
|
||||
if (!storySlug) {
|
||||
return NextResponse.json({ error: "Story slug required" }, { status: 400 });
|
||||
}
|
||||
|
||||
const result = await getStoryWithPagesBySlug(storySlug);
|
||||
if (!result) {
|
||||
return NextResponse.json({ error: "Story not found" }, { status: 404 });
|
||||
}
|
||||
|
||||
const { story, pages } = result;
|
||||
|
||||
const images = pages
|
||||
.map((page: any) => page.generatedImageUrl)
|
||||
.filter((url: string) => url && url !== "/placeholder.svg");
|
||||
|
||||
if (images.length === 0) {
|
||||
return NextResponse.json({ error: "No images to download" }, { status: 400 });
|
||||
}
|
||||
|
||||
// Fetch all images server-side
|
||||
const imagePromises = images.map(async (url: string) => {
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch image: ${url}`);
|
||||
}
|
||||
const arrayBuffer = await response.arrayBuffer();
|
||||
return Buffer.from(arrayBuffer);
|
||||
});
|
||||
|
||||
const imageBuffers = await Promise.all(imagePromises);
|
||||
|
||||
// Create PDF
|
||||
const pdf = new jsPDF();
|
||||
|
||||
for (let i = 0; i < imageBuffers.length; i++) {
|
||||
if (i > 0) pdf.addPage();
|
||||
|
||||
const imgBuffer = imageBuffers[i];
|
||||
const imgData = `data:image/jpeg;base64,${imgBuffer.toString('base64')}`;
|
||||
|
||||
// For simplicity, assume images fit the page; in production you might want to scale
|
||||
pdf.addImage(imgData, 'JPEG', 10, 10, 190, 277); // A4 portrait size minus margins
|
||||
|
||||
// Add "Created by Make Comics" at the bottom
|
||||
pdf.setFontSize(8);
|
||||
pdf.text('Created by Make Comics', 105, 290, { align: 'center' });
|
||||
}
|
||||
|
||||
const pdfBuffer = Buffer.from(pdf.output('arraybuffer'));
|
||||
|
||||
// Return PDF as response
|
||||
return new NextResponse(pdfBuffer, {
|
||||
headers: {
|
||||
'Content-Type': 'application/pdf',
|
||||
'Content-Disposition': `attachment; filename="${story.title}.pdf"`,
|
||||
},
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error generating PDF:', error);
|
||||
return NextResponse.json(
|
||||
{ error: "Failed to generate PDF" },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -60,6 +60,7 @@ export function StoryEditorClient() {
|
||||
const [existingCharacterImages, setExistingCharacterImages] = useState<
|
||||
string[]
|
||||
>([]);
|
||||
const [isGeneratingPDF, setIsGeneratingPDF] = useState(false);
|
||||
const { toast } = useToast();
|
||||
const [apiKey, setApiKey] = useApiKey();
|
||||
|
||||
@@ -225,6 +226,47 @@ export function StoryEditorClient() {
|
||||
setShowApiModal(true);
|
||||
};
|
||||
|
||||
const downloadPDF = async () => {
|
||||
if (!story || pages.length === 0) return;
|
||||
|
||||
setIsGeneratingPDF(true);
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/download-pdf?storySlug=${story.slug}`);
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json();
|
||||
throw new Error(errorData.error || "Failed to generate PDF");
|
||||
}
|
||||
|
||||
// Create blob from response and trigger download
|
||||
const blob = await response.blob();
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `${story.title}.pdf`;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
window.URL.revokeObjectURL(url);
|
||||
|
||||
toast({
|
||||
title: "PDF downloaded",
|
||||
description: "Your comic has been downloaded as a PDF.",
|
||||
duration: 3000,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error generating PDF:', error);
|
||||
toast({
|
||||
title: "Failed to generate PDF",
|
||||
description: "An error occurred while generating the PDF.",
|
||||
variant: "destructive",
|
||||
duration: 4000,
|
||||
});
|
||||
} finally {
|
||||
setIsGeneratingPDF(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeletePage = (pageIndex: number) => {
|
||||
setPageToDelete(pageIndex);
|
||||
setShowDeleteDialog(true);
|
||||
@@ -372,6 +414,8 @@ export function StoryEditorClient() {
|
||||
<EditorToolbar
|
||||
title={story.title}
|
||||
onContinueStory={handleAddPage}
|
||||
onDownloadPDF={downloadPDF}
|
||||
isGeneratingPDF={isGeneratingPDF}
|
||||
isOwner={isOwner}
|
||||
/>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user