Skip to content

Commit

Permalink
Implement underline annotations for PDF view
Browse files Browse the repository at this point in the history
  • Loading branch information
mrtcode committed Jun 20, 2023
1 parent 8f66e45 commit 4129599
Show file tree
Hide file tree
Showing 27 changed files with 381 additions and 137 deletions.
66 changes: 66 additions & 0 deletions demo/pdf/annotations.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file modified res/icons/darwin/highlight-white.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified res/icons/darwin/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified res/icons/darwin/highlight.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified res/icons/darwin/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added res/icons/darwin/underline-white.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added res/icons/darwin/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added res/icons/darwin/underline.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added res/icons/darwin/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 10 additions & 2 deletions src/common/components/common/icons.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,16 @@ import React from 'react';

export function IconHighlight() {
return (
<svg width="12" height="12" viewBox="0 0 12 12">
<path fill="currentColor" d="M12,5H0V3H12Zm0,1H0V8H12ZM9,9H0v2H9Zm3-9H3V2h9Z"/>
<svg width="12" height="12" viewBox="0 0 32 32">
<path d="M0.1875 1.71875L0.1875 30.3125L31.875 30.3125L31.875 1.71875L0.1875 1.71875ZM16 5.09375C16.6657 5.09375 17.3316 5.48537 17.625 6.21875L24.8438 24.2188L25.625 24.2188C26.3154 24.2188 26.875 24.7784 26.875 25.4688C26.875 26.1591 26.3154 26.7188 25.625 26.7188L20.8125 26.7188C20.1221 26.7188 19.5625 26.1591 19.5625 25.4688C19.5625 24.7784 20.1221 24.2188 20.8125 24.2188L21.0938 24.2188L19.6875 20.7188L12.3438 20.7188L10.9375 24.2188L11.2188 24.2188C11.9091 24.2188 12.4688 24.7784 12.4688 25.4688C12.4688 26.1591 11.9091 26.7188 11.2188 26.7188L6.40625 26.7188C5.71589 26.7188 5.15625 26.1591 5.15625 25.4688C5.15625 24.7784 5.71589 24.2188 6.40625 24.2188L7.1875 24.2188L14.375 6.21875C14.6684 5.48537 15.3343 5.09375 16 5.09375ZM16 11.5625L13.3438 18.2188L18.6562 18.2188L16 11.5625Z" fill="currentColor"/>
</svg>
);
}

export function IconUnderline() {
return (
<svg width="12" height="12" viewBox="0 0 32 32">
<path d="M16.0625 2.5625C15.4919 2.5625 14.9389 2.87139 14.6875 3.5L7.53125 21.4062C7.51046 21.4582 7.5461 21.5099 7.53125 21.5625L6.53125 21.5625C5.97897 21.5625 5.53125 22.0102 5.53125 22.5625C5.53125 23.1148 5.97897 23.5625 6.53125 23.5625L11.3125 23.5625C11.8648 23.5625 12.3125 23.1148 12.3125 22.5625C12.3125 22.0102 11.8648 21.5625 11.3125 21.5625L10.6875 21.5625L12.2812 17.5938L19.875 17.5938L21.4688 21.5625L20.8438 21.5625C20.2915 21.5625 19.8437 22.0102 19.8438 22.5625C19.8438 23.1148 20.2915 23.5625 20.8438 23.5625L25.625 23.5625C26.1773 23.5625 26.625 23.1148 26.625 22.5625C26.625 22.0102 26.1773 21.5625 25.625 21.5625L24.625 21.5625C24.6104 21.5099 24.6458 21.4582 24.625 21.4062L17.4688 3.5C17.2173 2.87139 16.6331 2.5625 16.0625 2.5625ZM16.0938 8.09375L19.0938 15.5938L13.0938 15.5938L16.0938 8.09375ZM2.4375 26.5312L2.4375 29.5625L29.5938 29.5625L29.5938 26.5312L2.4375 26.5312Z" fill="currentColor" opacity="1" stroke="none"/>
</svg>
);
}
Expand Down
16 changes: 9 additions & 7 deletions src/common/components/common/preview.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { FormattedMessage, useIntl } from 'react-intl';
import cx from 'classnames';
import Editor from './editor';
import ExpandableEditor from './expandable-editor';
import { IconHighlight, IconNote, IconArea, IconInk, IconText } from './icons';
import { IconHighlight, IconUnderline, IconNote, IconArea, IconInk, IconText } from './icons';
import { getPopupCoordinatesFromClickEvent } from '../../lib/utilities';

