Skip to content

Commit

Permalink
feat: pdf viewer supports fit to page
Browse files Browse the repository at this point in the history
  • Loading branch information
fundon committed Nov 13, 2024
1 parent 9469b13 commit a72bac1
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ import { calculatePageNum } from './utils';

const THUMBNAIL_WIDTH = 94;

enum DisplayMode {
FitToPage,
FitToWidth,
ActualSize,
}

interface ViewerProps {
model: AttachmentBlockModel;
}
Expand All @@ -44,6 +50,8 @@ interface PDFViewerInnerProps {

const PDFViewerInner = ({ pdf, state }: PDFViewerInnerProps) => {
const [cursor, setCursor] = useState(0);
const [zoom, setZoom] = useState(1);
const [mode, _setMode] = useState(DisplayMode.FitToPage);
const [collapsed, setCollapsed] = useState(true);
const [viewportInfo, setViewportInfo] = useState({ width: 0, height: 0 });

Expand All @@ -69,11 +77,7 @@ const PDFViewerInner = ({ pdf, state }: PDFViewerInnerProps) => {
const scroller = pagesScrollerHandleRef.current;
if (!scroller) return;

scroller.scrollToIndex({
index,
align: 'center',
behavior: 'smooth',
});
scroller.scrollToIndex({ index, align: 'center', behavior: 'smooth' });
},
[pagesScrollerHandleRef]
);
Expand All @@ -99,13 +103,24 @@ const PDFViewerInner = ({ pdf, state }: PDFViewerInnerProps) => {
[pdf]
);

const pagesContext = useMemo(() => {
const { width: w, height: h } = state.meta;
console.log(zoom);

return {
width: zoom * w,
height: zoom * h,
pageClassName: styles.pdfPage,
};
}, [state, zoom]);

const thumbnailsConfig = useMemo(() => {
const { height: vh } = viewportInfo;
const { pageCount: t, height: h, width: w } = state.meta;
const { width: w, height: h, pageCount: c } = state.meta;
const p = h / (w || 1);
const pw = THUMBNAIL_WIDTH;
const ph = Math.ceil(pw * p);
const height = Math.min(vh - 60 - 24 - 24 - 2 - 8, t * ph + (t - 1) * 12);
const height = Math.min(vh - 60 - 24 - 24 - 2 - 8, c * ph + (c - 1) * 12);
return {
context: {
width: pw,
Expand All @@ -124,6 +139,22 @@ const PDFViewerInner = ({ pdf, state }: PDFViewerInnerProps) => {
};
}, []);

useEffect(() => {
let { width: vw, height: vh } = viewportInfo;
const { width: w, height: h } = state.meta;

vh -= 40;
vw -= 40;

if (mode === DisplayMode.FitToPage) {
if (h > w) {
setZoom(vh / h);
} else {
setZoom(vw / w);
}
}
}, [viewportInfo, mode, state]);

useEffect(() => {
const viewer = viewerRef.current;
if (!viewer) return;
Expand Down Expand Up @@ -156,11 +187,7 @@ const PDFViewerInner = ({ pdf, state }: PDFViewerInnerProps) => {
Footer: ListPadding,
ScrollSeekPlaceholder,
}}
context={{
width: state.meta.width,
height: state.meta.height,
pageClassName: styles.pdfPage,
}}
context={pagesContext}
scrollSeekConfiguration={scrollSeekConfig}
/>
<div className={clsx(['thumbnails', styles.pdfThumbnails])}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,9 +148,8 @@ export const useSharingUrl = ({ workspaceId, pageId }: UseSharingUrl) => {
if (sharingUrl) {
copyTextToClipboard(sharingUrl)
.then(success => {
if (success) {
notify.success({ title: t['Copied link to clipboard']() });
}
if (!success) return;
notify.success({ title: t['Copied link to clipboard']() });
})
.catch(err => {
console.error(err);
Expand Down
16 changes: 12 additions & 4 deletions packages/frontend/core/src/modules/pdf/views/components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,24 @@ export const ScrollSeekPlaceholder = forwardRef<
ScrollSeekPlaceholderProps & {
context?: PDFVirtuosoContext;
}
>(({ context }, ref) => {
>(({ context, index, height: size }, ref) => {
const className = context?.pageClassName;
const width = context?.width ?? 537;
const height = context?.height ?? 759;
const style = { width, aspectRatio: `${width} / ${height}` };

return (
<div className={className} style={style} ref={ref}>
<LoadingSvg />
</div>
<Item
data-index={index}
data-known-size={size}
data-item-index={index}
style={{ overflowAnchor: 'none' }}
ref={ref}
>
<div className={className} style={style}>
<LoadingSvg />
</div>
</Item>
);
});

Expand Down
43 changes: 27 additions & 16 deletions packages/frontend/core/src/modules/pdf/views/page-renderer.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useI18n } from '@affine/i18n';
import { useLiveData } from '@toeverything/infra';
import { useEffect, useRef, useState } from 'react';
import { forwardRef, useEffect, useRef, useState } from 'react';

import type { PDF } from '../entities/pdf';
import type { PDFPage } from '../entities/pdf-page';
Expand Down Expand Up @@ -58,27 +58,38 @@ export const PDFPageRenderer = ({
ctx.drawImage(img, 0, 0);
}, [img, width, height, scale]);

if (error) {
return (
<div className={className} style={style}>
<p className={styles.pdfPageError}>
{t['com.affine.pdf.page.render.error']()}
</p>
</div>
);
}

return (
<div
className={className}
style={style}
onClick={() => onSelect?.(pageNum)}
>
{img === null ? (
<LoadingSvg />
) : (
<canvas className={styles.pdfPageCanvas} ref={canvasRef} />
)}
<PageRendererInner
img={img}
ref={canvasRef}
err={error ? t['com.affine.pdf.page.render.error']() : null}
/>
</div>
);
};

interface PageRendererInnerProps {
img: ImageBitmap | null;
err: string | null;
}

const PageRendererInner = forwardRef<HTMLCanvasElement, PageRendererInnerProps>(
({ img, err }, ref) => {
if (img) {
return <canvas className={styles.pdfPageCanvas} ref={ref} />;
}

if (err) {
return <p className={styles.pdfPageError}>{err}</p>;
}

return <LoadingSvg />;
}
);

PageRendererInner.displayName = 'pdf-page-renderer-inner';
4 changes: 1 addition & 3 deletions packages/frontend/core/src/utils/clipboard/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,7 @@ export const copyLinkToBlockStdScopeClipboard = async (
) => {
let success = false;

if (!clipboard) return success;

if (clipboardWriteIsSupported) {
if (clipboardWriteIsSupported && clipboard) {
try {
await clipboard.writeToClipboard(items => {
items['text/plain'] = text;
Expand Down

0 comments on commit a72bac1

Please sign in to comment.