Skip to content

Commit

Permalink
Add square annotation editor
Browse files Browse the repository at this point in the history
  • Loading branch information
gpalsingh committed Apr 11, 2024
1 parent e3bcf0e commit 622776c
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 20 deletions.
4 changes: 2 additions & 2 deletions packages/react-pdf/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@commutatus/react-pdf",
"version": "8.0.6",
"version": "8.0.7",
"description": "Display PDFs in your React app as easily as if they were images.",
"type": "module",
"sideEffects": [
Expand Down Expand Up @@ -61,7 +61,7 @@
},
"license": "MIT",
"dependencies": {
"@commutatus/pdfjs-dist": "^5.0.2",
"@commutatus/pdfjs-dist": "5.0.3",
"clsx": "^2.0.0",
"dequal": "^2.0.3",
"make-cancellable-promise": "^1.3.1",
Expand Down
61 changes: 53 additions & 8 deletions packages/react-pdf/src/Document.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import React, {
useImperativeHandle,
useMemo,
useRef,
useState,
} from 'react';
import PropTypes from 'prop-types';
import makeEventProps from 'make-event-props';
Expand Down Expand Up @@ -123,6 +124,8 @@ export type DocumentProps = {
*/
highlightEditorColors?: HighlightEditorColorsType;
defaultHighlightColor?: string;
defaultSquareFillColor?: string;
defaultSquareOpacity?: string;
/**
* The path used to prefix the src attributes of annotation SVGs.
*
Expand Down Expand Up @@ -286,6 +289,8 @@ const Document = forwardRef(function Document(
file,
highlightEditorColors,
defaultHighlightColor,
defaultSquareFillColor,
defaultSquareOpacity,
inputRef,
imageResourcesPath,
loading = 'Loading PDF…',
Expand Down Expand Up @@ -315,6 +320,7 @@ const Document = forwardRef(function Document(
const eventBus = useRef(new EventBus());
const defaultInputRef = useRef<HTMLDivElement>(null);
const mainContainerRef = inputRef || defaultInputRef;
const [globalScale, setGlobalScale] = useState<null | number>(null);

const linkService = useRef(new LinkService());

Expand All @@ -323,6 +329,16 @@ const Document = forwardRef(function Document(
const prevFile = useRef<File>();
const prevOptions = useRef<Options>();

useEffect(() => {
if (globalScale && (mainContainerRef as any)?.current) {
eventBus.current.dispatch('scalechanging', {
source: (mainContainerRef as any).current,
scale: globalScale,
presetValue: 'auto',
});
}
}, [globalScale, mainContainerRef]);

useEffect(() => {
if (file && file !== prevFile.current && isParameterObject(file)) {
warning(
Expand Down Expand Up @@ -513,14 +529,41 @@ const Document = forwardRef(function Document(
[source],
);

useEffect(() => {
if (defaultHighlightColor && annotationEditorUiManager) {
annotationEditorUiManager.updateParams(
pdfjs.AnnotationEditorParamsType.HIGHLIGHT_DEFAULT_COLOR,
defaultHighlightColor,
);
}
}, [defaultHighlightColor, annotationEditorUiManager]);
useEffect(
function updateDefaultHighlightColor() {
if (defaultHighlightColor && annotationEditorUiManager) {
annotationEditorUiManager.updateParams(
pdfjs.AnnotationEditorParamsType.HIGHLIGHT_DEFAULT_COLOR,
defaultHighlightColor,
);
}
},
[defaultHighlightColor, annotationEditorUiManager],
);

useEffect(
function updateDefaultSquareOpacity() {
if (defaultSquareOpacity && annotationEditorUiManager) {
annotationEditorUiManager.updateParams(
pdfjs.AnnotationEditorParamsType.SQUARE_OPACITY,
defaultSquareOpacity,
);
}
},
[defaultSquareOpacity, annotationEditorUiManager],
);

useEffect(
function updateDefaultSquareFillColor() {
if (defaultSquareFillColor && annotationEditorUiManager) {
annotationEditorUiManager.updateParams(
pdfjs.AnnotationEditorParamsType.SQUARE_COLOR,
defaultSquareFillColor,
);
}
},
[defaultSquareFillColor, annotationEditorUiManager],
);

function createAnnotationEditorUiManager() {
const colorPickerOptions = getHighlightColorsString(highlightEditorColors);
Expand Down Expand Up @@ -671,6 +714,7 @@ const Document = forwardRef(function Document(
registerPage,
renderMode,
rotate,
setGlobalScale,
unregisterPage,
}),
[
Expand All @@ -681,6 +725,7 @@ const Document = forwardRef(function Document(
pdf,
renderMode,
rotate,
setGlobalScale,
],
);

Expand Down
23 changes: 18 additions & 5 deletions packages/react-pdf/src/Page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,7 @@ const Page: React.FC<PageProps> = function Page(props) {
renderTextLayer: renderTextLayerProps = true,
rotate: rotateProps,
scale: scaleProps = defaultScale,
setGlobalScale,
unregisterPage,
width,
...otherProps
Expand All @@ -384,13 +385,15 @@ const Page: React.FC<PageProps> = function Page(props) {

const rotate = rotateProps ?? (page ? page.rotate : null);

const scale = useMemo(() => {
const { scale, pdfjsInternalScale } = useMemo(() => {
if (!page) {
return null;
// TODO: Fix this workaround and use a single scale value
return { scale: null, pdfjsInternalScale: null };
}

// Be default, we'll render page at 100% * scale width.
let pageScale = 1;
let pageScale = 1,
pdfjsInternalScale = 1;

// Passing scale explicitly null would cause the page not to render
const scaleWithDefault = scaleProps ?? defaultScale;
Expand All @@ -399,13 +402,17 @@ const Page: React.FC<PageProps> = function Page(props) {
if (width || height) {
const viewport = page.getViewport({ scale: 1, rotation: rotate as number });
if (width) {
// TODO: Fix hardcoded values
const pageBaseWidth = 816;
const sidebarWidth = 0;
pdfjsInternalScale = (width - sidebarWidth) / pageBaseWidth;
pageScale = width / viewport.width;
} else if (height) {
pageScale = height / viewport.height;
}
}

return scaleWithDefault * pageScale;
return { scale: scaleWithDefault * pageScale, pdfjsInternalScale };
}, [height, page, rotate, scaleProps, width]);

const drawLayer = useMemo(() => {
Expand Down Expand Up @@ -436,6 +443,12 @@ const Page: React.FC<PageProps> = function Page(props) {

useEffect(hook, [_enableRegisterUnregisterPage, pdf, pageIndex, unregisterPage]);

useEffect(() => {
if (pdfjsInternalScale && setGlobalScale) {
setGlobalScale(pdfjsInternalScale);
}
}, [pdfjsInternalScale, setGlobalScale]);

useEffect(
function updateAnnotationEditorUiManagerPage() {
if (annotationEditorUiManager) {
Expand Down Expand Up @@ -703,7 +716,7 @@ const Page: React.FC<PageProps> = function Page(props) {
data-page-number={pageNumber}
ref={mergeRefs(inputRef, pageElement)}
style={{
['--scale-factor' as string]: `${scale}`,
['--scale-factor' as string]: `${(pdfjsInternalScale || 1) * pdfjs.PixelsPerInch.PDF_TO_CSS_UNITS}`,
backgroundColor: canvasBackground || 'white',
position: 'relative',
minWidth: 'min-content',
Expand Down
20 changes: 15 additions & 5 deletions packages/react-pdf/src/Page/AnnotationEditorLayer.css
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,10 @@
--editorFreeText-editing-cursor: text;
/*#if COMPONENTS*/
--editorInk-editing-cursor: pointer;
--editorSquare-editing-cursor: pointer;
/*#else*/
--editorInk-editing-cursor: svg-load(../images/cursor-editorInk.svg) 0 16, pointer;
--editorSquare-editing-cursor: svg-load(../images/cursor-editorSquare.svg) 25 25, pointer;
/*#endif*/
}

Expand Down Expand Up @@ -103,7 +105,11 @@
cursor: var(--editorInk-editing-cursor);
}

