mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-16 11:23:10 +02:00
@@ -2,8 +2,9 @@ import { useImperativeHandle, forwardRef, useEffect, useRef } from 'react';
|
||||
import { useHistoryCapability } from '@embedpdf/plugin-history/react';
|
||||
import { useAnnotationCapability } from '@embedpdf/plugin-annotation/react';
|
||||
import { useSignature } from '@app/contexts/SignatureContext';
|
||||
import { uuidV4 } from '@embedpdf/models';
|
||||
import { PdfAnnotationSubtype, uuidV4 } from '@embedpdf/models';
|
||||
import type { HistoryAPI } from '@app/components/viewer/viewerTypes';
|
||||
import { ANNOTATION_RECREATION_DELAY_MS, ANNOTATION_VERIFICATION_DELAY_MS } from '@app/constants/app';
|
||||
|
||||
export const HistoryAPIBridge = forwardRef<HistoryAPI>(function HistoryAPIBridge(_, ref) {
|
||||
const { provides: historyApi } = useHistoryCapability();
|
||||
@@ -19,14 +20,14 @@ export const HistoryAPIBridge = forwardRef<HistoryAPI>(function HistoryAPIBridge
|
||||
const annotation = event.annotation;
|
||||
|
||||
// Store image data for all STAMP annotations immediately when created or modified
|
||||
if (annotation && annotation.type === 13 && annotation.id && annotation.imageSrc) {
|
||||
if (annotation && annotation.type === PdfAnnotationSubtype.STAMP && annotation.id && annotation.imageSrc) {
|
||||
const storedImageData = getImageData(annotation.id);
|
||||
if (!storedImageData) {
|
||||
storeImageData(annotation.id, annotation.imageSrc);
|
||||
}
|
||||
}
|
||||
|
||||
if (annotation && annotation.type === 13 && annotation.id) {
|
||||
if (annotation && annotation.type === PdfAnnotationSubtype.STAMP && annotation.id) {
|
||||
// Prevent infinite loops when we recreate annotations
|
||||
if (restoringIds.current.has(annotation.id)) {
|
||||
restoringIds.current.delete(annotation.id);
|
||||
@@ -59,7 +60,7 @@ export const HistoryAPIBridge = forwardRef<HistoryAPI>(function HistoryAPIBridge
|
||||
data: storedImageData,
|
||||
appearance: storedImageData,
|
||||
});
|
||||
}, 50);
|
||||
}, ANNOTATION_RECREATION_DELAY_MS);
|
||||
} catch (restoreError) {
|
||||
console.error('HistoryAPI: Failed to restore cropped signature:', restoreError);
|
||||
}
|
||||
@@ -70,7 +71,7 @@ export const HistoryAPIBridge = forwardRef<HistoryAPI>(function HistoryAPIBridge
|
||||
// Handle annotation restoration after undo operations
|
||||
if (event.type === 'create' && event.committed) {
|
||||
// Check if this is a STAMP annotation (signature) that might need image data restoration
|
||||
if (annotation && annotation.type === 13 && annotation.id) {
|
||||
if (annotation && annotation.type === PdfAnnotationSubtype.STAMP && annotation.id) {
|
||||
getImageData(annotation.id);
|
||||
|
||||
// Delay the check to allow the annotation to be fully created
|
||||
@@ -103,12 +104,12 @@ export const HistoryAPIBridge = forwardRef<HistoryAPI>(function HistoryAPIBridge
|
||||
// Small delay to ensure deletion completes
|
||||
setTimeout(() => {
|
||||
annotationApi.createAnnotation(event.pageIndex, restoredData);
|
||||
}, 50);
|
||||
}, ANNOTATION_RECREATION_DELAY_MS);
|
||||
} catch (error) {
|
||||
console.error('HistoryAPI: Failed to restore annotation:', error);
|
||||
}
|
||||
}
|
||||
}, 100);
|
||||
}, ANNOTATION_VERIFICATION_DELAY_MS);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -43,23 +43,32 @@ export function PdfViewerToolbar({
|
||||
|
||||
// Register for immediate scroll updates and sync with actual scroll state
|
||||
useEffect(() => {
|
||||
registerImmediateScrollUpdate((currentPage, _totalPages) => {
|
||||
const unregister = registerImmediateScrollUpdate((currentPage, _totalPages) => {
|
||||
setPageInput(currentPage);
|
||||
});
|
||||
setPageInput(scrollState.currentPage);
|
||||
}, [registerImmediateScrollUpdate]);
|
||||
return () => {
|
||||
unregister?.();
|
||||
};
|
||||
}, [registerImmediateScrollUpdate, scrollState.currentPage]);
|
||||
|
||||
// Register for immediate zoom updates and sync with actual zoom state
|
||||
useEffect(() => {
|
||||
registerImmediateZoomUpdate(setDisplayZoomPercent);
|
||||
const unregister = registerImmediateZoomUpdate(setDisplayZoomPercent);
|
||||
setDisplayZoomPercent(zoomState.zoomPercent || 140);
|
||||
}, [zoomState.zoomPercent, registerImmediateZoomUpdate]);
|
||||
return () => {
|
||||
unregister?.();
|
||||
};
|
||||
}, [registerImmediateZoomUpdate, zoomState.zoomPercent]);
|
||||
|
||||
useEffect(() => {
|
||||
registerImmediateSpreadUpdate((_mode, isDual) => {
|
||||
const unregister = registerImmediateSpreadUpdate((_mode, isDual) => {
|
||||
setIsDualPageActive(isDual);
|
||||
});
|
||||
setIsDualPageActive(spreadState.isDualPage);
|
||||
return () => {
|
||||
unregister?.();
|
||||
};
|
||||
}, [registerImmediateSpreadUpdate, spreadState.isDualPage]);
|
||||
|
||||
const handleZoomOut = () => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useImperativeHandle, forwardRef, useEffect, useCallback, useRef } from 'react';
|
||||
import { useImperativeHandle, forwardRef, useEffect, useCallback, useRef, useState } from 'react';
|
||||
import { useAnnotationCapability } from '@embedpdf/plugin-annotation/react';
|
||||
import { PdfAnnotationSubtype, uuidV4 } from '@embedpdf/models';
|
||||
import { useSignature } from '@app/contexts/SignatureContext';
|
||||
@@ -14,6 +14,14 @@ const MIN_SIGNATURE_DIMENSION = 12;
|
||||
// This provides a good balance between visual fidelity and performance/memory usage.
|
||||
const TEXT_OVERSAMPLE_FACTOR = 2;
|
||||
|
||||
type TextStampImageResult = {
|
||||
dataUrl: string;
|
||||
pixelWidth: number;
|
||||
pixelHeight: number;
|
||||
displayWidth: number;
|
||||
displayHeight: number;
|
||||
};
|
||||
|
||||
const extractDataUrl = (value: unknown, depth = 0, visited: Set<unknown> = new Set()): string | undefined => {
|
||||
if (!value || depth > 6) return undefined;
|
||||
|
||||
@@ -48,7 +56,7 @@ const extractDataUrl = (value: unknown, depth = 0, visited: Set<unknown> = new S
|
||||
const createTextStampImage = (
|
||||
config: SignParameters,
|
||||
displaySize?: { width: number; height: number } | null
|
||||
): { dataUrl: string; pixelWidth: number; pixelHeight: number; displayWidth: number; displayHeight: number } | null => {
|
||||
): TextStampImageResult | null => {
|
||||
const text = (config.signerName ?? '').trim();
|
||||
if (!text) {
|
||||
return null;
|
||||
@@ -123,10 +131,20 @@ const createTextStampImage = (
|
||||
export const SignatureAPIBridge = forwardRef<SignatureAPI>(function SignatureAPIBridge(_, ref) {
|
||||
const { provides: annotationApi } = useAnnotationCapability();
|
||||
const { signatureConfig, storeImageData, isPlacementMode, placementPreviewSize } = useSignature();
|
||||
const { getZoomState } = useViewer();
|
||||
const currentZoom = getZoomState()?.currentZoom ?? 1;
|
||||
const { getZoomState, registerImmediateZoomUpdate } = useViewer();
|
||||
const [currentZoom, setCurrentZoom] = useState(() => getZoomState()?.currentZoom ?? 1);
|
||||
const lastStampImageRef = useRef<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentZoom(getZoomState()?.currentZoom ?? 1);
|
||||
const unregister = registerImmediateZoomUpdate(percent => {
|
||||
setCurrentZoom(Math.max(percent / 100, 0.01));
|
||||
});
|
||||
return () => {
|
||||
unregister?.();
|
||||
};
|
||||
}, [getZoomState, registerImmediateZoomUpdate]);
|
||||
|
||||
const cssToPdfSize = useCallback(
|
||||
(size: { width: number; height: number }) => {
|
||||
const zoom = currentZoom || 1;
|
||||
@@ -328,7 +346,7 @@ export const SignatureAPIBridge = forwardRef<SignatureAPI>(function SignatureAPI
|
||||
if (pageAnnotationsTask) {
|
||||
pageAnnotationsTask.toPromise().then((pageAnnotations: any) => {
|
||||
const annotation = pageAnnotations?.find((ann: any) => ann.id === annotationId);
|
||||
if (annotation && annotation.type === 13 && annotation.imageSrc) {
|
||||
if (annotation && annotation.type === PdfAnnotationSubtype.STAMP && annotation.imageSrc) {
|
||||
// Store image data before deletion
|
||||
storeImageData(annotationId, annotation.imageSrc);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user