Page editor continued improvements

This commit is contained in:
Reece Browne 2025-08-22 15:39:30 +01:00
parent 383d227dea
commit abed82cc6b
6 changed files with 314 additions and 155 deletions

View File

@ -11,6 +11,7 @@ import { PDFDocument, PDFPage } from "../../types/pageEditor";
import { ProcessedFile as EnhancedProcessedFile } from "../../types/processing"; import { ProcessedFile as EnhancedProcessedFile } from "../../types/processing";
import { useUndoRedo } from "../../hooks/useUndoRedo"; import { useUndoRedo } from "../../hooks/useUndoRedo";
import { pdfExportService } from "../../services/pdfExportService"; import { pdfExportService } from "../../services/pdfExportService";
import { documentManipulationService } from "../../services/documentManipulationService";
import { enhancedPDFProcessingService } from "../../services/enhancedPDFProcessingService"; import { enhancedPDFProcessingService } from "../../services/enhancedPDFProcessingService";
import { fileProcessingService } from "../../services/fileProcessingService"; import { fileProcessingService } from "../../services/fileProcessingService";
import { pdfProcessingService } from "../../services/pdfProcessingService"; import { pdfProcessingService } from "../../services/pdfProcessingService";
@ -42,7 +43,7 @@ class RotatePageCommand extends DOMCommand {
} }
execute(): void { execute(): void {
// Find the page thumbnail and rotate it directly in the DOM // Only update DOM for immediate visual feedback
const pageElement = document.querySelector(`[data-page-id="${this.pageId}"]`); const pageElement = document.querySelector(`[data-page-id="${this.pageId}"]`);
if (pageElement) { if (pageElement) {
const img = pageElement.querySelector('img'); const img = pageElement.querySelector('img');
@ -55,6 +56,7 @@ class RotatePageCommand extends DOMCommand {
} }
undo(): void { undo(): void {
// Only update DOM
const pageElement = document.querySelector(`[data-page-id="${this.pageId}"]`); const pageElement = document.querySelector(`[data-page-id="${this.pageId}"]`);
if (pageElement) { if (pageElement) {
const img = pageElement.querySelector('img'); const img = pageElement.querySelector('img');
@ -128,6 +130,7 @@ export interface PageEditorProps {
showExportPreview: (selectedOnly: boolean) => void; showExportPreview: (selectedOnly: boolean) => void;
onExportSelected: () => void; onExportSelected: () => void;
onExportAll: () => void; onExportAll: () => void;
applyChanges: () => void;
exportLoading: boolean; exportLoading: boolean;
selectionMode: boolean; selectionMode: boolean;
selectedPages: number[]; selectedPages: number[];
@ -267,8 +270,8 @@ const PageEditor = ({
thumbnail: page.thumbnail || null, thumbnail: page.thumbnail || null,
rotation: page.rotation || 0, rotation: page.rotation || 0,
selected: false, selected: false,
splitBefore: page.splitBefore || false, splitAfter: page.splitAfter || false,
originalPageNumber: page.pageNumber, originalPageNumber: page.originalPageNumber || page.pageNumber || pageIndex + 1,
originalFileId: fileId, originalFileId: fileId,
})); }));
} else if (processedFile?.totalPages) { } else if (processedFile?.totalPages) {
@ -282,7 +285,7 @@ const PageEditor = ({
rotation: 0, rotation: 0,
thumbnail: null, // Will be generated later thumbnail: null, // Will be generated later
selected: false, selected: false,
splitBefore: false, splitAfter: false,
})); }));
} }
@ -442,9 +445,30 @@ const PageEditor = ({
} }
class ToggleSplitCommand { class ToggleSplitCommand {
constructor(public pageId: string) {} constructor(public pageIds: string[]) {}
execute() { execute() {
console.log('Toggle split:', this.pageId); if (!displayDocument) return;
console.log('Toggle split:', this.pageIds);
// Create new pages array with toggled split markers
const newPages = displayDocument.pages.map(page => {
if (this.pageIds.includes(page.id)) {
return {
...page,
splitAfter: !page.splitAfter
};
}
return page;
});
// Update the document with new split markers
const updatedDocument: PDFDocument = {
...displayDocument,
pages: newPages,
};
setEditedDocument(updatedDocument);
} }
} }
@ -484,8 +508,21 @@ const PageEditor = ({
}, [selectedPageNumbers]); }, [selectedPageNumbers]);
const handleSplit = useCallback(() => { const handleSplit = useCallback(() => {
console.log('Split at selected pages:', selectedPageNumbers); if (!displayDocument || selectedPageNumbers.length === 0) return;
}, [selectedPageNumbers]);
console.log('Toggle split markers at selected pages:', selectedPageNumbers);
// Get page IDs for selected pages
const selectedPageIds = selectedPageNumbers.map(pageNum => {
const page = displayDocument.pages.find(p => p.pageNumber === pageNum);
return page?.id || '';
}).filter(id => id);
if (selectedPageIds.length > 0) {
const command = new ToggleSplitCommand(selectedPageIds);
command.execute();
}
}, [selectedPageNumbers, displayDocument]);
const handleReorderPages = useCallback((sourcePageNumber: number, targetIndex: number, selectedPages?: number[]) => { const handleReorderPages = useCallback((sourcePageNumber: number, targetIndex: number, selectedPages?: number[]) => {
if (!displayDocument) return; if (!displayDocument) return;
@ -535,6 +572,9 @@ const PageEditor = ({
totalPages: newPages.length, totalPages: newPages.length,
}; };
console.log('Reordered document page numbers:', newPages.map(p => p.pageNumber));
console.log('Reordered document page IDs:', newPages.map(p => p.id));
// Update the edited document state // Update the edited document state
setEditedDocument(reorderedDocument); setEditedDocument(reorderedDocument);
@ -542,42 +582,23 @@ const PageEditor = ({
}, [displayDocument]); }, [displayDocument]);
// Helper function to read DOM state and update document with current rotations
const updateDocumentWithDOMState = useCallback((pdfDocument: PDFDocument): PDFDocument => {
const updatedPages = pdfDocument.pages.map(page => {
// Find the DOM element for this page
const pageElement = document.querySelector(`[data-page-id="${page.id}"]`);
if (pageElement) {
const img = pageElement.querySelector('img');
if (img && img.style.rotate) {
// Parse rotation from DOM (e.g., "90deg" -> 90)
const rotationMatch = img.style.rotate.match(/-?\d+/);
const domRotation = rotationMatch ? parseInt(rotationMatch[0]) : 0;
return {
...page,
rotation: domRotation // Update page rotation from DOM state
};
}
}
return page;
});
return {
...pdfDocument,
pages: updatedPages
};
}, []);
const onExportSelected = useCallback(async () => { const onExportSelected = useCallback(async () => {
if (!displayDocument || selectedPageNumbers.length === 0) return; if (!displayDocument || selectedPageNumbers.length === 0) return;
setExportLoading(true); setExportLoading(true);
try { try {
// Step 1: Update document with current DOM state (rotations) // Step 1: Apply DOM changes to document state first
const documentWithDOMState = updateDocumentWithDOMState(displayDocument); console.log('Applying DOM changes before export...');
const processedDocuments = documentManipulationService.applyDOMChangesToDocument(
mergedPdfDocument || displayDocument, // Original order
displayDocument // Current display order (includes reordering)
);
// Step 2: Get page IDs for selected pages // For selected pages export, we work with the first document (or single document)
const documentWithDOMState = Array.isArray(processedDocuments) ? processedDocuments[0] : processedDocuments;
// Step 2: Convert selected page numbers to page IDs from the document with DOM state
const selectedPageIds = selectedPageNumbers.map(pageNum => { const selectedPageIds = selectedPageNumbers.map(pageNum => {
const page = documentWithDOMState.pages.find(p => p.pageNumber === pageNum); const page = documentWithDOMState.pages.find(p => p.pageNumber === pageNum);
return page?.id || ''; return page?.id || '';
@ -592,35 +613,61 @@ const PageEditor = ({
); );
// Step 4: Download the result // Step 4: Download the result
if ('blob' in result) { pdfExportService.downloadFile(result.blob, result.filename);
pdfExportService.downloadFile(result.blob, result.filename);
}
setExportLoading(false); setExportLoading(false);
} catch (error) { } catch (error) {
console.error('Export failed:', error); console.error('Export failed:', error);
setExportLoading(false); setExportLoading(false);
} }
}, [displayDocument, selectedPageNumbers, updateDocumentWithDOMState]); }, [displayDocument, selectedPageNumbers, mergedPdfDocument]);
const onExportAll = useCallback(async () => { const onExportAll = useCallback(async () => {
if (!displayDocument) return; if (!displayDocument) return;
setExportLoading(true); setExportLoading(true);
try { try {
// Step 1: Update document with current DOM state (rotations) // Step 1: Apply DOM changes to document state first
const documentWithDOMState = updateDocumentWithDOMState(displayDocument); console.log('Applying DOM changes before export...');
const processedDocuments = documentManipulationService.applyDOMChangesToDocument(
// Step 2: Export all pages with pdfExportService mergedPdfDocument || displayDocument, // Original order
console.log('Exporting all pages with DOM rotations applied'); displayDocument // Current display order (includes reordering)
const result = await pdfExportService.exportPDF(
documentWithDOMState,
[],
{ selectedOnly: false, filename: documentWithDOMState.name }
); );
// Step 2: Check if we have multiple documents (splits) or single document
if (Array.isArray(processedDocuments)) {
// Multiple documents (splits) - export as ZIP
console.log('Exporting multiple split documents:', processedDocuments.length);
const blobs: Blob[] = [];
const filenames: string[] = [];
for (const doc of processedDocuments) {
const result = await pdfExportService.exportPDF(doc, [], { filename: doc.name });
blobs.push(result.blob);
filenames.push(result.filename);
}
// Create ZIP file
const JSZip = await import('jszip');
const zip = new JSZip.default();
blobs.forEach((blob, index) => {
zip.file(filenames[index], blob);
});
const zipBlob = await zip.generateAsync({ type: 'blob' });
const zipFilename = displayDocument.name.replace(/\.pdf$/i, '_split.zip');
pdfExportService.downloadFile(zipBlob, zipFilename);
} else {
// Single document - regular export
console.log('Exporting as single PDF');
const result = await pdfExportService.exportPDF(
processedDocuments,
[],
{ selectedOnly: false, filename: processedDocuments.name }
);
// Step 3: Download the result
if ('blob' in result) {
pdfExportService.downloadFile(result.blob, result.filename); pdfExportService.downloadFile(result.blob, result.filename);
} }
@ -629,7 +676,25 @@ const PageEditor = ({
console.error('Export failed:', error); console.error('Export failed:', error);
setExportLoading(false); setExportLoading(false);
} }
}, [displayDocument, updateDocumentWithDOMState]); }, [displayDocument, mergedPdfDocument]);
// Apply DOM changes to document state using dedicated service
const applyChanges = useCallback(() => {
if (!displayDocument) return;
// Pass current display document (which includes reordering) to get both reordering AND DOM changes
const processedDocuments = documentManipulationService.applyDOMChangesToDocument(
mergedPdfDocument || displayDocument, // Original order
displayDocument // Current display order (includes reordering)
);
// For apply changes, we only set the first document if it's an array (splits shouldn't affect document state)
const documentToSet = Array.isArray(processedDocuments) ? processedDocuments[0] : processedDocuments;
setEditedDocument(documentToSet);
console.log('Changes applied to document');
}, [displayDocument, mergedPdfDocument]);
const closePdf = useCallback(() => { const closePdf = useCallback(() => {
actions.clearAllFiles(); actions.clearAllFiles();
@ -665,6 +730,7 @@ const PageEditor = ({
showExportPreview: handleExportPreview, showExportPreview: handleExportPreview,
onExportSelected, onExportSelected,
onExportAll, onExportAll,
applyChanges,
exportLoading, exportLoading,
selectionMode, selectionMode,
selectedPages: selectedPageNumbers, selectedPages: selectedPageNumbers,
@ -673,7 +739,7 @@ const PageEditor = ({
} }
}, [ }, [
onFunctionsReady, handleUndo, handleRedo, handleRotate, handleDelete, handleSplit, onFunctionsReady, handleUndo, handleRedo, handleRotate, handleDelete, handleSplit,
handleExportPreview, onExportSelected, onExportAll, exportLoading, selectionMode, selectedPageNumbers, closePdf handleExportPreview, onExportSelected, onExportAll, applyChanges, exportLoading, selectionMode, selectedPageNumbers, closePdf
]); ]);
// Display all pages - use edited or original document // Display all pages - use edited or original document

View File

@ -194,9 +194,14 @@ const PageThumbnail: React.FC<PageThumbnailProps> = ({
const handleSplit = useCallback((e: React.MouseEvent) => { const handleSplit = useCallback((e: React.MouseEvent) => {
e.stopPropagation(); e.stopPropagation();
console.log('Split at page:', page.pageNumber);
onSetStatus(`Split marker toggled for page ${page.pageNumber}`); // Create a command to toggle split marker
}, [page.pageNumber, onSetStatus]); const command = new ToggleSplitCommand([page.id]);
onExecuteCommand(command);
const action = page.splitAfter ? 'removed' : 'added';
onSetStatus(`Split marker ${action} after page ${page.pageNumber}`);
}, [page.pageNumber, page.id, page.splitAfter, onExecuteCommand, onSetStatus, ToggleSplitCommand]);
return ( return (
<div <div
@ -411,8 +416,8 @@ const PageThumbnail: React.FC<PageThumbnailProps> = ({
</ActionIcon> </ActionIcon>
</Tooltip> </Tooltip>
{index > 0 && ( {index < totalPages - 1 && (
<Tooltip label="Split Here"> <Tooltip label="Split After">
<ActionIcon <ActionIcon
size="md" size="md"
variant="subtle" variant="subtle"
@ -427,16 +432,16 @@ const PageThumbnail: React.FC<PageThumbnailProps> = ({
</div> </div>
{/* Split indicator */} {/* Split indicator - shows where document will be split */}
{page.splitBefore && ( {page.splitAfter && (
<div <div
style={{ style={{
position: 'absolute', position: 'absolute',
top: '-1px', right: '-8px',
left: '50%', top: '50%',
transform: 'translateX(-50%)', transform: 'translateY(-50%)',
width: '100px', width: '2px',
height: '2px', height: '60px',
backgroundColor: '#3b82f6', backgroundColor: '#3b82f6',
zIndex: 5, zIndex: 5,
}} }}

View File

@ -77,6 +77,7 @@ export function usePDFProcessor() {
pages.push({ pages.push({
id: `${file.name}-page-${i}`, id: `${file.name}-page-${i}`,
pageNumber: i, pageNumber: i,
originalPageNumber: i,
thumbnail: null, // Will be loaded lazily thumbnail: null, // Will be loaded lazily
rotation: 0, rotation: 0,
selected: false selected: false

View File

@ -0,0 +1,162 @@
import { PDFDocument, PDFPage } from '../types/pageEditor';
/**
* Service for applying DOM changes to PDF document state
* Reads current DOM state and updates the document accordingly
*/
export class DocumentManipulationService {
/**
* Apply all DOM changes (rotations, splits, reordering) to document state
* Returns single document or multiple documents if splits are present
*/
applyDOMChangesToDocument(pdfDocument: PDFDocument, currentDisplayOrder?: PDFDocument): PDFDocument | PDFDocument[] {
console.log('DocumentManipulationService: Applying DOM changes to document');
console.log('Original document page order:', pdfDocument.pages.map(p => p.pageNumber));
console.log('Current display order:', currentDisplayOrder?.pages.map(p => p.pageNumber) || 'none provided');
// Use current display order (from React state) if provided, otherwise use original order
const baseDocument = currentDisplayOrder || pdfDocument;
console.log('Using page order:', baseDocument.pages.map(p => p.pageNumber));
// Apply DOM changes to each page (rotation, split markers)
const updatedPages = baseDocument.pages.map(page => this.applyPageChanges(page));
// Create final document with reordered pages and applied changes
const finalDocument = {
...pdfDocument, // Use original document metadata but updated pages
pages: updatedPages // Use reordered pages with applied changes
};
// Check for splits and return multiple documents if needed
if (this.hasSplitMarkers(finalDocument)) {
return this.createSplitDocuments(finalDocument);
}
return finalDocument;
}
/**
* Check if document has split markers
*/
private hasSplitMarkers(document: PDFDocument): boolean {
return document.pages.some(page => page.splitAfter);
}
/**
* Create multiple documents from split markers
*/
private createSplitDocuments(document: PDFDocument): PDFDocument[] {
const documents: PDFDocument[] = [];
const splitPoints: number[] = [];
// Find split points - pages with splitAfter create split points AFTER them
document.pages.forEach((page, index) => {
if (page.splitAfter) {
splitPoints.push(index + 1);
}
});
// Add end point if not already there
if (splitPoints.length === 0 || splitPoints[splitPoints.length - 1] !== document.pages.length) {
splitPoints.push(document.pages.length);
}
let startIndex = 0;
let partNumber = 1;
for (const endIndex of splitPoints) {
const segmentPages = document.pages.slice(startIndex, endIndex);
if (segmentPages.length > 0) {
documents.push({
...document,
id: `${document.id}_part_${partNumber}`,
name: `${document.name.replace(/\.pdf$/i, '')}_part_${partNumber}.pdf`,
pages: segmentPages,
totalPages: segmentPages.length
});
partNumber++;
}
startIndex = endIndex;
}
console.log(`Created ${documents.length} split documents`);
return documents;
}
/**
* Apply DOM changes for a single page
*/
private applyPageChanges(page: PDFPage): PDFPage {
// Find the DOM element for this page
const pageElement = document.querySelector(`[data-page-id="${page.id}"]`);
if (!pageElement) {
console.log(`Page ${page.pageNumber}: No DOM element found, keeping original state`);
return page;
}
const updatedPage = { ...page };
// Apply rotation changes from DOM
updatedPage.rotation = this.getRotationFromDOM(pageElement, page);
// Apply split marker changes from document state (already handled by commands)
// Split markers are already updated by ToggleSplitCommand, so no DOM reading needed
return updatedPage;
}
/**
* Read rotation from DOM element
*/
private getRotationFromDOM(pageElement: Element, originalPage: PDFPage): number {
const img = pageElement.querySelector('img');
if (img && img.style.rotate) {
// Parse rotation from DOM (e.g., "90deg" -> 90)
const rotationMatch = img.style.rotate.match(/-?\d+/);
const domRotation = rotationMatch ? parseInt(rotationMatch[0]) : 0;
console.log(`Page ${originalPage.pageNumber}: DOM rotation = ${domRotation}°, original = ${originalPage.rotation}°`);
return domRotation;
}
console.log(`Page ${originalPage.pageNumber}: No DOM rotation found, keeping original = ${originalPage.rotation}°`);
return originalPage.rotation;
}
/**
* Reset all DOM changes (useful for "discard changes" functionality)
*/
resetDOMToDocumentState(pdfDocument: PDFDocument): void {
console.log('DocumentManipulationService: Resetting DOM to match document state');
pdfDocument.pages.forEach(page => {
const pageElement = document.querySelector(`[data-page-id="${page.id}"]`);
if (pageElement) {
const img = pageElement.querySelector('img');
if (img) {
// Reset rotation to match document state
img.style.rotate = `${page.rotation}deg`;
}
}
});
}
/**
* Check if DOM state differs from document state
*/
hasUnsavedChanges(pdfDocument: PDFDocument): boolean {
return pdfDocument.pages.some(page => {
const pageElement = document.querySelector(`[data-page-id="${page.id}"]`);
if (pageElement) {
const domRotation = this.getRotationFromDOM(pageElement, page);
return domRotation !== page.rotation;
}
return false;
});
}
}
// Export singleton instance
export const documentManipulationService = new DocumentManipulationService();

View File

@ -4,7 +4,6 @@ import { PDFDocument, PDFPage } from '../types/pageEditor';
export interface ExportOptions { export interface ExportOptions {
selectedOnly?: boolean; selectedOnly?: boolean;
filename?: string; filename?: string;
splitDocuments?: boolean;
} }
export class PDFExportService { export class PDFExportService {
@ -15,8 +14,8 @@ export class PDFExportService {
pdfDocument: PDFDocument, pdfDocument: PDFDocument,
selectedPageIds: string[] = [], selectedPageIds: string[] = [],
options: ExportOptions = {} options: ExportOptions = {}
): Promise<{ blob: Blob; filename: string } | { blobs: Blob[]; filenames: string[] }> { ): Promise<{ blob: Blob; filename: string }> {
const { selectedOnly = false, filename, splitDocuments = false } = options; const { selectedOnly = false, filename } = options;
try { try {
// Determine which pages to export // Determine which pages to export
@ -28,17 +27,13 @@ export class PDFExportService {
throw new Error('No pages to export'); throw new Error('No pages to export');
} }
// Load original PDF once // Load original PDF and create new document
const originalPDFBytes = await pdfDocument.file.arrayBuffer(); const originalPDFBytes = await pdfDocument.file.arrayBuffer();
const sourceDoc = await PDFLibDocument.load(originalPDFBytes); const sourceDoc = await PDFLibDocument.load(originalPDFBytes);
const blob = await this.createSingleDocument(sourceDoc, pagesToExport);
if (splitDocuments) { const exportFilename = this.generateFilename(filename || pdfDocument.name, selectedOnly);
return await this.createSplitDocuments(sourceDoc, pagesToExport, filename || pdfDocument.name);
} else { return { blob, filename: exportFilename };
const blob = await this.createSingleDocument(sourceDoc, pagesToExport);
const exportFilename = this.generateFilename(filename || pdfDocument.name, selectedOnly);
return { blob, filename: exportFilename };
}
} catch (error) { } catch (error) {
console.error('PDF export error:', error); console.error('PDF export error:', error);
throw new Error(`Failed to export PDF: ${error instanceof Error ? error.message : 'Unknown error'}`); throw new Error(`Failed to export PDF: ${error instanceof Error ? error.message : 'Unknown error'}`);
@ -55,8 +50,8 @@ export class PDFExportService {
const newDoc = await PDFLibDocument.create(); const newDoc = await PDFLibDocument.create();
for (const page of pages) { for (const page of pages) {
// Get the original page from source document // Get the original page from source document using originalPageNumber
const sourcePageIndex = page.pageNumber - 1; const sourcePageIndex = page.originalPageNumber - 1;
if (sourcePageIndex >= 0 && sourcePageIndex < sourceDoc.getPageCount()) { if (sourcePageIndex >= 0 && sourcePageIndex < sourceDoc.getPageCount()) {
// Copy the page // Copy the page
@ -81,70 +76,6 @@ export class PDFExportService {
return new Blob([pdfBytes], { type: 'application/pdf' }); return new Blob([pdfBytes], { type: 'application/pdf' });
} }
/**
* Create multiple PDF documents based on split markers
*/
private async createSplitDocuments(
sourceDoc: PDFLibDocument,
pages: PDFPage[],
baseFilename: string
): Promise<{ blobs: Blob[]; filenames: string[] }> {
const splitPoints: number[] = [];
const blobs: Blob[] = [];
const filenames: string[] = [];
// Find split points
pages.forEach((page, index) => {
if (page.splitBefore && index > 0) {
splitPoints.push(index);
}
});
// Add end point
splitPoints.push(pages.length);
let startIndex = 0;
let partNumber = 1;
for (const endIndex of splitPoints) {
const segmentPages = pages.slice(startIndex, endIndex);
if (segmentPages.length > 0) {
const newDoc = await PDFLibDocument.create();
for (const page of segmentPages) {
const sourcePageIndex = page.pageNumber - 1;
if (sourcePageIndex >= 0 && sourcePageIndex < sourceDoc.getPageCount()) {
const [copiedPage] = await newDoc.copyPages(sourceDoc, [sourcePageIndex]);
if (page.rotation !== 0) {
copiedPage.setRotation(degrees(page.rotation));
}
newDoc.addPage(copiedPage);
}
}
// Set metadata
newDoc.setCreator('Stirling PDF');
newDoc.setProducer('Stirling PDF');
newDoc.setTitle(`${baseFilename} - Part ${partNumber}`);
const pdfBytes = await newDoc.save();
const blob = new Blob([pdfBytes], { type: 'application/pdf' });
const filename = this.generateSplitFilename(baseFilename, partNumber);
blobs.push(blob);
filenames.push(filename);
partNumber++;
}
startIndex = endIndex;
}
return { blobs, filenames };
}
/** /**
* Generate appropriate filename for export * Generate appropriate filename for export
@ -155,13 +86,6 @@ export class PDFExportService {
return `${baseName}${suffix}.pdf`; return `${baseName}${suffix}.pdf`;
} }
/**
* Generate filename for split documents
*/
private generateSplitFilename(baseName: string, partNumber: number): string {
const cleanBaseName = baseName.replace(/\.pdf$/i, '');
return `${cleanBaseName}_part_${partNumber}.pdf`;
}
/** /**
* Download a single file * Download a single file
@ -185,7 +109,7 @@ export class PDFExportService {
* Download multiple files as a ZIP * Download multiple files as a ZIP
*/ */
async downloadAsZip(blobs: Blob[], filenames: string[], zipFilename: string): Promise<void> { async downloadAsZip(blobs: Blob[], filenames: string[], zipFilename: string): Promise<void> {
// For now, download files wherindividually // For now, download files individually
blobs.forEach((blob, index) => { blobs.forEach((blob, index) => {
setTimeout(() => { setTimeout(() => {
this.downloadFile(blob, filenames[index]); this.downloadFile(blob, filenames[index]);
@ -230,8 +154,8 @@ export class PDFExportService {
? pdfDocument.pages.filter(page => selectedPageIds.includes(page.id)) ? pdfDocument.pages.filter(page => selectedPageIds.includes(page.id))
: pdfDocument.pages; : pdfDocument.pages;
const splitCount = pagesToExport.reduce((count, page, index) => { const splitCount = pagesToExport.reduce((count, page) => {
return count + (page.splitBefore && index > 0 ? 1 : 0); return count + (page.splitAfter ? 1 : 0);
}, 1); // At least 1 document }, 1); // At least 1 document
// Rough size estimation (very approximate) // Rough size estimation (very approximate)

View File

@ -1,10 +1,11 @@
export interface PDFPage { export interface PDFPage {
id: string; id: string;
pageNumber: number; pageNumber: number;
originalPageNumber: number;
thumbnail: string | null; thumbnail: string | null;
rotation: number; rotation: number;
selected: boolean; selected: boolean;
splitBefore?: boolean; splitAfter?: boolean;
} }
export interface PDFDocument { export interface PDFDocument {