1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-21 09:38:01 +02:00

Desktop: Added PDF viewer options (#6800)

This commit is contained in:
asrient 2022-09-05 17:05:38 +05:30 committed by GitHub
parent eb7083d788
commit 1504cb71ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 310 additions and 145 deletions

View File

@ -2001,6 +2001,9 @@ packages/lib/versionInfo.js.map
packages/pdf-viewer/Page.d.ts
packages/pdf-viewer/Page.js
packages/pdf-viewer/Page.js.map
packages/pdf-viewer/PdfDocument.d.ts
packages/pdf-viewer/PdfDocument.js
packages/pdf-viewer/PdfDocument.js.map
packages/pdf-viewer/VerticalPages.d.ts
packages/pdf-viewer/VerticalPages.js
packages/pdf-viewer/VerticalPages.js.map
@ -2010,9 +2013,9 @@ packages/pdf-viewer/hooks/useIsFocused.js.map
packages/pdf-viewer/hooks/useIsVisible.d.ts
packages/pdf-viewer/hooks/useIsVisible.js
packages/pdf-viewer/hooks/useIsVisible.js.map
packages/pdf-viewer/hooks/usePdfData.d.ts
packages/pdf-viewer/hooks/usePdfData.js
packages/pdf-viewer/hooks/usePdfData.js.map
packages/pdf-viewer/hooks/usePdfDocument.d.ts
packages/pdf-viewer/hooks/usePdfDocument.js
packages/pdf-viewer/hooks/usePdfDocument.js.map
packages/pdf-viewer/hooks/useScaledSize.d.ts
packages/pdf-viewer/hooks/useScaledSize.js
packages/pdf-viewer/hooks/useScaledSize.js.map
@ -2025,12 +2028,15 @@ packages/pdf-viewer/main.js.map
packages/pdf-viewer/miniViewer.d.ts
packages/pdf-viewer/miniViewer.js
packages/pdf-viewer/miniViewer.js.map
packages/pdf-viewer/pdfSource.d.ts
packages/pdf-viewer/pdfSource.js
packages/pdf-viewer/pdfSource.js.map
packages/pdf-viewer/pdfSource.test.d.ts
packages/pdf-viewer/pdfSource.test.js
packages/pdf-viewer/pdfSource.test.js.map
packages/pdf-viewer/types.d.ts
packages/pdf-viewer/types.js
packages/pdf-viewer/types.js.map
packages/pdf-viewer/ui/IconButtons.d.ts
packages/pdf-viewer/ui/IconButtons.js
packages/pdf-viewer/ui/IconButtons.js.map
packages/pdf-viewer/ui/ZoomControls.d.ts
packages/pdf-viewer/ui/ZoomControls.js
packages/pdf-viewer/ui/ZoomControls.js.map

18
.gitignore vendored
View File

@ -1989,6 +1989,9 @@ packages/lib/versionInfo.js.map
packages/pdf-viewer/Page.d.ts
packages/pdf-viewer/Page.js
packages/pdf-viewer/Page.js.map
packages/pdf-viewer/PdfDocument.d.ts
packages/pdf-viewer/PdfDocument.js
packages/pdf-viewer/PdfDocument.js.map
packages/pdf-viewer/VerticalPages.d.ts
packages/pdf-viewer/VerticalPages.js
packages/pdf-viewer/VerticalPages.js.map
@ -1998,9 +2001,9 @@ packages/pdf-viewer/hooks/useIsFocused.js.map
packages/pdf-viewer/hooks/useIsVisible.d.ts
packages/pdf-viewer/hooks/useIsVisible.js
packages/pdf-viewer/hooks/useIsVisible.js.map
packages/pdf-viewer/hooks/usePdfData.d.ts
packages/pdf-viewer/hooks/usePdfData.js
packages/pdf-viewer/hooks/usePdfData.js.map
packages/pdf-viewer/hooks/usePdfDocument.d.ts
packages/pdf-viewer/hooks/usePdfDocument.js
packages/pdf-viewer/hooks/usePdfDocument.js.map
packages/pdf-viewer/hooks/useScaledSize.d.ts
packages/pdf-viewer/hooks/useScaledSize.js
packages/pdf-viewer/hooks/useScaledSize.js.map
@ -2013,12 +2016,15 @@ packages/pdf-viewer/main.js.map
packages/pdf-viewer/miniViewer.d.ts
packages/pdf-viewer/miniViewer.js
packages/pdf-viewer/miniViewer.js.map
packages/pdf-viewer/pdfSource.d.ts
packages/pdf-viewer/pdfSource.js
packages/pdf-viewer/pdfSource.js.map
packages/pdf-viewer/pdfSource.test.d.ts
packages/pdf-viewer/pdfSource.test.js
packages/pdf-viewer/pdfSource.test.js.map
packages/pdf-viewer/types.d.ts
packages/pdf-viewer/types.js
packages/pdf-viewer/types.js.map
packages/pdf-viewer/ui/IconButtons.d.ts
packages/pdf-viewer/ui/IconButtons.js
packages/pdf-viewer/ui/IconButtons.js.map
packages/pdf-viewer/ui/ZoomControls.d.ts
packages/pdf-viewer/ui/ZoomControls.js
packages/pdf-viewer/ui/ZoomControls.js.map

View File

@ -1,7 +1,8 @@
import { useEffect, useRef, useState, MutableRefObject } from 'react';
import * as React from 'react';
import useIsVisible from './hooks/useIsVisible';
import { PdfData, ScaledSize } from './pdfSource';
import PdfDocument from './PdfDocument';
import { ScaledSize } from './types';
import useAsyncEffect, { AsyncEffectEvent } from '@joplin/lib/hooks/useAsyncEffect';
import styled from 'styled-components';
@ -35,7 +36,7 @@ const PageInfo = styled.div`
`;
export interface PageProps {
pdf: PdfData;
pdfDocument: PdfDocument;
pageNo: number;
focusOnLoad: boolean;
isAnchored: boolean;
@ -87,9 +88,9 @@ export default function Page(props: PageProps) {
}, [page, props.scaledSize, isVisible, props.pageNo]);
useAsyncEffect(async (event: AsyncEffectEvent) => {
if (page || !isVisible || !props.pdf) return;
if (page || !isVisible || !props.pdfDocument) return;
try {
const _page = await props.pdf.getPage(props.pageNo);
const _page = await props.pdfDocument.getPage(props.pageNo);
if (event.cancelled) return;
setPage(_page);
} catch (error) {

View File

@ -1,13 +1,7 @@
import * as pdfjsLib from 'pdfjs-dist';
import { ScaledSize } from './types';
export interface ScaledSize {
height: number;
width: number;
scale: number;
}
export class PdfData {
export default class PdfDocument {
public url: string | Uint8Array;
private doc: any = null;
public pageCount: number = null;
@ -16,8 +10,10 @@ export class PdfData {
height: number;
width: number;
} = null;
private document: HTMLDocument = null;
public constructor() {
public constructor(document: HTMLDocument) {
this.document = document;
}
public loadDoc = async (url: string | Uint8Array) => {
@ -71,4 +67,36 @@ export class PdfData {
scale,
};
};
public getActivePageNo = (scaledSize: ScaledSize, pageGap: number, scrollTop: number): number => {
const pageHeight = scaledSize.height + pageGap;
const pageNo = Math.floor(scrollTop / pageHeight) + 1;
return Math.min(pageNo, this.pageCount);
};
public printPdf = () => {
const frame = this.document.createElement('iframe');
frame.style.position = 'fixed';
frame.style.display = 'none';
frame.style.height = '100%';
frame.style.width = '100%';
this.document.body.appendChild(frame);
frame.onload = () => {
frame.contentWindow.onafterprint = () => {
frame.remove();
};
frame.focus();
frame.contentWindow.print();
};
frame.src = this.url as string;
};
public downloadPdf = async () => {
const url = this.url as string;
const link = this.document.createElement('a');
link.href = url;
link.download = url;
link.click();
link.remove();
};
}

View File

@ -1,6 +1,6 @@
import { useEffect, useRef, useState, MutableRefObject } from 'react';
import * as React from 'react';
import { PdfData } from './pdfSource';
import PdfDocument from './PdfDocument';
import Page from './Page';
import styled from 'styled-components';
import useScaledSize, { ScaledSizeParams } from './hooks/useScaledSize';
@ -14,20 +14,21 @@ const PagesHolder = styled.div<{ pageGap: number }>`
flex-flow: column;
width: fit-content;
min-width: 100%;
min-height: 100%;
min-height: fit-content;
row-gap: ${(props)=> props.pageGap || 2}px;
`;
export interface VerticalPagesProps {
pdf: PdfData;
pdfDocument: PdfDocument;
isDarkTheme: boolean;
anchorPage?: number;
rememberScroll?: boolean;
pdfId?: string;
zoom?: number;
container: MutableRefObject<HTMLElement>;
pageGap?: number;
pageGap: number;
showPageNumbers?: boolean;
onActivePageChange: (page: number)=> void;
}
export default function VerticalPages(props: VerticalPagesProps) {
@ -35,7 +36,7 @@ export default function VerticalPages(props: VerticalPagesProps) {
const innerContainerEl = useRef<HTMLDivElement>(null);
const scaledSize = useScaledSize({
pdf: props.pdf,
pdfDocument: props.pdfDocument,
pdfId: props.pdfId,
containerWidth,
rememberScroll: props.rememberScroll,
@ -51,6 +52,9 @@ export default function VerticalPages(props: VerticalPagesProps) {
scaledSize,
pdfId: props.pdfId,
rememberScroll: props.rememberScroll,
pdfDocument: props.pdfDocument,
pageGap: props.pageGap,
onActivePageChange: props.onActivePageChange,
} as ScrollSaver);
useEffect(() => {
@ -81,13 +85,13 @@ export default function VerticalPages(props: VerticalPagesProps) {
resizeTimer = null;
}
};
}, [props.container, props.pdf]);
}, [props.container, props.pdfDocument]);
return (<PagesHolder pageGap={props.pageGap || 2} ref={innerContainerEl} >
{scaledSize ?
Array.from(Array(props.pdf.pageCount).keys()).map((i: number) => {
Array.from(Array(props.pdfDocument.pageCount).keys()).map((i: number) => {
// setting focusOnLoad only after scaledSize is set so that the container height is set correctly
return <Page pdf={props.pdf} pageNo={i + 1} focusOnLoad={scaledSize && props.anchorPage && props.anchorPage === i + 1}
return <Page pdfDocument={props.pdfDocument} pageNo={i + 1} focusOnLoad={scaledSize && props.anchorPage && props.anchorPage === i + 1}
isAnchored={props.anchorPage && props.anchorPage === i + 1}
showPageNumbers={props.showPageNumbers}
isDarkTheme={props.isDarkTheme} scaledSize={scaledSize} container={props.container} key={i} />;

View File

@ -19,7 +19,7 @@
}
::-webkit-scrollbar-thumb {
background: var(--grey);
background: rgb(112, 112, 112);
border-radius: 5px;
}
@ -27,7 +27,7 @@
--white: rgb(255, 255, 255);
--light: rgb(219, 219, 219);
--grey: rgb(128, 128, 128);
--dark: rgb(1, 0, 34);
--dark: rgb(38, 38, 38);
--black: rgb(24, 24, 24);
--red: #ff000d;
@ -77,68 +77,11 @@ hr {
overflow: visible;
}
.mini-app {
display: grid;
grid-template-rows: auto 2rem;
height: 100vh;
width: 100vw;
background-color: var(--bg);
overflow: hidden;
border-radius: 0.4rem;
border: solid thin var(--tertiary);
padding-top: 0.6rem;
padding-right: 0.25rem;
background-color: rgb(240, 241, 245);
.dark-bg{
background-color: rgb(171, 171, 171);
}
.mini-app.focused {
border: solid thin var(--grey);
}
.mini-app.loading {
display: flex;
justify-content: center;
align-items: center;
}
[data-theme="dark"] .mini-app {
[data-theme="dark"] .dark-bg {
background-color: rgb(54, 54, 54);
}
.app-bottom-bar {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
padding: 0.2rem 0.5rem;
font-size: 0.8rem;
color: var(--grey);
}
.app-pages {
display: block;
margin: 0px auto;
overflow-x: hidden;
width: 100%;
height: 100%;
padding: 0.5rem;
padding-top: 0px;
padding-bottom: 0px;
overflow-y: hidden;
padding-right: 0.25rem;
position: relative;
}
.app-pages.focused {
padding-right: 0px;
overflow-y: auto;
overflow-x: auto;
}
.pdf-info {
display: flex;
flex-direction: row;
justify-content: space-around;
align-items: center;
column-gap: 0.2rem;
}

View File

@ -1,19 +0,0 @@
import { useState } from 'react';
import useAsyncEffect, { AsyncEffectEvent } from '@joplin/lib/hooks/useAsyncEffect';
import { PdfData } from '../pdfSource';
const usePdfData = (pdfPath: string) => {
const [pdf, setPdf] = useState<PdfData>(null);
useAsyncEffect(async (event: AsyncEffectEvent) => {
const pdfData = new PdfData();
await pdfData.loadDoc(pdfPath);
if (event.cancelled) return;
setPdf(pdfData);
}, [pdfPath]);
return pdf;
};
export default usePdfData;

View File

@ -0,0 +1,19 @@
import { useState } from 'react';
import useAsyncEffect, { AsyncEffectEvent } from '@joplin/lib/hooks/useAsyncEffect';
import PdfDocument from '../PdfDocument';
const usePdfDocument = (pdfPath: string) => {
const [pdfDocument, setPdfDocument] = useState<PdfDocument>(null);
useAsyncEffect(async (event: AsyncEffectEvent) => {
const pdfData = new PdfDocument(document);
await pdfData.loadDoc(pdfPath);
if (event.cancelled) return;
setPdfDocument(pdfData);
}, [pdfPath]);
return pdfDocument;
};
export default usePdfDocument;

View File

@ -1,9 +1,10 @@
import { useRef, useState, MutableRefObject } from 'react';
import useAsyncEffect, { AsyncEffectEvent } from '@joplin/lib/hooks/useAsyncEffect';
import { ScaledSize, PdfData } from '../pdfSource';
import PdfDocument from '../PdfDocument';
import { ScaledSize } from '../types';
export interface ScaledSizeParams {
pdf: PdfData;
pdfDocument: PdfDocument;
pdfId: string;
containerWidth: number;
rememberScroll: boolean;
@ -14,21 +15,21 @@ export interface ScaledSizeParams {
zoom: number;
}
const useScaledSize = ({ pdf, pdfId, containerWidth, rememberScroll, anchorPage, container, innerContainerEl, pageGap, zoom }: ScaledSizeParams) => {
const useScaledSize = ({ pdfDocument, pdfId, containerWidth, rememberScroll, anchorPage, container, innerContainerEl, pageGap, zoom }: ScaledSizeParams) => {
const [scaledSize, setScaledSize] = useState<ScaledSize>(null);
const currentScaleSize = useRef(scaledSize);
useAsyncEffect(async (event: AsyncEffectEvent) => {
if (!pdf || !containerWidth) return;
if (!pdfDocument || !containerWidth) return;
// console.log('scaledSize calculation triggered');
const effectiveWidth = Math.min(containerWidth - 20, 900) * (zoom || 1);
const scaledSize_ = await pdf.getScaledSize(null, effectiveWidth);
const scaledSize_ = await pdfDocument.getScaledSize(null, effectiveWidth);
if (event.cancelled) return;
const oldScaleSize = currentScaleSize.current;
const oldScrollTop = container.current.scrollTop;
innerContainerEl.current.style.height = `${(scaledSize_.height + (pageGap || 2)) * pdf.pageCount}px`;
innerContainerEl.current.style.height = `${(scaledSize_.height + pageGap) * pdfDocument.pageCount}px`;
// Adjust scroll position after window resize to keep the same page visible
if (oldScaleSize && container.current) {
@ -47,7 +48,7 @@ const useScaledSize = ({ pdf, pdfId, containerWidth, rememberScroll, anchorPage,
// console.log('scroll set',container.current.scrollTop);
}
}
}, [pdf, pdfId, rememberScroll, anchorPage, containerWidth, zoom]);
}, [pdfDocument, pdfId, rememberScroll, anchorPage, containerWidth, zoom]);
return scaledSize;
};

View File

@ -1,15 +1,21 @@
import { useRef, useEffect, MutableRefObject } from 'react';
import { ScaledSize } from '../pdfSource';
import { useRef, useEffect, MutableRefObject, useState } from 'react';
import PdfDocument from '../PdfDocument';
import { ScaledSize } from '../types';
export interface ScrollSaver {
container: MutableRefObject<HTMLElement>;
scaledSize: ScaledSize;
pdfId: string;
rememberScroll: boolean;
onActivePageChange: (activePage: number)=> void;
pdfDocument: PdfDocument;
pageGap: number;
}
const useScrollSaver = ({ container, scaledSize, pdfId, rememberScroll }: ScrollSaver) => {
const useScrollSaver = ({ container, scaledSize, pdfId, rememberScroll, onActivePageChange, pdfDocument, pageGap }: ScrollSaver) => {
const currentScaleSize = useRef(scaledSize);
const [currentActivePage, setCurrentActivePage] = useState(1);
const currentActivePageRef = useRef(currentActivePage);
useEffect(() => {
let scrollTimer: number = null;
@ -18,9 +24,18 @@ const useScrollSaver = ({ container, scaledSize, pdfId, rememberScroll }: Scroll
const saveScroll = () => {
if (!currentScaleSize.current) return;
const scale = currentScaleSize.current.scale;
const scrollTop = container.current.scrollTop / scale;
const pdfScrollTop = container.current.scrollTop / scale;
if (rememberScroll && pdfId) {
sessionStorage.setItem(`pdf.${pdfId}.scrollTop`, `${scrollTop}`);
sessionStorage.setItem(`pdf.${pdfId}.scrollTop`, `${pdfScrollTop}`);
}
if (onActivePageChange && currentScaleSize.current) {
const activePage = pdfDocument.getActivePageNo(currentScaleSize.current, pageGap, container.current.scrollTop);
if (currentActivePageRef.current !== activePage) {
// console.log('Active page changed', activePage, container.current.scrollTop);
currentActivePageRef.current = activePage;
onActivePageChange(activePage);
setCurrentActivePage(activePage);
}
}
};
@ -41,13 +56,11 @@ const useScrollSaver = ({ container, scaledSize, pdfId, rememberScroll }: Scroll
scrollTimer = null;
}
};
}, [container, pdfId, rememberScroll, currentScaleSize]);
}, [container, pdfId, rememberScroll, currentScaleSize, onActivePageChange, pdfDocument, pageGap]);
useEffect(() => {
currentScaleSize.current = scaledSize;
} , [scaledSize]);
return scaledSize;
};
export default useScrollSaver;

View File

@ -5,7 +5,7 @@ import { render } from 'react-dom';
import * as pdfjsLib from 'pdfjs-dist';
import MiniViewerApp from './miniViewer';
require('./viewer.css');
require('./common.css');
// Setting worker path to worker bundle.
pdfjsLib.GlobalWorkerOptions.workerSrc = 'pdf.worker.js';
@ -13,14 +13,17 @@ pdfjsLib.GlobalWorkerOptions.workerSrc = 'pdf.worker.js';
const url = window.frameElement.getAttribute('x-url');
const type = window.frameElement.getAttribute('x-type');
const appearance = window.frameElement.getAttribute('x-appearance');
const anchorPage = window.frameElement.getAttribute('x-anchorPage');
const anchorPage = Number(window.frameElement.getAttribute('x-anchorPage')) || null;
const pdfId = window.frameElement.getAttribute('id');
document.documentElement.setAttribute('data-theme', appearance);
function App() {
if (type === 'mini') {
return <MiniViewerApp pdfPath={url} isDarkTheme={appearance === 'dark'} anchorPage={anchorPage ? Number(anchorPage) : null} pdfId={pdfId} />;
return <MiniViewerApp pdfPath={url}
isDarkTheme={appearance === 'dark'}
anchorPage={anchorPage}
pdfId={pdfId} />;
}
return <div>Error: Unknown app type "{type}"</div>;
}

View File

@ -0,0 +1,70 @@
.mini-app {
display: grid;
grid-template-rows: auto 2rem;
height: 100vh;
width: 100vw;
background-color: var(--bg);
overflow: hidden;
border-radius: 0.4rem;
border: solid thin var(--tertiary);
padding-top: 0.6rem;
padding-right: 0.25rem;
}
.mini-app.focused {
border: solid thin var(--grey);
}
.mini-app.loading {
display: flex;
justify-content: center;
align-items: center;
}
.app-bottom-bar {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
padding: 0.2rem 0.5rem;
font-size: 0.8rem;
color: var(--grey);
}
.app-pages {
display: block;
margin: 0px auto;
overflow-x: hidden;
width: 100%;
height: 100%;
padding: 0.5rem;
padding-top: 0px;
padding-bottom: 0px;
overflow-y: hidden;
padding-right: 0.25rem;
position: relative;
}
.app-pages.focused {
padding-right: 0px;
overflow-y: auto;
overflow-x: auto;
}
.pdf-info {
display: flex;
flex-direction: row;
justify-content: space-around;
align-items: center;
column-gap: 0.2rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
user-select: none;
}
@media screen and (max-width: 380px) {
.can-hide {
display: none;
}
}

View File

@ -1,8 +1,11 @@
import React, { useRef, useState } from 'react';
import React, { useRef, useState, useCallback } from 'react';
import useIsFocused from './hooks/useIsFocused';
import usePdfData from './hooks/usePdfData';
import usePdfDocument from './hooks/usePdfDocument';
import VerticalPages from './VerticalPages';
import ZoomControls from './ui/ZoomControls';
import { DownloadButton, PrintButton } from './ui/IconButtons';
require('./miniViewer.css');
export interface MiniViewerAppProps {
pdfPath: string;
@ -12,12 +15,17 @@ export interface MiniViewerAppProps {
}
export default function MiniViewerApp(props: MiniViewerAppProps) {
const pdf = usePdfData(props.pdfPath);
const pdfDocument = usePdfDocument(props.pdfPath);
const isFocused = useIsFocused();
const [zoom, setZoom] = useState<number>(1);
const containerEl = useRef<HTMLDivElement>(null);
const [activePage, setActivePage] = useState(1);
if (!pdf) {
const onActivePageChange = useCallback((page: number) => {
setActivePage(page);
}, []);
if (!pdfDocument) {
return (
<div className="mini-app loading">
<div>Loading pdf..</div>
@ -28,21 +36,25 @@ export default function MiniViewerApp(props: MiniViewerAppProps) {
<div className={`mini-app${isFocused ? ' focused' : ''}`}>
<div className={`app-pages${isFocused ? ' focused' : ''}`} ref={containerEl}>
<VerticalPages
pdf={pdf}
pdfDocument={pdfDocument}
isDarkTheme={props.isDarkTheme}
anchorPage={props.anchorPage}
pdfId={props.pdfId}
rememberScroll={true}
container={containerEl}
showPageNumbers={true}
zoom={zoom} />
zoom={zoom}
pageGap={2}
onActivePageChange={onActivePageChange} />
</div>
<div className='app-bottom-bar'>
<div className='pdf-info'>
<div style={{ paddingRight: '0.4rem' }}>{pdf.pageCount} pages</div>
<div style={{ paddingRight: '0.4rem' }}>{activePage}/{pdfDocument.pageCount} pages</div>
<ZoomControls onChange={setZoom} zoom={zoom} />
<PrintButton onClick={pdfDocument?.printPdf}/>
<DownloadButton onClick={pdfDocument?.downloadPdf}/>
</div>
<div>{isFocused ? '' : 'Click to enable scroll'}</div>
<div className="can-hide">{isFocused ? '' : 'Click to enable scroll'}</div>
</div>
</div>
);

View File

@ -1,4 +1,4 @@
import { PdfData } from './pdfSource';
import PdfDocument from './PdfDocument';
import * as pdfjsLib from 'pdfjs-dist';
import { readFile } from 'fs';
import { resolve } from 'path';
@ -24,13 +24,13 @@ describe('pdfData', () => {
test('Should have correct page count', async () => {
const file = await loadFile(pdfFilePath1);
const pdf = new PdfData();
const pdf = new PdfDocument(document);
await pdf.loadDoc(file);
expect(pdf.pageCount).toBe(1);
});
test('Should throw error on invalid file', async () => {
const pdf = new PdfData();
const pdf = new PdfDocument(document);
await expect(async () => {
await pdf.loadDoc('');
}).rejects.toThrowError();
@ -38,7 +38,7 @@ describe('pdfData', () => {
test('Should get correct page size', async () => {
const file = await loadFile(pdfFilePath1);
const pdf = new PdfData();
const pdf = new PdfDocument(document);
await pdf.loadDoc(file);
const size = await pdf.getPageSize();
expect(size.height).toBeCloseTo(841.91998);
@ -47,10 +47,21 @@ describe('pdfData', () => {
test('Should calculate scaled size', async () => {
const file = await loadFile(pdfFilePath1);
const pdf = new PdfData();
const pdf = new PdfDocument(document);
await pdf.loadDoc(file);
const scaledSize = await pdf.getScaledSize(null, 200);
expect(scaledSize.scale).toBeCloseTo(0.336157);
});
test('Should get correct active page', async () => {
const file = await loadFile(pdfFilePath1);
const pdf = new PdfDocument(document);
await pdf.loadDoc(file);
const scaledSize = await pdf.getScaledSize(null, 200);
const activePage = pdf.getActivePageNo(scaledSize, 3, 0);
expect(activePage).toBe(1);
const activePage2 = pdf.getActivePageNo(scaledSize, 4, 8000);
expect(activePage2).toBe(1);
});
});

View File

@ -0,0 +1,11 @@
export interface ScaledSize {
height: number;
width: number;
scale: number;
}
export interface IconButtonProps {
onClick: ()=> void;
size?: number;
color?: string;
}

View File

@ -0,0 +1,56 @@
import React from 'react';
import styled from 'styled-components';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faPrint, faDownload, IconDefinition } from '@fortawesome/free-solid-svg-icons';
import { IconButtonProps } from '../types';
const ButtonElement = styled.button<{ hoverColor?: string; size?: number; color?: string }>`
padding: 0.2rem 0.7rem;
cursor: pointer;
border: solid thin transparent;
border-radius: 5px;
user-select: none;
display: flex;
justify-content: center;
align-items: center;
font-size: ${props=> props.size || 1}rem;
background: transparent;
color: var(--${props=>props.color || 'grey'});
&:hover {
background: #7676764d;
${props=>props.hoverColor && `color: var(--${props.hoverColor})`}
}
`;
interface BaseButtonProps {
icon: IconDefinition;
onClick: ()=> void;
name: string;
size: number;
color: string;
hoverColor?: string;
}
function BaseButton({ onClick, icon, name, size, color, hoverColor }: BaseButtonProps) {
return (
<ButtonElement onClick={onClick} title={name}
color={color}
size={size}
hoverColor={hoverColor || 'secondary'}>
<FontAwesomeIcon icon={icon} />
</ButtonElement>
);
}
export function DownloadButton({ onClick, size, color }: IconButtonProps) {
return (
<BaseButton onClick={onClick} icon={faDownload} name='Download' size={size} color={color} />
);
}
export function PrintButton({ onClick, size, color }: IconButtonProps) {
return (
<BaseButton onClick={onClick} icon={faPrint} name='Print' size={size} color={color} />
);
}