File size: 14,081 Bytes
2cdfc6e
 
f44cf45
 
2cdfc6e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f44cf45
2cdfc6e
f44cf45
 
 
 
2cdfc6e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f44cf45
2cdfc6e
 
 
 
 
 
 
 
 
 
 
 
 
f44cf45
2cdfc6e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f44cf45
 
2cdfc6e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f44cf45
2cdfc6e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f44cf45
 
2cdfc6e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f44cf45
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
import { jsPDF } from 'jspdf';
import type { ComicSeries, ComicIteration } from '../types';

export const createMangaPdf = (images: string[], title: string): void => {
    console.log('createMangaPdf called with:', { imageCount: images.length, title });
    
    if (images.length === 0) {
        console.error('No images provided for PDF creation');
        return;
    }
    
    // Create PDF with first image dimensions to maintain aspect ratio
    let doc: jsPDF | null = null;

    images.forEach((imgData, index) => {
        if (index === 0) {
            // Use jsPDF's built-in method to get image properties
            try {
                const imgProps = (doc as any)?.getImageProperties ? 
                    (doc as any).getImageProperties(`data:image/jpeg;base64,${imgData}`) : 
                    null;
                
                // If we can't get properties, create a temporary jsPDF instance to get them
                if (!imgProps) {
                    const tempDoc = new jsPDF();
                    const tempImgProps = (tempDoc as any).getImageProperties(`data:image/jpeg;base64,${imgData}`);
                    var aspectRatio = tempImgProps.width / tempImgProps.height;
                    console.log('Image dimensions from temp doc:', { width: tempImgProps.width, height: tempImgProps.height, aspectRatio });
                } else {
                    var aspectRatio = imgProps.width / imgProps.height;
                    console.log('Image dimensions:', { width: imgProps.width, height: imgProps.height, aspectRatio });
                }
            } catch (error) {
                // Fallback: assume standard comic book aspect ratio (3:4 portrait)
                console.warn('Could not determine image dimensions, using default comic aspect ratio:', error);
                var aspectRatio = 3 / 4; // Standard comic book ratio
            }
            
            // Scale to reasonable print size while maintaining aspect ratio
            // Use a base size and scale according to aspect ratio
            const baseSize = 200; // mm - base dimension for the larger side
            
            let pageWidth, pageHeight;
            if (aspectRatio > 1) {
                // Landscape/Wide format
                pageWidth = baseSize;
                pageHeight = baseSize / aspectRatio;
            } else {
                // Portrait/Tall format  
                pageHeight = baseSize;
                pageWidth = baseSize * aspectRatio;
            }
            
            console.log('PDF page dimensions:', { pageWidth, pageHeight, aspectRatio });
            
            doc = new jsPDF({
                orientation: aspectRatio > 1 ? 'landscape' : 'portrait',
                unit: 'mm',
                format: [pageWidth, pageHeight]
            });
            
            // Add first image filling entire page with no margins
            doc.addImage(`data:image/jpeg;base64,${imgData}`, 'JPEG', 0, 0, pageWidth, pageHeight);
        } else {
            // For subsequent pages, maintain the same format as the first page
            if (doc) {
                doc.addPage();
                const pageSize = doc.internal.pageSize;
                const currentWidth = pageSize.getWidth();
                const currentHeight = pageSize.getHeight();
                doc.addImage(`data:image/jpeg;base64,${imgData}`, 'JPEG', 0, 0, currentWidth, currentHeight);
            }
        }
    });

    const safeTitle = title.replace(/[^a-z0-9]/gi, '_').toLowerCase();
    const filename = `${safeTitle}_manga.pdf`;
    console.log('Saving PDF with filename:', filename);
    
    try {
        doc.save(filename);
        console.log('PDF save command executed successfully');
    } catch (error) {
        console.error('Error saving PDF:', error);
    }
};

/**
 * Creates a comprehensive PDF from a complete comic series with multiple iterations
 * Uses the aspect ratio of the first image in the series
 */
