This commit is contained in:
Reece Browne 2025-11-14 20:43:57 +00:00 committed by GitHub
commit d7032ec143
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 20 additions and 66 deletions

View File

@ -191,7 +191,6 @@ export function LocalEmbedPDF({ file, url, enableAnnotations = false, onSignatur
height: '100%', height: '100%',
width: '100%', width: '100%',
position: 'relative', position: 'relative',
overflow: 'hidden',
flex: 1, flex: 1,
minHeight: 0, minHeight: 0,
minWidth: 0, minWidth: 0,
@ -287,8 +286,6 @@ export function LocalEmbedPDF({ file, url, enableAnnotations = false, onSignatur
minHeight: 0, minHeight: 0,
minWidth: 0, minWidth: 0,
contain: 'strict', contain: 'strict',
display: 'flex',
justifyContent: 'center',
}} }}
> >
<Scroller <Scroller

View File

@ -7,9 +7,7 @@ import {
determineAutoZoom, determineAutoZoom,
DEFAULT_FALLBACK_ZOOM, DEFAULT_FALLBACK_ZOOM,
DEFAULT_VISIBILITY_THRESHOLD, DEFAULT_VISIBILITY_THRESHOLD,
measureRenderedPageRect,
useFitWidthResize, useFitWidthResize,
ZoomViewport,
} from '@app/utils/viewerZoom'; } from '@app/utils/viewerZoom';
import { getFirstPageAspectRatioFromStub } from '@app/utils/pageMetadata'; import { getFirstPageAspectRatioFromStub } from '@app/utils/pageMetadata';
@ -73,18 +71,6 @@ export function ZoomAPIBridge() {
} }
}, [spreadMode, zoomState?.zoomLevel, scheduleAutoZoom, requestFitWidth]); }, [spreadMode, zoomState?.zoomLevel, scheduleAutoZoom, requestFitWidth]);
const getViewportSnapshot = useCallback((): ZoomViewport | null => {
if (!zoomState || typeof zoomState !== 'object') {
return null;
}
if ('viewport' in zoomState) {
const candidate = (zoomState as { viewport?: ZoomViewport | null }).viewport;
return candidate ?? null;
}
return null;
}, [zoomState]);
const isManagedZoom = const isManagedZoom =
!!zoom && !!zoom &&
@ -119,7 +105,7 @@ export function ZoomAPIBridge() {
} }
const fitWidthZoom = zoomState.currentZoomLevel; const fitWidthZoom = zoomState.currentZoomLevel;
if (!fitWidthZoom || fitWidthZoom <= 0) { if (!fitWidthZoom || fitWidthZoom <= 0 || fitWidthZoom === 1) {
return; return;
} }
@ -137,37 +123,23 @@ export function ZoomAPIBridge() {
const pagesPerSpread = currentSpreadMode !== SpreadMode.None ? 2 : 1; const pagesPerSpread = currentSpreadMode !== SpreadMode.None ? 2 : 1;
const metadataAspectRatio = getFirstPageAspectRatioFromStub(firstFileStub); const metadataAspectRatio = getFirstPageAspectRatioFromStub(firstFileStub);
const viewport = getViewportSnapshot();
if (cancelled) { if (cancelled) {
return; return;
} }
const metrics = viewport ?? {}; const viewportWidth = window.innerWidth ?? 0;
const viewportWidth = const viewportHeight = window.innerHeight ?? 0;
metrics.clientWidth ?? metrics.width ?? window.innerWidth ?? 0;
const viewportHeight =
metrics.clientHeight ?? metrics.height ?? window.innerHeight ?? 0;
if (viewportWidth <= 0 || viewportHeight <= 0) { if (viewportWidth <= 0 || viewportHeight <= 0) {
return; return;
} }
const pageRect = await measureRenderedPageRect({
shouldCancel: () => cancelled,
});
if (cancelled) {
return;
}
const decision = determineAutoZoom({ const decision = determineAutoZoom({
viewportWidth, viewportWidth,
viewportHeight, viewportHeight,
fitWidthZoom, fitWidthZoom,
pagesPerSpread, pagesPerSpread,
pageRect: pageRect pageRect: undefined,
? { width: pageRect.width, height: pageRect.height }
: undefined,
metadataAspectRatio: metadataAspectRatio ?? null, metadataAspectRatio: metadataAspectRatio ?? null,
visibilityThreshold: DEFAULT_VISIBILITY_THRESHOLD, visibilityThreshold: DEFAULT_VISIBILITY_THRESHOLD,
fallbackZoom: DEFAULT_FALLBACK_ZOOM, fallbackZoom: DEFAULT_FALLBACK_ZOOM,
@ -197,7 +169,6 @@ export function ZoomAPIBridge() {
firstFileId, firstFileId,
firstFileStub, firstFileStub,
requestFitWidth, requestFitWidth,
getViewportSnapshot,
autoZoomTick, autoZoomTick,
spreadMode, spreadMode,
triggerImmediateZoomUpdate, triggerImmediateZoomUpdate,

View File

@ -1,6 +1,6 @@
import { useEffect, useRef } from 'react'; import { useEffect, useRef } from 'react';
export const DEFAULT_VISIBILITY_THRESHOLD = 80; // Require at least 80% of the page height to be visible export const DEFAULT_VISIBILITY_THRESHOLD = 70; // Require at least 70% of the page height to be visible
export const DEFAULT_FALLBACK_ZOOM = 1.44; // 144% fallback when no reliable metadata is present export const DEFAULT_FALLBACK_ZOOM = 1.44; // 144% fallback when no reliable metadata is present
export interface ZoomViewport { export interface ZoomViewport {
@ -36,47 +36,33 @@ export function determineAutoZoom({
visibilityThreshold = DEFAULT_VISIBILITY_THRESHOLD, visibilityThreshold = DEFAULT_VISIBILITY_THRESHOLD,
fallbackZoom = DEFAULT_FALLBACK_ZOOM, fallbackZoom = DEFAULT_FALLBACK_ZOOM,
}: AutoZoomParams): AutoZoomDecision { }: AutoZoomParams): AutoZoomDecision {
// Get aspect ratio from pageRect or metadata
const rectWidth = pageRect?.width ?? 0; const rectWidth = pageRect?.width ?? 0;
const rectHeight = pageRect?.height ?? 0; const rectHeight = pageRect?.height ?? 0;
const aspectRatio: number | null = const aspectRatio: number | null =
rectWidth > 0 ? rectHeight / rectWidth : metadataAspectRatio ?? null; rectWidth > 0 ? rectHeight / rectWidth : metadataAspectRatio ?? null;
let renderedHeight: number | null = rectHeight > 0 ? rectHeight : null; // Need aspect ratio to proceed
if (!aspectRatio || aspectRatio <= 0) {
if (!renderedHeight || renderedHeight <= 0) {
if (aspectRatio == null || aspectRatio <= 0) {
return { type: 'fallback', zoom: Math.min(fitWidthZoom, fallbackZoom) }; return { type: 'fallback', zoom: Math.min(fitWidthZoom, fallbackZoom) };
} }
const pageWidth = viewportWidth / (fitWidthZoom * pagesPerSpread); // Landscape pages need 100% visibility, portrait need the specified threshold
const pageHeight = pageWidth * aspectRatio; const isLandscape = aspectRatio < 1;
renderedHeight = pageHeight * fitWidthZoom;
}
if (!renderedHeight || renderedHeight <= 0) {
return { type: 'fitWidth' };
}
const isLandscape = aspectRatio !== null && aspectRatio < 1;
const targetVisibility = isLandscape ? 100 : visibilityThreshold; const targetVisibility = isLandscape ? 100 : visibilityThreshold;
const visiblePercent = (viewportHeight / renderedHeight) * 100; // Calculate zoom level that shows targetVisibility% of page height
const pageHeightAtFitWidth = (viewportWidth / pagesPerSpread) * aspectRatio;
const heightBasedZoom = fitWidthZoom * (viewportHeight / pageHeightAtFitWidth) / (targetVisibility / 100);
if (visiblePercent >= targetVisibility) { // Use whichever zoom is smaller (more zoomed out) to satisfy both width and height constraints
if (heightBasedZoom < fitWidthZoom) {
// Need to zoom out from fitWidth to show enough height
return { type: 'adjust', zoom: heightBasedZoom };
} else {
// fitWidth already shows enough
return { type: 'fitWidth' }; return { type: 'fitWidth' };
} }
const allowableHeightRatio = targetVisibility / 100;
const zoomScale =
viewportHeight / (allowableHeightRatio * renderedHeight);
const targetZoom = Math.min(fitWidthZoom, fitWidthZoom * zoomScale);
if (Math.abs(targetZoom - fitWidthZoom) < 0.001) {
return { type: 'fitWidth' };
}
return { type: 'adjust', zoom: targetZoom };
} }
export interface MeasurePageRectOptions { export interface MeasurePageRectOptions {