Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

7455 - PDF document scaling #7485

Draft
wants to merge 31 commits into
base: development
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
7347dc5
maintain scale of current selections
Zasa-san Nov 25, 2024
395ac3a
Merge branch 'development' into 7455-pdf-document-scaling
Zasa-san Nov 26, 2024
5208a26
Merge branch 'development' into 7455-pdf-document-scaling
Zasa-san Nov 26, 2024
5e62de4
stateful scaling + device zoom adjustments
Zasa-san Nov 26, 2024
877f388
Merge branch 'development' into 7455-pdf-document-scaling
Zasa-san Nov 26, 2024
e16d7d9
Merge branch 'development' into 7455-pdf-document-scaling
Zasa-san Nov 27, 2024
4c0630e
Merge branch 'development' into 7455-pdf-document-scaling
Zasa-san Nov 28, 2024
5144f2b
Merge branch 'development' into 7455-pdf-document-scaling
Zasa-san Nov 28, 2024
052d619
centered pdf with no scaling
Zasa-san Nov 28, 2024
f8dc69f
use container size for intial width
Zasa-san Nov 28, 2024
789fa8d
Merge branch 'development' into 7455-pdf-document-scaling
Zasa-san Dec 2, 2024
5500d50
final scale adjustments on the sidepanel
Zasa-san Dec 2, 2024
6e5e88c
better scalling + atom
Zasa-san Dec 2, 2024
08458b4
test scaling helper
Zasa-san Dec 2, 2024
6517764
fix scaling on entity view
Zasa-san Dec 2, 2024
c7d13a3
Merge branch 'development' into 7455-pdf-document-scaling
Zasa-san Dec 3, 2024
ebf1abc
better scalling calculation
Zasa-san Dec 3, 2024
270924e
move necessary styles to sheet
Zasa-san Dec 3, 2024
cf1ba0b
Merge branch 'development' into 7455-pdf-document-scaling
Zasa-san Dec 4, 2024
3e83783
Merge branch '7455-pdf-document-scaling' of https://github.com/hurido…
Zasa-san Dec 4, 2024
2eddb48
minimal scaling
Zasa-san Dec 5, 2024
32fc6f8
update spec
Zasa-san Dec 5, 2024
487f399
refactor + scale new selections
Zasa-san Dec 5, 2024
78f7388
type update + metadata extractor new selections
Zasa-san Dec 9, 2024
1060230
make excepition for empty rectangles
Zasa-san Dec 9, 2024
0867bd1
refactor and test handler
Zasa-san Dec 9, 2024
4f11999
adjust existing selections in main pdf view
Zasa-san Dec 9, 2024
a87d8c5
cover with snaptshot
Zasa-san Dec 9, 2024
911ebb3
Merge branch 'development' into 7455-pdf-document-scaling
Zasa-san Dec 10, 2024
4e785d2
display existing references
Zasa-san Dec 10, 2024
f2006de
handle new references
Zasa-san Dec 10, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions app/react/App/scss/elements/_pdfViewer.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#pdf-container {
& .canvasWrapper {
& canvas {
margin: 0;
display: block;
width: 100%;
height: 100%;
}
}
}
1 change: 1 addition & 0 deletions app/react/App/scss/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
@import 'elements/item';
@import 'elements/linkField';
@import 'elements/panel';
@import 'elements/pdfViewer';
@import 'elements/breadcrumbs';
@import 'elements/draggable';
@import 'elements/dropdown';
Expand Down
20 changes: 15 additions & 5 deletions app/react/Metadata/components/MetadataExtractor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ import React from 'react';
import { bindActionCreators, Dispatch } from 'redux';
import { connect, ConnectedProps } from 'react-redux';
import { ModelAction } from 'react-redux-form';
import { useAtomValue } from 'jotai';
import { Icon } from 'app/UI';
import { IStore } from 'app/istore';
import { t, Translate } from 'app/I18N';
import { notificationActions } from 'app/Notifications';
import { SelectionRectanglesSchema } from 'shared/types/commonTypes';
import { pdfScaleAtom } from 'V2/atoms';
import { selectionHandlers } from 'V2/Components/PDFViewer';
import { updateSelection, updateFormField } from '../actions/metadataExtractionActions';