export const createComicSeriesPdf = (comicSeries: ComicSeries): void => {
    console.log('createComicSeriesPdf called with:', { iterations: comicSeries.iterations.length, seriesTitle: comicSeries.seriesTitle });
    
    // Get dimensions from the first image in the first iteration
    const firstIteration = comicSeries.iterations[0];
    if (!firstIteration || firstIteration.images.length === 0) {
        console.error('No images found in comic series');
        return;
    }
    
    const firstImageData = firstIteration.images[0];
    
    // Use jsPDF's built-in method to get image properties
    let aspectRatio;
    try {
        const tempDoc = new jsPDF();
        const imgProps = (tempDoc as any).getImageProperties(`data:image/jpeg;base64,${firstImageData}`);
        aspectRatio = imgProps.width / imgProps.height;
        console.log('Series image dimensions:', { width: imgProps.width, height: imgProps.height, aspectRatio });
    } catch (error) {
        console.warn('Could not determine series image dimensions, using default comic aspect ratio:', error);
        aspectRatio = 3 / 4; // Standard comic book ratio
    }
    
    // Use same sizing logic as single comic PDF
    const baseSize = 200;
    let pdfWidth, pdfHeight;
    if (aspectRatio > 1) {
        pdfWidth = baseSize;
        pdfHeight = baseSize / aspectRatio;
    } else {
        pdfHeight = baseSize;
        pdfWidth = baseSize * aspectRatio;
    }
    
    console.log('Series PDF dimensions:', { pdfWidth, pdfHeight, aspectRatio });
    
    const doc = new jsPDF({
        orientation: aspectRatio > 1 ? 'landscape' : 'portrait',
        unit: 'mm',
        format: [pdfWidth, pdfHeight]
    });

    let isFirstPage = true;
    let totalPages = 0;

    // Add title page for the series
    if (isFirstPage) {
        // Create a series title page
        doc.setFillColor(20, 20, 30);
        doc.rect(0, 0, pdfWidth, pdfHeight, 'F');
        
        // Title
        doc.setTextColor(255, 255, 255);
        doc.setFontSize(24);
        doc.setFont('helvetica', 'bold');
        const titleLines = doc.splitTextToSize(comicSeries.seriesTitle, pdfWidth - 20);
        const titleY = pdfHeight / 2 - 30;
        doc.text(titleLines, pdfWidth / 2, titleY, { align: 'center' });
        
        // Author
        doc.setFontSize(16);
        doc.setFont('helvetica', 'normal');
        doc.text(`By ${comicSeries.author}`, pdfWidth / 2, titleY + 20, { align: 'center' });
        
        // Series info
        doc.setFontSize(12);
        doc.text(`Complete Series • ${comicSeries.iterations.length} Chapters • ${comicSeries.totalPages} Pages`, 
                 pdfWidth / 2, titleY + 35, { align: 'center' });
        
        // Created date
        doc.setFontSize(10);
        doc.setTextColor(180, 180, 180);
        doc.text(`Created: ${comicSeries.createdAt.toLocaleDateString()}`, 
                 pdfWidth / 2, titleY + 50, { align: 'center' });
        
        // "Comic Genesis AI" attribution
        doc.text('Generated by Comic Genesis AI', 
                 pdfWidth / 2, pdfHeight - 20, { align: 'center' });
        
        isFirstPage = false;
        totalPages = 1;
    }

    // Add each iteration with separator pages
    comicSeries.iterations.forEach((iteration, iterationIndex) => {
        // Add chapter divider page (except for the first iteration)
        if (iterationIndex > 0) {
            doc.addPage();
            totalPages++;
            
            // Chapter divider page
            doc.setFillColor(30, 30, 40);
            doc.rect(0, 0, pdfWidth, pdfHeight, 'F');
            
            doc.setTextColor(255, 255, 255);
            doc.setFontSize(20);
            doc.setFont('helvetica', 'bold');
            
            const chapterTitle = `Chapter ${iterationIndex + 1}`;
            doc.text(chapterTitle, pdfWidth / 2, pdfHeight / 2 - 20, { align: 'center' });
            
            doc.setFontSize(16);
            doc.setFont('helvetica', 'normal');
            const iterationTitleLines = doc.splitTextToSize(iteration.title, pdfWidth - 20);
            doc.text(iterationTitleLines, pdfWidth / 2, pdfHeight / 2, { align: 'center' });
            
            doc.setFontSize(10);
            doc.setTextColor(180, 180, 180);
            doc.text(`${iteration.images.length} pages`, 
                     pdfWidth / 2, pdfHeight / 2 + 20, { align: 'center' });
        }

        // Add all images from this iteration
        iteration.images.forEach((imgData, imageIndex) => {
            if (!isFirstPage || imageIndex > 0) {
                doc.addPage();
                totalPages++;
            }
            
            try {
                // Fill entire page with image
                doc.addImage(`data:image/jpeg;base64,${imgData}`, 'JPEG', 0, 0, pdfWidth, pdfHeight);
                
                // Add page number footer
                doc.setFontSize(8);
                doc.setTextColor(120, 120, 120);
                doc.text(`${totalPages}`, pdfWidth - 10, pdfHeight - 5, { align: 'right' });
                
            } catch (error) {
                console.warn(`Failed to add image ${imageIndex} from iteration ${iterationIndex}:`, error);
                
                // Add error page
                doc.setFillColor(50, 50, 60);
                doc.rect(0, 0, pdfWidth, pdfHeight, 'F');
                doc.setTextColor(200, 200, 200);
                doc.setFontSize(12);
                doc.text('Image could not be loaded', pdfWidth / 2, pdfHeight / 2, { align: 'center' });
            }
            
            if (isFirstPage) {
                isFirstPage = false;
            }
        });
    });

    // Add final credits/summary page
    doc.addPage();
    totalPages++;
    
    doc.setFillColor(15, 15, 25);
    doc.rect(0, 0, pdfWidth, pdfHeight, 'F');
    
    doc.setTextColor(255, 255, 255);
    doc.setFontSize(18);
    doc.setFont('helvetica', 'bold');
    doc.text('The End', pdfWidth / 2, pdfHeight / 2 - 30, { align: 'center' });
    
    doc.setFontSize(12);
    doc.setFont('helvetica', 'normal');
    doc.text(`Complete Series: ${comicSeries.seriesTitle}`, pdfWidth / 2, pdfHeight / 2 - 10, { align: 'center' });
    doc.text(`By ${comicSeries.author}`, pdfWidth / 2, pdfHeight / 2 + 5, { align: 'center' });
    
    doc.setFontSize(10);
    doc.setTextColor(180, 180, 180);
    doc.text(`${comicSeries.iterations.length} chapters • ${totalPages} total pages`, 
             pdfWidth / 2, pdfHeight / 2 + 25, { align: 'center' });
    
    doc.text(`Generated: ${new Date().toLocaleDateString()}`, 
             pdfWidth / 2, pdfHeight / 2 + 40, { align: 'center' });
    
    doc.text('Powered by Comic Genesis AI & Google Gemini', 
             pdfWidth / 2, pdfHeight - 15, { align: 'center' });

    // Save the PDF
    const safeTitle = comicSeries.seriesTitle.replace(/[^a-z0-9]/gi, '_').toLowerCase();
    doc.save(`${safeTitle}_complete_series.pdf`);
};