// TODO: Don't allow to select UI text in popup header and footer
Expand Down Expand Up @@ -62,6 +62,7 @@ export function PopupPreview(props) {
>
{
annotation.type === 'highlight' && <IconHighlight/>
|| annotation.type === 'underline' && <IconUnderline/>
|| annotation.type === 'note' && <IconNote/>
|| annotation.type === 'image' && <IconArea/>
|| annotation.type === 'ink' && <IconInk/>
Expand Down Expand Up @@ -186,8 +187,8 @@ export function SidebarPreview(props) {
props.onEditorBlur(props.annotation.id);
}

function handleHighlightDoubleClick() {
props.onDoubleClickHighlight(props.annotation.id);
function handleTextDoubleClick() {
props.onDoubleClickText(props.annotation.id);
}

function handleContextMenu(event) {
Expand All @@ -202,11 +203,11 @@ export function SidebarPreview(props) {

let { annotation, state, type } = props;

let text = annotation.type === 'highlight' && (
let text = ['highlight', 'underline'].includes(annotation.type) && (
<div
className="highlight"
onClick={e => handleSectionClick(e, 'highlight')}
onDoubleClick={handleHighlightDoubleClick}
className="annotation-text"
onClick={e => handleSectionClick(e, 'annotation-text')}
onDoubleClick={handleTextDoubleClick}
draggable={state !== 3 || annotation.readOnly}
onDragStart={handleDragStart}
>
Expand Down Expand Up @@ -275,6 +276,7 @@ export function SidebarPreview(props) {
>
{
annotation.type === 'highlight' && <IconHighlight/>
|| annotation.type === 'underline' && <IconUnderline/>
|| annotation.type === 'note' && <IconNote/>
|| annotation.type === 'image' && <IconArea/>
|| annotation.type === 'ink' && <IconInk/>
Expand Down
2 changes: 1 addition & 1 deletion src/common/components/reader-ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ const ReaderUI = React.forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({
setState,
sidebarScrollAnnotationIntoView: (id) => annotationsViewRef.current?.scrollAnnotationIntoView(id),
sidebarEditHighlightText: (id) => annotationsViewRef.current?.editHighlightText(id),
sidebarEditAnnotationText: (id) => annotationsViewRef.current?.editAnnotationText(id),
sidebarOpenPageLabelPopup: (id) => annotationsViewRef.current?.openPageLabelPopup(id)
}));

Expand Down
20 changes: 10 additions & 10 deletions src/common/components/sidebar/annotations-view.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ const Annotation = React.memo((props) => {
selected={props.isSelected}
onSetDataTransferAnnotations={props.onSetDataTransferAnnotations}
onClickSection={props.onClickAnnotationSection}
onDoubleClickHighlight={props.onDoubleClickHighlight}
onDoubleClickText={props.onDoubleClickText}
onDoubleClickPageLabel={props.onDoubleClickPageLabel}
onOpenContextMenu={props.onOpenContextMenu}
onChange={props.onChange}
Expand All @@ -142,8 +142,8 @@ const AnnotationsView = memo(React.forwardRef((props, ref) => {
// Expansion state:
// 0 - None or multiple annotations are selected
// 1 - Single annotation selected, comment expanded
// 2 - Single annotation selected, comment expanded, highlight expanded
// 3 - Single annotation selected, comment expanded, highlight expanded and editable
// 2 - Single annotation selected, comment expanded, text expanded
// 3 - Single annotation selected, comment expanded, text expanded and editable
const [expansionState, setExpansionState] = useState(0);
const pointerDownRef = useRef(false);
const selectionTimeRef = useRef(0);
Expand All @@ -155,11 +155,11 @@ const AnnotationsView = memo(React.forwardRef((props, ref) => {
});
}

function editHighlightText(id) {
function editAnnotationText(id) {
document.querySelector(`[data-sidebar-annotation-id="${id}"]`).focus();
setTimeout(() => {
setExpansionState(3);
focusSidebarHighlight(id);
focusSidebarAnnotationText(id);
}, 50);
}

Expand All @@ -173,7 +173,7 @@ const AnnotationsView = memo(React.forwardRef((props, ref) => {

useImperativeHandle(ref, () => ({
scrollAnnotationIntoView,
editHighlightText,
editAnnotationText,
openPageLabelPopup
}));

Expand Down Expand Up @@ -314,9 +314,9 @@ const AnnotationsView = memo(React.forwardRef((props, ref) => {
}
}, [expansionState, props.selectedIDs]);

function focusSidebarHighlight(annotationID) {
function focusSidebarAnnotationText(annotationID) {
setTimeout(function () {
let content = document.querySelector(`[data-sidebar-annotation-id="${annotationID}"] .highlight .content`);
let content = document.querySelector(`[data-sidebar-annotation-id="${annotationID}"] .annotation-text .content`);
if (content) {
setCaretToEnd(content);
}
Expand All @@ -329,7 +329,7 @@ const AnnotationsView = memo(React.forwardRef((props, ref) => {
&& Date.now() - selectionTimeRef.current > 500) {
if (expansionState >= 1 && expansionState <= 2) {
setExpansionState(3);
focusSidebarHighlight(id);
focusSidebarAnnotationText(id);
}
}
}, [expansionState, props.selectedIDs]);
Expand Down Expand Up @@ -468,7 +468,7 @@ const AnnotationsView = memo(React.forwardRef((props, ref) => {
onFocus={handleAnnotationFocus}
onChange={props.onChange}
onClickAnnotationSection={handleSidebarAnnotationSectionClick}
onDoubleClickHighlight={handleSidebarAnnotationDoubleClick}
onDoubleClickText={handleSidebarAnnotationDoubleClick}
onDoubleClickPageLabel={props.onOpenPageLabelPopup}
onOpenContextMenu={handleContextMenuOpen}
onSetDataTransferAnnotations={props.onSetDataTransferAnnotations}
Expand Down
7 changes: 7 additions & 0 deletions src/common/components/toolbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,13 @@ function Toolbar(props) {
onClick={() => handleToolClick('highlight')}>
<span className="button-background"/>
</button>
<button
tabIndex={-1}
className={cx('toolbarButton underline', { toggled: props.tool.type === 'underline' })}
title={intl.formatMessage({ id: 'pdfReader.underlineText' })}
onClick={() => handleToolClick('underline')}>
<span className="button-background"/>
</button>
<button
tabIndex={-1}
className={cx('toolbarButton note', {
Expand Down
6 changes: 0 additions & 6 deletions src/common/components/view/overlay-popup/portal-popup.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,6 @@ function PortalPopup(props) {
let x = props.params.rect[0];
let y = props.params.rect[1];



function handleColorPick(color) {
props.onHighlight(color);
}

function handleOnRender() {
let parent = containerRef.current;
let top = parent.offsetTop;
Expand Down
8 changes: 4 additions & 4 deletions src/common/context-menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ export function createAnnotationContextMenu(reader, params) {
label: reader._getString('pdfReader.addToNote'),
disabled: !reader._state.enableAddToNote,
persistent: true,
onCommand: () => reader._sidebarEditHighlightText(params.ids[0])
onCommand: () => reader._sidebarEditAnnotationText(params.ids[0])
}
],
ANNOTATION_COLORS.map(([label, color]) => ({
Expand All @@ -133,14 +133,14 @@ export function createAnnotationContextMenu(reader, params) {
onCommand: () => reader._sidebarOpenPageLabelPopup(params.currentID)
},
!params.view && {
label: reader._getString('pdfReader.editHighlightedText'),
label: reader._getString('pdfReader.editAnnotationText'),
disabled: readOnly || !(
params.ids.length === 1
&& reader._state.annotations.find(x => x.id === params.ids[0] && x.type === 'highlight')
&& reader._state.annotations.find(x => x.id === params.ids[0] && ['highlight', 'underline'].includes(x.type))
&& !params.popup
),
persistent: true,
onCommand: () => reader._sidebarEditHighlightText(params.ids[0])
onCommand: () => reader._sidebarEditAnnotationText(params.ids[0])
}
],
[
Expand Down
2 changes: 1 addition & 1 deletion src/common/keyboard-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ export class KeyboardManager {
let annotation = this._reader._state.annotations.find(x => x.id === selectedIDs[0]);
if (event.target.closest('.content')
&& (
event.target.closest('.highlight')
event.target.closest('.annotation-text')
|| annotation.comment
|| !this._reader._enableAnnotationDeletionFromComment
)
Expand Down
4 changes: 2 additions & 2 deletions src/common/reader.js
Original file line number Diff line number Diff line change
Expand Up @@ -533,8 +533,8 @@ class Reader {
this._readerRef.current.sidebarScrollAnnotationIntoView(id);
}

_sidebarEditHighlightText(id) {
this._readerRef.current.sidebarEditHighlightText(id);
_sidebarEditAnnotationText(id) {
this._readerRef.current.sidebarEditAnnotationText(id);
}

_sidebarOpenPageLabelPopup(id) {
Expand Down
2 changes: 1 addition & 1 deletion src/common/stylesheets/components/_editor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
}
}

.highlight {
.annotation-text {
.editor {
.content {
&:empty::before {
Expand Down
8 changes: 8 additions & 0 deletions src/common/stylesheets/components/_icons.scss
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@
}
}

.underline {
@include icon($underline-tool-icon);

&.toggled {
@include icon($underline-tool-icon-active);
}
}

.note {
@include icon($note-tool-icon);

Expand Down
2 changes: 1 addition & 1 deletion src/common/stylesheets/components/_preview.scss
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@
}
}

.highlight {
.annotation-text {
color: #555;
position: relative;

Expand Down
2 changes: 2 additions & 0 deletions src/common/stylesheets/themes/_light-darwin.scss
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,8 @@ $progress-label-width: 38px;
// Annotation tools
$highlight-tool-icon: "darwin/highlight";
$highlight-tool-icon-active: "darwin/highlight-white";
$underline-tool-icon: "darwin/underline";
$underline-tool-icon-active: "darwin/underline-white";
$note-tool-icon: "darwin/note";
$note-tool-icon-active: "darwin/note-white";
$area-tool-icon: "darwin/area";
Expand Down
14 changes: 10 additions & 4 deletions src/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ import { Selector } from "../dom/common/lib/selector";

export type ToolType =
| 'highlight'
| 'underline'
| 'note'
| 'pointer'
// Future:
| 'underline';
| 'image'
| 'text'
| 'ink'
| 'eraser'
| 'pointer';

export type Tool = {
type: ToolType;
Expand All @@ -14,9 +17,12 @@ export type Tool = {

export type AnnotationType =
| 'highlight'
| 'underline'
| 'note'
| 'image'
| 'ink';
| 'text'
| 'ink'
| 'eraser';

export interface Annotation {
id: string;
Expand Down
2 changes: 1 addition & 1 deletion src/en-us.strings.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ export default {
'pdfReader.rotateLeft': 'Rotate Left',
'pdfReader.rotateRight': 'Rotate Right',
'pdfReader.editPageNumber': 'Edit Page Number…',
'pdfReader.editHighlightedText': 'Edit Highlighted Text',
'pdfReader.editAnnotationText': 'Edit Annotation Text',
'pdfReader.copyImage': 'Copy Image',
'pdfReader.saveImageAs': 'Save Image As…',
'pdfReader.pageNumberPopupHeader': 'Change page number for:',
Expand Down
Loading

0 comments on commit 4129599

Please sign in to comment.