.annotationEditorLayer :is(.freeTextEditor, .inkEditor, .stampEditor) {
.annotationEditorLayer.squareEditing {
cursor: var(--editorSquare-editing-cursor);
}

.annotationEditorLayer :is(.freeTextEditor, .inkEditor, .squareEditor, .stampEditor) {
position: absolute;
background: transparent;
z-index: 1;
Expand Down Expand Up @@ -164,6 +170,7 @@
:is(
.freeTextEditor,
.inkEditor,
.squareEditor,
.stampEditor,
.highlightEditor,
.underlineEditor,
Expand Down Expand Up @@ -429,16 +436,19 @@
user-select: auto;
}

.annotationEditorLayer .inkEditor {
.annotationEditorLayer .inkEditor,
.annotationEditorLayer .squareEditor {
width: 100%;
height: 100%;
}

.annotationEditorLayer .inkEditor.editing {
.annotationEditorLayer .inkEditor.editing,
.annotationEditorLayer .squareEditor.editing {
cursor: inherit;
}

.annotationEditorLayer .inkEditor .inkEditorCanvas {
.annotationEditorLayer .inkEditor .inkEditorCanvas,
.annotationEditorLayer .squareEditor .squareEditorCanvas {
position: absolute;
inset: 0;
width: 100%;
Expand All @@ -457,7 +467,7 @@
}

.annotationEditorLayer {
:is(.freeTextEditor, .inkEditor, .stampEditor) {
:is(.freeTextEditor, .inkEditor, .squareEditor, .stampEditor) {
& > .resizers {
position: absolute;
inset: 0;
Expand Down
1 change: 1 addition & 0 deletions packages/react-pdf/src/images/cursor-editorSquare.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions packages/react-pdf/src/shared/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import type {
import type { AnnotationLayerParameters } from '@commutatus/pdfjs-dist/types/src/display/annotation_layer.js';
import type LinkService from '../LinkService.js';
import type React from 'react';
import type { Dispatch, SetStateAction } from 'react';

type NullableObject<T extends object> = { [P in keyof T]: T[P] | null };

Expand Down Expand Up @@ -136,6 +137,7 @@ export type DocumentContextType = {
registerPage: RegisterPage;
renderMode?: RenderMode;
rotate?: number | null;
setGlobalScale: Dispatch<SetStateAction<number | null>>;
unregisterPage: UnregisterPage;
} | null;

Expand Down

0 comments on commit 622776c

Please sign in to comment.