/**
 * Creates a PDF from a single comic iteration with chapter formatting
 * Uses the aspect ratio of the first image in the iteration
 */
export const createIterationPdf = (iteration: ComicIteration, chapterNumber?: number): void => {
    if (iteration.images.length === 0) {
        console.error('No images found in iteration');
        return;
    }
    
    // Get dimensions from the first image using jsPDF
    const firstImageData = iteration.images[0];
    
    let aspectRatio;
    try {
        const tempDoc = new jsPDF();
        const imgProps = (tempDoc as any).getImageProperties(`data:image/jpeg;base64,${firstImageData}`);
        aspectRatio = imgProps.width / imgProps.height;
        console.log('Iteration image dimensions:', { width: imgProps.width, height: imgProps.height, aspectRatio });
    } catch (error) {
        console.warn('Could not determine iteration image dimensions, using default comic aspect ratio:', error);
        aspectRatio = 3 / 4; // Standard comic book ratio
    }
    
    // Use same sizing logic as other PDF functions
    const baseSize = 200;
    let pdfWidth, pdfHeight;
    if (aspectRatio > 1) {
        pdfWidth = baseSize;
        pdfHeight = baseSize / aspectRatio;
    } else {
        pdfHeight = baseSize;
        pdfWidth = baseSize * aspectRatio;
    }
    
    const doc = new jsPDF({
        orientation: aspectRatio > 1 ? 'landscape' : 'portrait',
        unit: 'mm',
        format: [pdfWidth, pdfHeight]
    });

    let isFirstPage = true;

    // Add chapter title page if chapter number is provided
    if (chapterNumber) {
        doc.setFillColor(20, 20, 30);
        doc.rect(0, 0, pdfWidth, pdfHeight, 'F');
        
        doc.setTextColor(255, 255, 255);
        doc.setFontSize(20);
        doc.setFont('helvetica', 'bold');
        doc.text(`Chapter ${chapterNumber}`, pdfWidth / 2, pdfHeight / 2 - 20, { align: 'center' });
        
        doc.setFontSize(16);
        doc.setFont('helvetica', 'normal');
        const titleLines = doc.splitTextToSize(iteration.title, pdfWidth - 20);
        doc.text(titleLines, pdfWidth / 2, pdfHeight / 2, { align: 'center' });
        
        doc.setFontSize(10);
        doc.text(`By ${iteration.author}`, pdfWidth / 2, pdfHeight / 2 + 20, { align: 'center' });
        
        isFirstPage = false;
    }

    // Add all images
    iteration.images.forEach((imgData, index) => {
        if (!isFirstPage || index > 0) {
            doc.addPage();
        }
        
        try {
            // Fill entire page with image
            doc.addImage(`data:image/jpeg;base64,${imgData}`, 'JPEG', 0, 0, pdfWidth, pdfHeight);
            
        } catch (error) {
            console.warn(`Failed to add image ${index}:`, error);
        }
        
        if (isFirstPage) {
            isFirstPage = false;
        }
    });

    const safeTitle = iteration.title.replace(/[^a-z0-9]/gi, '_').toLowerCase();
    const fileName = chapterNumber ? 
        `${safeTitle}_chapter_${chapterNumber}.pdf` : 
        `${safeTitle}.pdf`;
    
    doc.save(fileName);
};