type OwnPropTypes = {
Expand All @@ -17,15 +20,15 @@ type OwnPropTypes = {
locale?: string;
};

type selection = {
type Selection = {
text: string;
selectionRectangles: SelectionRectanglesSchema;
};

const mapStateToProps = (state: IStore) => ({
selection: state.documentViewer.uiState
.get('reference')
.get('sourceRange') as unknown as selection,
.get('sourceRange') as unknown as Selection,
});

const mapDispatchToProps = (dispatch: Dispatch<{}>, ownProps: OwnPropTypes) => {
Expand Down Expand Up @@ -53,15 +56,22 @@ const MetadataExtractorComponent = ({
updateField,
notify,
}: mappedProps) => {
const pdfScaling = useAtomValue(pdfScaleAtom);

const onClick = async () => {
if (!selection.selectionRectangles?.length) {
notify(
t('System', 'Could not detect the area for the selected text', null, false),
'warning'
);
}
setSelection(selection);
updateField(selection.text);

const selected = selection.selectionRectangles?.length
? selectionHandlers.adjustSelectionsToScale(selection, pdfScaling, true)
: selection;
setSelection(selected);

updateField(selected.text);
};

if (!selection) {
Expand All @@ -79,5 +89,5 @@ const MetadataExtractorComponent = ({

const container = connector(MetadataExtractorComponent);

export type { selection };
export type { Selection };
export { container as MetadataExtractor };
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import { screen, act, fireEvent } from '@testing-library/react';
import { notificationActions } from 'app/Notifications';
import { defaultState, renderConnectedContainer } from 'app/utils/test/renderConnected';
import * as actions from '../../actions/metadataExtractionActions';
import { MetadataExtractor, selection } from '../MetadataExtractor';
import { MetadataExtractor, Selection } from '../MetadataExtractor';

describe('MetadataExtractor', () => {
let selected: selection | undefined;
let selected: Selection | undefined;

beforeEach(() => {
spyOn(actions, 'updateSelection').and.returnValue(() => {});
Expand Down
2 changes: 2 additions & 0 deletions app/react/PDF/components/PDF.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ class PDF extends Component {
this.pdfContainer = ref;
}}
style={this.props.style}
id="pdf-container"
>
<HandleTextSelection
onSelect={this.props.onTextSelection}
Expand All @@ -172,6 +173,7 @@ class PDF extends Component {
page={page}
pdf={this.state.pdf}
highlightReference={this.props.highlightReference}
containerWidth={this.pdfContainer.clientWidth}
/>
</SelectionRegion>
</div>
Expand Down
20 changes: 17 additions & 3 deletions app/react/PDF/components/PDFPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import React, { Component } from 'react';
import { isClient } from 'app/utils';
import { PageReferences } from 'app/Viewer/components/PageReferences';
import { PageSelections } from 'app/Viewer/components/PageSelections';
import { calculateScaling } from 'V2/Components/PDFViewer';
import { atomStore, pdfScaleAtom } from 'V2/atoms';
import PDFJS, { EventBus } from '../PDFJS';

class PDFPage extends Component {
Expand Down Expand Up @@ -112,13 +114,24 @@ class PDFPage extends Component {
this.props.onLoading(this.props.page);
this.setState({ rendered: true });
this.props.pdf.getPage(this.props.page).then(page => {
const scale = 1;
const defaultViewport = page.getViewport({ scale: 1 });
const { devicePixelRatio } = window;

const adjustedScale = calculateScaling(
devicePixelRatio,
defaultViewport.width,
this.props.containerWidth
);

const adjustedViewport = page.getViewport({ scale: adjustedScale });

atomStore.set(pdfScaleAtom, adjustedScale);

this.pdfPageView = new PDFJS.PDFPageView({
container: this.pageContainer,
id: this.props.page,
scale,
defaultViewport: page.getViewport({ scale }),
scale: adjustedScale,
defaultViewport: adjustedViewport,
textLayerMode: 1,
eventBus: new EventBus(),
});
Expand Down Expand Up @@ -172,6 +185,7 @@ PDFPage.propTypes = {
onUnload: PropTypes.func.isRequired,
pdf: PropTypes.object.isRequired,
highlightReference: PropTypes.func,
containerWidth: PropTypes.number.isRequired,
};

export default PDFPage;
15 changes: 11 additions & 4 deletions app/react/V2/Components/PDFViewer/PDF.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const PDF = ({
size,
}: PDFProps) => {
const scrollToRef = useRef<HTMLDivElement>(null);
const pdfContainerRef = useRef<HTMLDivElement>(null);
const [pdf, setPDF] = useState<PDFDocumentProxy>();
const [error, setError] = useState<string>();

Expand Down Expand Up @@ -84,9 +85,10 @@ const PDF = ({
<HandleTextSelection onSelect={onSelect} onDeselect={onDeselect}>
<div
id="pdf-container"
ref={pdfContainerRef}
style={{
height: size?.height || 'auto',
width: size?.width || 'auto',
height: size?.height || '100%',
width: size?.width || '100%',
overflow: size?.overflow || 'auto',
padding: '10px',
}}
Expand All @@ -99,12 +101,17 @@ const PDF = ({
return (
<div
key={`page-${regionId}`}
className="relative"
className="w-fit m-auto relative"
id={`page-${regionId}-container`}
ref={shouldScrollToPage ? scrollToRef : undefined}
>
<SelectionRegion regionId={regionId}>
<PDFPage pdf={pdf} page={number} highlights={pageHighlights} />
<PDFPage
pdf={pdf}
page={number}
highlights={pageHighlights}
containerWidth={pdfContainerRef.current?.clientWidth}
/>
</SelectionRegion>
</div>
);
Expand Down
48 changes: 35 additions & 13 deletions app/react/V2/Components/PDFViewer/PDFPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,26 @@
import React, { useEffect, useRef, useState } from 'react';
import { PDFDocumentProxy, PDFPageProxy } from 'pdfjs-dist';
import { Highlight } from '@huridocs/react-text-selection-handler';
import { useAtom } from 'jotai';
import { pdfScaleAtom } from 'V2/atoms';
import { EventBus, PDFJSViewer } from './pdfjs';
import { TextHighlight } from './types';
import { calculateScaling } from './functions/calculateScaling';
import { adjustSelectionsToScale } from './functions/handleTextSelection';

interface PDFPageProps {
pdf: PDFDocumentProxy;
page: number;
highlights?: TextHighlight[];
containerWidth?: number;
}

const PDFPage = ({ pdf, page, highlights }: PDFPageProps) => {
const PDFPage = ({ pdf, page, containerWidth, highlights }: PDFPageProps) => {
const pageContainerRef = useRef<HTMLDivElement>(null);
const [isVisible, setIsVisible] = useState(false);
const [pdfPage, setPdfPage] = useState<PDFPageProxy>();
const [error, setError] = useState<string>();
const [scale, setScale] = useAtom(pdfScaleAtom);

useEffect(() => {
pdf
Expand Down Expand Up @@ -58,16 +64,26 @@ const PDFPage = ({ pdf, page, highlights }: PDFPageProps) => {
currentContainer.style.height = `${defaultViewport.height}px`;
};

const { devicePixelRatio } = window;
const adjustedScale = calculateScaling(
devicePixelRatio,
defaultViewport.width,
containerWidth
);

setScale(adjustedScale);

const adjustedViewport = pdfPage.getViewport({ scale: adjustedScale });

if (isVisible) {
const pageViewer = new PDFJSViewer.PDFPageView({
container: currentContainer,
id: page,
scale: 1,
defaultViewport,
scale: adjustedScale,
defaultViewport: adjustedViewport,
annotationMode: 0,
eventBus: new EventBus(),
});

pageViewer.setPdfPage(pdfPage);
currentContainer.style.height = 'auto';
pageViewer.draw().catch((e: Error) => setError(e.message));
Expand All @@ -77,22 +93,28 @@ const PDFPage = ({ pdf, page, highlights }: PDFPageProps) => {
handlePlaceHolder();
}
}
}, [isVisible, page, pdfPage]);
}, [isVisible, scale, page, pdfPage, setScale, containerWidth]);

if (error) {
return <div>{error}</div>;
}

return (
<div ref={pageContainerRef}>
<div ref={pageContainerRef} style={{ width: '100%' }}>
{isVisible &&
highlights?.map(highlight => (
<Highlight
key={highlight.key}
textSelection={highlight.textSelection}
color={highlight.color}
/>
))}
highlights?.map(highlight => {
const scaledHightlight = {
...highlight,
textSelection: adjustSelectionsToScale(highlight.textSelection, scale),
};
return (
<Highlight
key={scaledHightlight.key}
textSelection={scaledHightlight.textSelection}
color={scaledHightlight.color}
/>
);
})}
</div>
);
};
Expand Down
27 changes: 27 additions & 0 deletions app/react/V2/Components/PDFViewer/functions/calculateScaling.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
const calculateScaling = (
devicePixelRatio: number,
pageWidth: number,
parentWidth: number | undefined
) => {
let widthRatio = 1;
let adjustedScale = 1;
let minScale = 0.5;

if (parentWidth) {
widthRatio = Math.max(parentWidth / pageWidth, 1);

if (parentWidth < 500) {
minScale = 0.8;
}
}

if (devicePixelRatio >= 1) {
adjustedScale = Math.min(1, widthRatio / devicePixelRatio);
} else {
adjustedScale = widthRatio / (1 + devicePixelRatio);
}

return Math.max(adjustedScale, minScale);
};

export { calculateScaling };
31 changes: 31 additions & 0 deletions app/react/V2/Components/PDFViewer/functions/handleTextSelection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,9 +184,40 @@ const deleteFileSelection = (
return updatedSelections;
};

const adjustSelectionsToScale = (
selection: TextSelection,
scalingFactor: number,
normalize?: boolean
): TextSelection => {
if (scalingFactor === 1) {
return selection;
}

const scaledSelection = { ...selection };

if (scaledSelection.selectionRectangles.length) {
scaledSelection.selectionRectangles = selection.selectionRectangles.map(rectangle => {
const left = rectangle.left || 0;
const top = rectangle.top || 0;
const width = rectangle.width || 0;
const height = rectangle.height || 0;
return {
...rectangle,
left: normalize ? left / scalingFactor : left * scalingFactor,
top: normalize ? top / scalingFactor : top * scalingFactor,
width: normalize ? width / scalingFactor : width * scalingFactor,
height: normalize ? height / scalingFactor : height * scalingFactor,
};
});
}

return scaledSelection;
};

export {
getHighlightsFromFile,
getHighlightsFromSelection,
updateFileSelection,
deleteFileSelection,
adjustSelectionsToScale,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { calculateScaling } from '../calculateScaling';

describe('pdf scaling calculation', () => {
describe('device scaling is greater or equal to 1', () => {
it('shoud return a scaling of 1 when the width ratio is below 1', () => {
const result = calculateScaling(1.8, 5, 10);
expect(result).toEqual(1);
});

it('should return the scaling if the width ratio is above 1', () => {
const result = calculateScaling(3, 450, 500);
expect(result).toEqual(0.5);
});
});

describe('device scaling is less than 1', () => {
it('should return a ratio based on the device scaling', () => {
const result = calculateScaling(0.6, 12, 24);
expect(result).toEqual(1.25);
});
});

it('should have a minimun scaling', () => {
let result = calculateScaling(0.4, 50, 10);
expect(result).toEqual(0.8);

result = calculateScaling(0.4, 500, 10);
expect(result).toEqual(0.8);
});
});
Loading
Loading