RED-3988: replace old pdf viewer
This commit is contained in:
parent
7ad5fe09ac
commit
edc53c7971
@ -7,11 +7,11 @@ import { UserService } from '@services/user.service';
|
|||||||
import { AnnotationReferencesService } from '../../services/annotation-references.service';
|
import { AnnotationReferencesService } from '../../services/annotation-references.service';
|
||||||
import { MultiSelectService } from '../../services/multi-select.service';
|
import { MultiSelectService } from '../../services/multi-select.service';
|
||||||
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
||||||
import { HelpModeService, ScrollableParentView, ScrollableParentViews } from '@iqser/common-ui';
|
import { HelpModeService } from '@iqser/common-ui';
|
||||||
import { PdfViewer } from '../../services/pdf-viewer.service';
|
|
||||||
import { FileDataService } from '../../services/file-data.service';
|
import { FileDataService } from '../../services/file-data.service';
|
||||||
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
|
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
|
||||||
import { ViewModeService } from '../../services/view-mode.service';
|
import { ViewModeService } from '../../services/view-mode.service';
|
||||||
|
import { ReusablePdfViewer } from '../../../shared/components/reusable-pdf-viewer/reusable-pdf-viewer.service';
|
||||||
|
|
||||||
export const AnnotationButtonTypes = {
|
export const AnnotationButtonTypes = {
|
||||||
dark: 'dark',
|
dark: 'dark',
|
||||||
@ -40,7 +40,7 @@ export class AnnotationActionsComponent implements OnChanges {
|
|||||||
readonly helpModeService: HelpModeService,
|
readonly helpModeService: HelpModeService,
|
||||||
private readonly _changeRef: ChangeDetectorRef,
|
private readonly _changeRef: ChangeDetectorRef,
|
||||||
private readonly _userService: UserService,
|
private readonly _userService: UserService,
|
||||||
private readonly _pdf: PdfViewer,
|
private readonly _reusablePdf: ReusablePdfViewer,
|
||||||
private readonly _state: FilePreviewStateService,
|
private readonly _state: FilePreviewStateService,
|
||||||
private readonly _permissionsService: PermissionsService,
|
private readonly _permissionsService: PermissionsService,
|
||||||
private readonly _fileDataService: FileDataService,
|
private readonly _fileDataService: FileDataService,
|
||||||
@ -59,11 +59,7 @@ export class AnnotationActionsComponent implements OnChanges {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get viewerAnnotations() {
|
get viewerAnnotations() {
|
||||||
if (this._pdf.annotationManager) {
|
return this._reusablePdf.getAnnotationsById(this._annotations.map(a => a.id));
|
||||||
return this._annotations.map(a => this._pdf.annotationManager.getAnnotationById(a.id));
|
|
||||||
} else {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get isVisible() {
|
get isVisible() {
|
||||||
@ -98,15 +94,15 @@ export class AnnotationActionsComponent implements OnChanges {
|
|||||||
|
|
||||||
hideAnnotation($event: MouseEvent) {
|
hideAnnotation($event: MouseEvent) {
|
||||||
$event.stopPropagation();
|
$event.stopPropagation();
|
||||||
this._pdf.annotationManager.hideAnnotations(this.viewerAnnotations);
|
this._reusablePdf.hideAnnotations(this.viewerAnnotations);
|
||||||
this._pdf.annotationManager.deselectAllAnnotations();
|
this._reusablePdf.deselectAnnotations();
|
||||||
this._fileDataService.updateHiddenAnnotations(this.viewerAnnotations, true);
|
this._fileDataService.updateHiddenAnnotations(this.viewerAnnotations, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
showAnnotation($event: MouseEvent) {
|
showAnnotation($event: MouseEvent) {
|
||||||
$event.stopPropagation();
|
$event.stopPropagation();
|
||||||
this._pdf.annotationManager.showAnnotations(this.viewerAnnotations);
|
this._reusablePdf.showAnnotations(this.viewerAnnotations);
|
||||||
this._pdf.annotationManager.deselectAllAnnotations();
|
this._reusablePdf.deselectAnnotations();
|
||||||
this._fileDataService.updateHiddenAnnotations(this.viewerAnnotations, false);
|
this._fileDataService.updateHiddenAnnotations(this.viewerAnnotations, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -18,6 +18,7 @@ import { ViewModeService } from '../../services/view-mode.service';
|
|||||||
import { BehaviorSubject } from 'rxjs';
|
import { BehaviorSubject } from 'rxjs';
|
||||||
import { TextHighlightsGroup } from '@red/domain';
|
import { TextHighlightsGroup } from '@red/domain';
|
||||||
import { PdfViewer } from '../../services/pdf-viewer.service';
|
import { PdfViewer } from '../../services/pdf-viewer.service';
|
||||||
|
import { ReusablePdfViewer } from '../../../shared/components/reusable-pdf-viewer/reusable-pdf-viewer.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-annotations-list',
|
selector: 'redaction-annotations-list',
|
||||||
@ -41,6 +42,7 @@ export class AnnotationsListComponent extends HasScrollbarDirective implements O
|
|||||||
private readonly _userPreferenceService: UserPreferenceService,
|
private readonly _userPreferenceService: UserPreferenceService,
|
||||||
private readonly _viewModeService: ViewModeService,
|
private readonly _viewModeService: ViewModeService,
|
||||||
private readonly _pdf: PdfViewer,
|
private readonly _pdf: PdfViewer,
|
||||||
|
private readonly _reusablePdf: ReusablePdfViewer,
|
||||||
private readonly _listingService: ListingService<AnnotationWrapper>,
|
private readonly _listingService: ListingService<AnnotationWrapper>,
|
||||||
readonly annotationReferencesService: AnnotationReferencesService,
|
readonly annotationReferencesService: AnnotationReferencesService,
|
||||||
) {
|
) {
|
||||||
@ -69,7 +71,7 @@ export class AnnotationsListComponent extends HasScrollbarDirective implements O
|
|||||||
this.pagesPanelActive.emit(false);
|
this.pagesPanelActive.emit(false);
|
||||||
|
|
||||||
if (this._listingService.isSelected(annotation)) {
|
if (this._listingService.isSelected(annotation)) {
|
||||||
this._pdf.deselectAnnotations([annotation]);
|
this._reusablePdf.deselectAnnotations([annotation]);
|
||||||
} else {
|
} else {
|
||||||
const canMultiSelect = this._multiSelectService.isEnabled;
|
const canMultiSelect = this._multiSelectService.isEnabled;
|
||||||
if (canMultiSelect && ($event?.ctrlKey || $event?.metaKey) && this._listingService.selected.length > 0) {
|
if (canMultiSelect && ($event?.ctrlKey || $event?.metaKey) && this._listingService.selected.length > 0) {
|
||||||
|
|||||||
@ -48,7 +48,7 @@
|
|||||||
<div *ngIf="multiSelectService.active$ | async" class="multi-select">
|
<div *ngIf="multiSelectService.active$ | async" class="multi-select">
|
||||||
<div class="selected-wrapper">
|
<div class="selected-wrapper">
|
||||||
<iqser-round-checkbox
|
<iqser-round-checkbox
|
||||||
(click)="pdf.deselectAllAnnotations()"
|
(click)="reusablePdf.deselectAnnotations()"
|
||||||
[indeterminate]="listingService.areSomeSelected$ | async"
|
[indeterminate]="listingService.areSomeSelected$ | async"
|
||||||
type="with-bg"
|
type="with-bg"
|
||||||
></iqser-round-checkbox>
|
></iqser-round-checkbox>
|
||||||
|
|||||||
@ -37,6 +37,7 @@ import { ViewModeService } from '../../services/view-mode.service';
|
|||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import { FileDataService } from '../../services/file-data.service';
|
import { FileDataService } from '../../services/file-data.service';
|
||||||
import { PdfViewer } from '../../services/pdf-viewer.service';
|
import { PdfViewer } from '../../services/pdf-viewer.service';
|
||||||
|
import { ReusablePdfViewer } from '../../../shared/components/reusable-pdf-viewer/reusable-pdf-viewer.service';
|
||||||
|
|
||||||
const COMMAND_KEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Escape'];
|
const COMMAND_KEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Escape'];
|
||||||
const ALL_HOTKEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'];
|
const ALL_HOTKEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'];
|
||||||
@ -78,10 +79,11 @@ export class FileWorkloadComponent {
|
|||||||
readonly viewModeService: ViewModeService,
|
readonly viewModeService: ViewModeService,
|
||||||
readonly listingService: ListingService<AnnotationWrapper>,
|
readonly listingService: ListingService<AnnotationWrapper>,
|
||||||
readonly pdf: PdfViewer,
|
readonly pdf: PdfViewer,
|
||||||
|
readonly reusablePdf: ReusablePdfViewer,
|
||||||
private readonly _changeDetectorRef: ChangeDetectorRef,
|
private readonly _changeDetectorRef: ChangeDetectorRef,
|
||||||
private readonly _annotationProcessingService: AnnotationProcessingService,
|
private readonly _annotationProcessingService: AnnotationProcessingService,
|
||||||
) {
|
) {
|
||||||
this.pdf.currentPage$.pipe(takeWhile(() => !!this)).subscribe(pageNumber => {
|
this.reusablePdf.currentPage$.pipe(takeWhile(() => !!this)).subscribe(pageNumber => {
|
||||||
this._scrollViews();
|
this._scrollViews();
|
||||||
this.scrollAnnotationsToPage(pageNumber, 'always');
|
this.scrollAnnotationsToPage(pageNumber, 'always');
|
||||||
});
|
});
|
||||||
@ -134,7 +136,7 @@ export class FileWorkloadComponent {
|
|||||||
return this.multiSelectService.inactive$.pipe(
|
return this.multiSelectService.inactive$.pipe(
|
||||||
tap(value => {
|
tap(value => {
|
||||||
if (value) {
|
if (value) {
|
||||||
this.pdf.deselectAllAnnotations();
|
this.reusablePdf.deselectAnnotations();
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
shareDistinctLast(),
|
shareDistinctLast(),
|
||||||
@ -183,7 +185,7 @@ export class FileWorkloadComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
deselectAllOnActivePage(): void {
|
deselectAllOnActivePage(): void {
|
||||||
this.pdf.deselectAnnotations(this.activeAnnotations);
|
this.reusablePdf.deselectAnnotations(this.activeAnnotations);
|
||||||
}
|
}
|
||||||
|
|
||||||
@HostListener('window:keyup', ['$event'])
|
@HostListener('window:keyup', ['$event'])
|
||||||
|
|||||||
@ -1,29 +1,31 @@
|
|||||||
<div class="page">
|
<ng-container *ngIf="reusablePdf.show$ | async">
|
||||||
<div #viewer [id]="(stateService.file$ | async).fileId" class="viewer"></div>
|
<input #compareFileInput (change)="uploadFile($event.target['files'])" accept="application/pdf" class="file-upload-input" type="file" />
|
||||||
</div>
|
|
||||||
|
|
||||||
<input #compareFileInput (change)="uploadFile($event.target['files'])" accept="application/pdf" class="file-upload-input" type="file" />
|
<div class="pagination noselect">
|
||||||
|
<div (click)="pdfViewer.navigatePreviousPage()">
|
||||||
|
<mat-icon class="chevron-icon" svgIcon="red:nav-prev"></mat-icon>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div *ngIf="pdfViewer?.totalPages && pdfViewer?.currentPage" class="pagination noselect">
|
<div>
|
||||||
<div (click)="pdfViewer.navigatePreviousPage()">
|
<input
|
||||||
<mat-icon class="chevron-icon" svgIcon="red:nav-prev"></mat-icon>
|
#pageInput
|
||||||
|
(change)="pdfViewer.navigateToPage(pageInput.value)"
|
||||||
|
[max]="pdfViewer.totalPages"
|
||||||
|
[value]="pdfViewer.currentPage"
|
||||||
|
class="page-number-input"
|
||||||
|
min="1"
|
||||||
|
type="number"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="separator">/</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{{ pdfViewer.totalPages }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div (click)="pdfViewer.navigateNextPage()">
|
||||||
|
<mat-icon class="chevron-icon" svgIcon="red:nav-next"></mat-icon>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
</ng-container>
|
||||||
<input
|
|
||||||
#pageInput
|
|
||||||
(change)="pdfViewer.navigateToPage(pageInput.value)"
|
|
||||||
[max]="pdfViewer.totalPages"
|
|
||||||
[value]="pdfViewer.currentPage"
|
|
||||||
class="page-number-input"
|
|
||||||
min="1"
|
|
||||||
type="number"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="separator">/</div>
|
|
||||||
<div>
|
|
||||||
{{ pdfViewer.totalPages }}
|
|
||||||
</div>
|
|
||||||
<div (click)="pdfViewer.navigateNextPage()">
|
|
||||||
<mat-icon class="chevron-icon" svgIcon="red:nav-next"></mat-icon>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|||||||
@ -1,14 +1,3 @@
|
|||||||
.page {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.viewer {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.searching {
|
.searching {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 9px;
|
top: 9px;
|
||||||
@ -22,6 +11,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.pagination {
|
.pagination {
|
||||||
|
z-index: 1;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 20px;
|
bottom: 20px;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
|
|||||||
@ -26,7 +26,7 @@ import { AnnotationActionsService } from '../../services/annotation-actions.serv
|
|||||||
import { UserPreferenceService } from '@services/user-preference.service';
|
import { UserPreferenceService } from '@services/user-preference.service';
|
||||||
import { BASE_HREF } from '../../../../tokens';
|
import { BASE_HREF } from '../../../../tokens';
|
||||||
import { ConfigService } from '@services/config.service';
|
import { ConfigService } from '@services/config.service';
|
||||||
import { AutoUnsubscribe, ConfirmationDialogInput, CustomError, ErrorService, LoadingService } from '@iqser/common-ui';
|
import { AutoUnsubscribe, ConfirmationDialogInput, CustomError, ErrorService, LoadingService, log } from '@iqser/common-ui';
|
||||||
import { PdfViewer } from '../../services/pdf-viewer.service';
|
import { PdfViewer } from '../../services/pdf-viewer.service';
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import { toPosition } from '../../utils/pdf-calculation.utils';
|
import { toPosition } from '../../utils/pdf-calculation.utils';
|
||||||
@ -44,8 +44,6 @@ import { ViewerHeaderConfigService } from '../../services/viewer-header-config.s
|
|||||||
import { TooltipsService } from '../../services/tooltips.service';
|
import { TooltipsService } from '../../services/tooltips.service';
|
||||||
import { ManualRedactionService } from '../../services/manual-redaction.service';
|
import { ManualRedactionService } from '../../services/manual-redaction.service';
|
||||||
import { ReusablePdfViewer } from '../../../shared/components/reusable-pdf-viewer/reusable-pdf-viewer.service';
|
import { ReusablePdfViewer } from '../../../shared/components/reusable-pdf-viewer/reusable-pdf-viewer.service';
|
||||||
import Tools = Core.Tools;
|
|
||||||
import TextTool = Tools.TextTool;
|
|
||||||
import Annotation = Core.Annotations.Annotation;
|
import Annotation = Core.Annotations.Annotation;
|
||||||
|
|
||||||
const DocLoadingError = new CustomError(_('error.file-preview.label'), _('error.file-preview.action'), 'iqser:refresh');
|
const DocLoadingError = new CustomError(_('error.file-preview.label'), _('error.file-preview.action'), 'iqser:refresh');
|
||||||
@ -62,7 +60,6 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
|
|||||||
@Output() readonly manualAnnotationRequested = new EventEmitter<ManualRedactionEntryWrapper>();
|
@Output() readonly manualAnnotationRequested = new EventEmitter<ManualRedactionEntryWrapper>();
|
||||||
@Output() readonly pageChanged = new EventEmitter<number>();
|
@Output() readonly pageChanged = new EventEmitter<number>();
|
||||||
@Output() readonly keyUp = new EventEmitter<KeyboardEvent>();
|
@Output() readonly keyUp = new EventEmitter<KeyboardEvent>();
|
||||||
@ViewChild('viewer', { static: true }) viewer: ElementRef;
|
|
||||||
@ViewChild('compareFileInput', { static: true }) compareFileInput: ElementRef;
|
@ViewChild('compareFileInput', { static: true }) compareFileInput: ElementRef;
|
||||||
instance: WebViewerInstance;
|
instance: WebViewerInstance;
|
||||||
documentViewer: Core.DocumentViewer;
|
documentViewer: Core.DocumentViewer;
|
||||||
@ -85,7 +82,7 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
|
|||||||
private readonly _headerConfigService: ViewerHeaderConfigService,
|
private readonly _headerConfigService: ViewerHeaderConfigService,
|
||||||
private readonly _tooltipsService: TooltipsService,
|
private readonly _tooltipsService: TooltipsService,
|
||||||
private readonly _errorService: ErrorService,
|
private readonly _errorService: ErrorService,
|
||||||
private readonly _reusablePdfViewer: ReusablePdfViewer,
|
readonly reusablePdf: ReusablePdfViewer,
|
||||||
readonly stateService: FilePreviewStateService,
|
readonly stateService: FilePreviewStateService,
|
||||||
readonly viewModeService: ViewModeService,
|
readonly viewModeService: ViewModeService,
|
||||||
readonly multiSelectService: MultiSelectService,
|
readonly multiSelectService: MultiSelectService,
|
||||||
@ -94,27 +91,25 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
|
|||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
ngOnInit() {
|
||||||
this._setReadyAndInitialState = this._setReadyAndInitialState.bind(this);
|
this._setReadyAndInitialState = this._setReadyAndInitialState.bind(this);
|
||||||
await this._loadViewer();
|
this._loadViewer();
|
||||||
|
|
||||||
|
console.log('pdf-viewer.component.ts: ngOnInit');
|
||||||
this.addActiveScreenSubscription = this.stateService.blob$
|
this.addActiveScreenSubscription = this.stateService.blob$
|
||||||
.pipe(
|
.pipe(
|
||||||
switchMap(blob => from(this.pdfViewer.lockDocument()).pipe(map(() => blob))),
|
log('Reload blob'),
|
||||||
|
switchMap(blob => from(this.reusablePdf.lockDocument()).pipe(map(() => blob))),
|
||||||
withLatestFrom(this.stateService.file$),
|
withLatestFrom(this.stateService.file$),
|
||||||
tap(() => this._errorService.clear()),
|
tap(() => this._errorService.clear()),
|
||||||
tap(([blob, file]) => this._loadDocument(blob, file)),
|
tap(([blob, file]) => this.reusablePdf.loadDocument(blob, file)),
|
||||||
tap(([blob, file]) => this._reusablePdfViewer.loadDocument(blob, file)),
|
tap(() => this._pageRotationService.clearRotationsHideActions()),
|
||||||
)
|
)
|
||||||
.subscribe();
|
.subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnChanges(changes: SimpleChanges): void {
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
if (!this.instance) {
|
if (changes.canPerformActions && !!this.instance) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changes.canPerformActions) {
|
|
||||||
this._handleCustomActions();
|
this._handleCustomActions();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -187,14 +182,12 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
|
|||||||
fileReader.readAsArrayBuffer(fileToCompare);
|
fileReader.readAsArrayBuffer(fileToCompare);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _loadViewer() {
|
private _loadViewer() {
|
||||||
this.instance = await this.pdfViewer.loadViewer(this.viewer.nativeElement as HTMLElement);
|
this.instance = this.reusablePdf.instance;
|
||||||
this.documentViewer = this.pdfViewer.documentViewer;
|
this.documentViewer = this.reusablePdf.documentViewer;
|
||||||
this.annotationManager = this.pdfViewer.annotationManager;
|
this.annotationManager = this.reusablePdf.annotationManager;
|
||||||
|
|
||||||
this._setSelectionMode();
|
|
||||||
this._configureElements();
|
this._configureElements();
|
||||||
this.pdfViewer.disableHotkeys();
|
|
||||||
this._configureTextPopup();
|
this._configureTextPopup();
|
||||||
|
|
||||||
this.annotationManager.addEventListener('annotationSelected', (annotations: Annotation[], action) => {
|
this.annotationManager.addEventListener('annotationSelected', (annotations: Annotation[], action) => {
|
||||||
@ -202,7 +195,7 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
|
|||||||
|
|
||||||
if (action === 'deselected') {
|
if (action === 'deselected') {
|
||||||
// Remove deselected annotations from selected list
|
// Remove deselected annotations from selected list
|
||||||
nextAnnotations = this.annotationManager.getSelectedAnnotations().filter(ann => !annotations.find(a => a.Id === ann.Id));
|
nextAnnotations = this.annotationManager.getSelectedAnnotations().filter(ann => !annotations.some(a => a.Id === ann.Id));
|
||||||
} else if (!this.multiSelectService.isEnabled) {
|
} else if (!this.multiSelectService.isEnabled) {
|
||||||
// Only choose the last selected annotation, to bypass viewer multi select
|
// Only choose the last selected annotation, to bypass viewer multi select
|
||||||
nextAnnotations = annotations;
|
nextAnnotations = annotations;
|
||||||
@ -217,8 +210,8 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!this.multiSelectService.isEnabled) {
|
if (!this.multiSelectService.isEnabled) {
|
||||||
this.pdfViewer.deselectAnnotations(
|
this.reusablePdf.deselectAnnotations(
|
||||||
this._fileDataService.all.filter(wrapper => !nextAnnotations.find(ann => ann.Id === wrapper.id)),
|
this._fileDataService.all.filter(wrapper => !nextAnnotations.some(ann => ann.Id === wrapper.id)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
this.#configureAnnotationSpecificActions(annotations);
|
this.#configureAnnotationSpecificActions(annotations);
|
||||||
@ -236,7 +229,7 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.documentViewer.addEventListener('pageNumberUpdated', (pageNumber: number) => {
|
this.documentViewer.addEventListener('pageNumberUpdated', (pageNumber: number) => {
|
||||||
this.pdfViewer.deselectAllAnnotations();
|
this.reusablePdf.deselectAnnotations();
|
||||||
this._ngZone.run(() => this.pageChanged.emit(pageNumber));
|
this._ngZone.run(() => this.pageChanged.emit(pageNumber));
|
||||||
return this._handleCustomActions();
|
return this._handleCustomActions();
|
||||||
});
|
});
|
||||||
@ -302,11 +295,6 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
|
|||||||
return this._baseHref + path;
|
return this._baseHref + path;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _setSelectionMode(): void {
|
|
||||||
const textTool = this.instance.Core.Tools.TextTool as unknown as TextTool;
|
|
||||||
textTool.SELECTION_MODE = this._configService.values.SELECTION_MODE;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _toggleRectangleAnnotationAction(readonly = false) {
|
private _toggleRectangleAnnotationAction(readonly = false) {
|
||||||
if (!readonly) {
|
if (!readonly) {
|
||||||
this.instance.UI.enableElements([TextPopups.ADD_RECTANGLE]);
|
this.instance.UI.enableElements([TextPopups.ADD_RECTANGLE]);
|
||||||
@ -316,29 +304,6 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _configureElements() {
|
private _configureElements() {
|
||||||
this.instance.UI.disableElements([
|
|
||||||
'pageNavOverlay',
|
|
||||||
'menuButton',
|
|
||||||
'selectToolButton',
|
|
||||||
'textHighlightToolButton',
|
|
||||||
'textUnderlineToolButton',
|
|
||||||
'textSquigglyToolButton',
|
|
||||||
'textStrikeoutToolButton',
|
|
||||||
'viewControlsButton',
|
|
||||||
'contextMenuPopup',
|
|
||||||
'linkButton',
|
|
||||||
'toggleNotesButton',
|
|
||||||
'notesPanel',
|
|
||||||
'thumbnailControl',
|
|
||||||
'documentControl',
|
|
||||||
'ribbons',
|
|
||||||
'toolsHeader',
|
|
||||||
'rotateClockwiseButton',
|
|
||||||
'rotateCounterClockwiseButton',
|
|
||||||
'annotationStyleEditButton',
|
|
||||||
'annotationGroupButton',
|
|
||||||
]);
|
|
||||||
|
|
||||||
this._headerConfigService.initialize(this.compareFileInput);
|
this._headerConfigService.initialize(this.compareFileInput);
|
||||||
|
|
||||||
const dossierTemplateId = this.dossier.dossierTemplateId;
|
const dossierTemplateId = this.dossier.dossierTemplateId;
|
||||||
@ -568,7 +533,6 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
|
|||||||
const document = await pdfNet.PDFDoc.createFromBuffer(await blob.arrayBuffer());
|
const document = await pdfNet.PDFDoc.createFromBuffer(await blob.arrayBuffer());
|
||||||
await document.flattenAnnotations(false);
|
await document.flattenAnnotations(false);
|
||||||
this.instance.UI.loadDocument(document, { filename: file?.filename + '.pdf' ?? 'document.pdf', onError });
|
this.instance.UI.loadDocument(document, { filename: file?.filename + '.pdf' ?? 'document.pdf', onError });
|
||||||
this._pageRotationService.clearRotationsHideActions();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _setReadyAndInitialState() {
|
private _setReadyAndInitialState() {
|
||||||
|
|||||||
@ -68,9 +68,7 @@
|
|||||||
(keyUp)="handleKeyEvent($event); workloadComponent.handleKeyEvent($event)"
|
(keyUp)="handleKeyEvent($event); workloadComponent.handleKeyEvent($event)"
|
||||||
(manualAnnotationRequested)="openManualAnnotationDialog($event)"
|
(manualAnnotationRequested)="openManualAnnotationDialog($event)"
|
||||||
(pageChanged)="viewerPageChanged($event)"
|
(pageChanged)="viewerPageChanged($event)"
|
||||||
*ngIf="displayPdfViewer"
|
|
||||||
[canPerformActions]="canPerformAnnotationActions$ | async"
|
[canPerformActions]="canPerformAnnotationActions$ | async"
|
||||||
[class.hidden]="!ready"
|
|
||||||
[dossier]="dossier"
|
[dossier]="dossier"
|
||||||
></redaction-pdf-viewer>
|
></redaction-pdf-viewer>
|
||||||
</div>
|
</div>
|
||||||
@ -88,7 +86,7 @@
|
|||||||
<redaction-file-workload
|
<redaction-file-workload
|
||||||
(selectPage)="selectPage($event)"
|
(selectPage)="selectPage($event)"
|
||||||
*ngIf="!file.excluded"
|
*ngIf="!file.excluded"
|
||||||
[activeViewerPage]="pdf.currentPage$ | async"
|
[activeViewerPage]="reusablePdf.currentPage$ | async"
|
||||||
[annotationActionsTemplate]="annotationActionsTemplate"
|
[annotationActionsTemplate]="annotationActionsTemplate"
|
||||||
[dialogRef]="dialogRef"
|
[dialogRef]="dialogRef"
|
||||||
[file]="file"
|
[file]="file"
|
||||||
|
|||||||
@ -31,7 +31,7 @@ import { download, handleFilterDelta } from '../../utils';
|
|||||||
import { FileWorkloadComponent } from './components/file-workload/file-workload.component';
|
import { FileWorkloadComponent } from './components/file-workload/file-workload.component';
|
||||||
import { FilesService } from '@services/files/files.service';
|
import { FilesService } from '@services/files/files.service';
|
||||||
import { FileManagementService } from '@services/files/file-management.service';
|
import { FileManagementService } from '@services/files/file-management.service';
|
||||||
import { catchError, map, startWith, switchMap, tap } from 'rxjs/operators';
|
import { catchError, filter, map, startWith, switchMap, tap } from 'rxjs/operators';
|
||||||
import { FilesMapService } from '@services/files/files-map.service';
|
import { FilesMapService } from '@services/files/files-map.service';
|
||||||
import { ExcludedPagesService } from './services/excluded-pages.service';
|
import { ExcludedPagesService } from './services/excluded-pages.service';
|
||||||
import { ViewModeService } from './services/view-mode.service';
|
import { ViewModeService } from './services/view-mode.service';
|
||||||
@ -51,6 +51,7 @@ import { FileDataService } from './services/file-data.service';
|
|||||||
import { ActionsHelpModeKeys, ALL_HOTKEYS } from './utils/constants';
|
import { ActionsHelpModeKeys, ALL_HOTKEYS } from './utils/constants';
|
||||||
import { NGXLogger } from 'ngx-logger';
|
import { NGXLogger } from 'ngx-logger';
|
||||||
import { StampService } from './services/stamp.service';
|
import { StampService } from './services/stamp.service';
|
||||||
|
import { ReusablePdfViewer } from '../shared/components/reusable-pdf-viewer/reusable-pdf-viewer.service';
|
||||||
import Annotation = Core.Annotations.Annotation;
|
import Annotation = Core.Annotations.Annotation;
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -63,12 +64,10 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
|||||||
|
|
||||||
dialogRef: MatDialogRef<unknown>;
|
dialogRef: MatDialogRef<unknown>;
|
||||||
fullScreen = false;
|
fullScreen = false;
|
||||||
displayPdfViewer = false;
|
|
||||||
readonly canPerformAnnotationActions$: Observable<boolean>;
|
readonly canPerformAnnotationActions$: Observable<boolean>;
|
||||||
readonly fileId = this.state.fileId;
|
readonly fileId = this.state.fileId;
|
||||||
readonly dossierId = this.state.dossierId;
|
readonly dossierId = this.state.dossierId;
|
||||||
readonly file$ = this.state.file$.pipe(tap(file => this._fileDataService.loadAnnotations(file)));
|
readonly file$ = this.state.file$.pipe(tap(file => this._fileDataService.loadAnnotations(file)));
|
||||||
ready = false;
|
|
||||||
@ViewChild(FileWorkloadComponent) readonly workloadComponent: FileWorkloadComponent;
|
@ViewChild(FileWorkloadComponent) readonly workloadComponent: FileWorkloadComponent;
|
||||||
private _lastPage: string;
|
private _lastPage: string;
|
||||||
@ViewChild('annotationFilterTemplate', {
|
@ViewChild('annotationFilterTemplate', {
|
||||||
@ -79,6 +78,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly pdf: PdfViewer,
|
readonly pdf: PdfViewer,
|
||||||
|
readonly reusablePdf: ReusablePdfViewer,
|
||||||
readonly documentInfoService: DocumentInfoService,
|
readonly documentInfoService: DocumentInfoService,
|
||||||
readonly state: FilePreviewStateService,
|
readonly state: FilePreviewStateService,
|
||||||
readonly listingService: ListingService<AnnotationWrapper>,
|
readonly listingService: ListingService<AnnotationWrapper>,
|
||||||
@ -143,7 +143,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
|||||||
async updateViewMode(): Promise<void> {
|
async updateViewMode(): Promise<void> {
|
||||||
this._logger.info(`[PDF] Update ${this._viewModeService.viewMode} view mode`);
|
this._logger.info(`[PDF] Update ${this._viewModeService.viewMode} view mode`);
|
||||||
|
|
||||||
const annotations = this.pdf.getAnnotations(a => a.getCustomData('redact-manager'));
|
const annotations = this.reusablePdf.getAnnotations(a => Boolean(a.getCustomData('redact-manager')));
|
||||||
const redactions = annotations.filter(a => a.getCustomData('redaction'));
|
const redactions = annotations.filter(a => a.getCustomData('redaction'));
|
||||||
|
|
||||||
switch (this._viewModeService.viewMode) {
|
switch (this._viewModeService.viewMode) {
|
||||||
@ -156,8 +156,8 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
|||||||
.filter(a => !ocrAnnotationIds.includes(a.Id));
|
.filter(a => !ocrAnnotationIds.includes(a.Id));
|
||||||
const nonStandardEntries = annotations.filter(a => a.getCustomData('changeLogRemoved') === 'true');
|
const nonStandardEntries = annotations.filter(a => a.getCustomData('changeLogRemoved') === 'true');
|
||||||
this._setAnnotationsOpacity(standardEntries, true);
|
this._setAnnotationsOpacity(standardEntries, true);
|
||||||
this.pdf.showAnnotations(standardEntries);
|
this.reusablePdf.showAnnotations(standardEntries);
|
||||||
this.pdf.hideAnnotations(nonStandardEntries);
|
this.reusablePdf.hideAnnotations(nonStandardEntries);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'DELTA': {
|
case 'DELTA': {
|
||||||
@ -165,21 +165,21 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
|||||||
const nonChangeLogEntries = annotations.filter(a => a.getCustomData('changeLog') === 'false');
|
const nonChangeLogEntries = annotations.filter(a => a.getCustomData('changeLog') === 'false');
|
||||||
this._setAnnotationsColor(redactions, 'annotationColor');
|
this._setAnnotationsColor(redactions, 'annotationColor');
|
||||||
this._setAnnotationsOpacity(changeLogEntries, true);
|
this._setAnnotationsOpacity(changeLogEntries, true);
|
||||||
this.pdf.showAnnotations(changeLogEntries);
|
this.reusablePdf.showAnnotations(changeLogEntries);
|
||||||
this.pdf.hideAnnotations(nonChangeLogEntries);
|
this.reusablePdf.hideAnnotations(nonChangeLogEntries);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'REDACTED': {
|
case 'REDACTED': {
|
||||||
const nonRedactionEntries = annotations.filter(a => a.getCustomData('redaction') === 'false');
|
const nonRedactionEntries = annotations.filter(a => a.getCustomData('redaction') === 'false');
|
||||||
this._setAnnotationsOpacity(redactions);
|
this._setAnnotationsOpacity(redactions);
|
||||||
this._setAnnotationsColor(redactions, 'redactionColor');
|
this._setAnnotationsColor(redactions, 'redactionColor');
|
||||||
this.pdf.showAnnotations(redactions);
|
this.reusablePdf.showAnnotations(redactions);
|
||||||
this.pdf.hideAnnotations(nonRedactionEntries);
|
this.reusablePdf.hideAnnotations(nonRedactionEntries);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'TEXT_HIGHLIGHTS': {
|
case 'TEXT_HIGHLIGHTS': {
|
||||||
this._loadingService.start();
|
this._loadingService.start();
|
||||||
this.pdf.hideAnnotations(annotations);
|
this.reusablePdf.hideAnnotations(annotations);
|
||||||
const highlights = await this._fileDataService.loadTextHighlights();
|
const highlights = await this._fileDataService.loadTextHighlights();
|
||||||
await this._annotationDrawService.draw(highlights);
|
await this._annotationDrawService.draw(highlights);
|
||||||
this._loadingService.stop();
|
this._loadingService.stop();
|
||||||
@ -192,7 +192,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
|||||||
|
|
||||||
ngOnDetach(): void {
|
ngOnDetach(): void {
|
||||||
this._pageRotationService.clearRotations();
|
this._pageRotationService.clearRotations();
|
||||||
this.displayPdfViewer = false;
|
this.reusablePdf.closeDocument();
|
||||||
super.ngOnDetach();
|
super.ngOnDetach();
|
||||||
this._changeDetectorRef.markForCheck();
|
this._changeDetectorRef.markForCheck();
|
||||||
}
|
}
|
||||||
@ -204,6 +204,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
|||||||
|
|
||||||
this._viewModeService.compareMode = false;
|
this._viewModeService.compareMode = false;
|
||||||
this._viewModeService.switchToStandard();
|
this._viewModeService.switchToStandard();
|
||||||
|
this.state.reloadBlob();
|
||||||
|
|
||||||
await this.ngOnInit();
|
await this.ngOnInit();
|
||||||
await this._fileDataService.loadRedactionLog();
|
await this._fileDataService.loadRedactionLog();
|
||||||
@ -218,7 +219,6 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
|||||||
return this._handleDeletedFile();
|
return this._handleDeletedFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.ready = false;
|
|
||||||
this._loadingService.start();
|
this._loadingService.start();
|
||||||
await this.userPreferenceService.saveLastOpenedFileForDossier(this.dossierId, this.fileId);
|
await this.userPreferenceService.saveLastOpenedFileForDossier(this.dossierId, this.fileId);
|
||||||
this._subscribeToFileUpdates();
|
this._subscribeToFileUpdates();
|
||||||
@ -228,8 +228,6 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
|||||||
const reanalyzeFiles = reanalysisService.reanalyzeFilesForDossier([file], this.dossierId, { force: true });
|
const reanalyzeFiles = reanalysisService.reanalyzeFilesForDossier([file], this.dossierId, { force: true });
|
||||||
await firstValueFrom(reanalyzeFiles);
|
await firstValueFrom(reanalyzeFiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.displayPdfViewer = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAnnotationSelected(annotationIds: string[]) {
|
handleAnnotationSelected(annotationIds: string[]) {
|
||||||
@ -251,9 +249,9 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
|||||||
null,
|
null,
|
||||||
{ manualRedactionEntryWrapper, dossierId: this.dossierId, file },
|
{ manualRedactionEntryWrapper, dossierId: this.dossierId, file },
|
||||||
(wrappers: ManualRedactionEntryWrapper[]) => {
|
(wrappers: ManualRedactionEntryWrapper[]) => {
|
||||||
const selectedAnnotations = this.pdf.annotationManager.getSelectedAnnotations();
|
const selectedAnnotations = this.reusablePdf.annotationManager.getSelectedAnnotations();
|
||||||
if (selectedAnnotations.length > 0) {
|
if (selectedAnnotations.length > 0) {
|
||||||
this.pdf.deleteAnnotations([selectedAnnotations[0].Id]);
|
this.reusablePdf.deleteAnnotations([selectedAnnotations[0].Id]);
|
||||||
}
|
}
|
||||||
const manualRedactionService = this._injector.get(ManualRedactionService);
|
const manualRedactionService = this._injector.get(ManualRedactionService);
|
||||||
const add$ = manualRedactionService.addAnnotation(
|
const add$ = manualRedactionService.addAnnotation(
|
||||||
@ -311,13 +309,6 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
|||||||
|
|
||||||
@Debounce(100)
|
@Debounce(100)
|
||||||
async viewerReady() {
|
async viewerReady() {
|
||||||
this.ready = true;
|
|
||||||
this._setExcludedPageStyles();
|
|
||||||
|
|
||||||
this.pdf.documentViewer.addEventListener('pageComplete', () => {
|
|
||||||
this._setExcludedPageStyles();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Go to initial page from query params
|
// Go to initial page from query params
|
||||||
const pageNumber: string = this._lastPage || this._activatedRoute.snapshot.queryParams.page;
|
const pageNumber: string = this._lastPage || this._activatedRoute.snapshot.queryParams.page;
|
||||||
if (pageNumber) {
|
if (pageNumber) {
|
||||||
@ -356,8 +347,11 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
|||||||
}
|
}
|
||||||
|
|
||||||
loadAnnotations() {
|
loadAnnotations() {
|
||||||
const documentLoaded$ = this.pdf.documentLoaded$.pipe(tap(() => this.viewerReady()));
|
const documentLoaded$ = this.reusablePdf.show$.pipe(
|
||||||
const currentPageAnnotations$ = combineLatest([this.pdf.currentPage$, this._fileDataService.annotations$]).pipe(
|
filter(s => s),
|
||||||
|
tap(() => this.viewerReady()),
|
||||||
|
);
|
||||||
|
const currentPageAnnotations$ = combineLatest([this.reusablePdf.currentPage$, this._fileDataService.annotations$]).pipe(
|
||||||
map(([page, annotations]) => annotations.filter(annotation => annotation.pageNumber === page)),
|
map(([page, annotations]) => annotations.filter(annotation => annotation.pageNumber === page)),
|
||||||
);
|
);
|
||||||
let start;
|
let start;
|
||||||
@ -382,12 +376,12 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._logger.info('[ANNOTATIONS] To delete: ', annotationsToDelete);
|
this._logger.info('[ANNOTATIONS] To delete: ', annotationsToDelete);
|
||||||
this.pdf.deleteAnnotations(annotationsToDelete.map(annotation => annotation.id));
|
this.reusablePdf.deleteAnnotations(annotationsToDelete.map(annotation => annotation.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
drawChangedAnnotations(oldAnnotations: AnnotationWrapper[], newAnnotations: AnnotationWrapper[]) {
|
drawChangedAnnotations(oldAnnotations: AnnotationWrapper[], newAnnotations: AnnotationWrapper[]) {
|
||||||
let annotationsToDraw: readonly AnnotationWrapper[];
|
let annotationsToDraw: readonly AnnotationWrapper[];
|
||||||
const annotationsList = this.pdf.annotationManager.getAnnotationsList();
|
const annotationsList = this.reusablePdf.getAnnotations();
|
||||||
const ann = annotationsList.map(a => oldAnnotations.find(oldAnnotation => oldAnnotation.id === a.Id));
|
const ann = annotationsList.map(a => oldAnnotations.find(oldAnnotation => oldAnnotation.id === a.Id));
|
||||||
const hasAnnotations = ann.filter(a => !!a).length > 0;
|
const hasAnnotations = ann.filter(a => !!a).length > 0;
|
||||||
|
|
||||||
@ -402,7 +396,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._logger.info('[ANNOTATIONS] To draw: ', annotationsToDraw);
|
this._logger.info('[ANNOTATIONS] To draw: ', annotationsToDraw);
|
||||||
this.pdf.deleteAnnotations(annotationsToDraw.map(a => a.annotationId));
|
this.reusablePdf.deleteAnnotations(annotationsToDraw.map(a => a.annotationId));
|
||||||
return this._cleanupAndRedrawAnnotations(annotationsToDraw);
|
return this._cleanupAndRedrawAnnotations(annotationsToDraw);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -478,14 +472,14 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
|||||||
|
|
||||||
#deactivateMultiSelect() {
|
#deactivateMultiSelect() {
|
||||||
this.multiSelectService.deactivate();
|
this.multiSelectService.deactivate();
|
||||||
this.pdf.deselectAllAnnotations();
|
this.reusablePdf.deselectAnnotations();
|
||||||
this.handleAnnotationSelected([]);
|
this.handleAnnotationSelected([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _setExcludedPageStyles() {
|
private _setExcludedPageStyles() {
|
||||||
const file = this._filesMapService.get(this.dossierId, this.fileId);
|
const file = this._filesMapService.get(this.dossierId, this.fileId);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const iframeDoc = this.pdf.UI.iframeWindow.document;
|
const iframeDoc = this.reusablePdf.instance.UI.iframeWindow.document;
|
||||||
const pageContainer = iframeDoc.getElementById(`pageWidgetContainer${this.pdf.currentPage}`);
|
const pageContainer = iframeDoc.getElementById(`pageWidgetContainer${this.pdf.currentPage}`);
|
||||||
if (pageContainer) {
|
if (pageContainer) {
|
||||||
if (file.excludedPages.includes(this.pdf.currentPage)) {
|
if (file.excludedPages.includes(this.pdf.currentPage)) {
|
||||||
@ -519,6 +513,10 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.subscribe();
|
.subscribe();
|
||||||
|
|
||||||
|
this.addActiveScreenSubscription = this.reusablePdf.pageComplete$.subscribe(() => {
|
||||||
|
this._setExcludedPageStyles();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleDeletedDossier(): void {
|
private _handleDeletedDossier(): void {
|
||||||
|
|||||||
@ -35,6 +35,7 @@ import { PdfViewer } from './pdf-viewer.service';
|
|||||||
import { FilePreviewDialogService } from './file-preview-dialog.service';
|
import { FilePreviewDialogService } from './file-preview-dialog.service';
|
||||||
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
|
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
|
||||||
import { FileDataService } from './file-data.service';
|
import { FileDataService } from './file-data.service';
|
||||||
|
import { ReusablePdfViewer } from '../../shared/components/reusable-pdf-viewer/reusable-pdf-viewer.service';
|
||||||
import Quad = Core.Math.Quad;
|
import Quad = Core.Math.Quad;
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@ -49,6 +50,7 @@ export class AnnotationActionsService {
|
|||||||
private readonly _dialogService: FilePreviewDialogService,
|
private readonly _dialogService: FilePreviewDialogService,
|
||||||
private readonly _dialog: MatDialog,
|
private readonly _dialog: MatDialog,
|
||||||
private readonly _pdf: PdfViewer,
|
private readonly _pdf: PdfViewer,
|
||||||
|
private readonly _reusablePdf: ReusablePdfViewer,
|
||||||
private readonly _annotationDrawService: AnnotationDrawService,
|
private readonly _annotationDrawService: AnnotationDrawService,
|
||||||
private readonly _activeDossiersService: ActiveDossiersService,
|
private readonly _activeDossiersService: ActiveDossiersService,
|
||||||
private readonly _dictionariesMapService: DictionariesMapService,
|
private readonly _dictionariesMapService: DictionariesMapService,
|
||||||
@ -395,21 +397,21 @@ export class AnnotationActionsService {
|
|||||||
|
|
||||||
annotationWrapper.resizing = true;
|
annotationWrapper.resizing = true;
|
||||||
|
|
||||||
const viewerAnnotation = this._pdf.annotationManager.getAnnotationById(annotationWrapper.id);
|
const viewerAnnotation = this._reusablePdf.annotationManager.getAnnotationById(annotationWrapper.id);
|
||||||
if (annotationWrapper.rectangle || annotationWrapper.imported || annotationWrapper.isImage) {
|
if (annotationWrapper.rectangle || annotationWrapper.imported || annotationWrapper.isImage) {
|
||||||
this._pdf.deleteAnnotations([annotationWrapper.id]);
|
this._reusablePdf.deleteAnnotations([annotationWrapper.id]);
|
||||||
const rectangleAnnotation = this.#generateRectangle(annotationWrapper);
|
const rectangleAnnotation = this.#generateRectangle(annotationWrapper);
|
||||||
this._pdf.annotationManager.addAnnotation(rectangleAnnotation, { imported: true });
|
this._reusablePdf.annotationManager.addAnnotation(rectangleAnnotation, { imported: true });
|
||||||
await this._pdf.annotationManager.drawAnnotationsFromList([rectangleAnnotation]);
|
await this._reusablePdf.annotationManager.drawAnnotationsFromList([rectangleAnnotation]);
|
||||||
this._pdf.annotationManager.selectAnnotation(rectangleAnnotation);
|
this._reusablePdf.annotationManager.selectAnnotation(rectangleAnnotation);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
viewerAnnotation.ReadOnly = false;
|
viewerAnnotation.ReadOnly = false;
|
||||||
viewerAnnotation.Hidden = false;
|
viewerAnnotation.Hidden = false;
|
||||||
viewerAnnotation.disableRotationControl();
|
viewerAnnotation.disableRotationControl();
|
||||||
this._pdf.annotationManager.redrawAnnotation(viewerAnnotation);
|
this._reusablePdf.annotationManager.redrawAnnotation(viewerAnnotation);
|
||||||
this._pdf.annotationManager.selectAnnotation(viewerAnnotation);
|
this._reusablePdf.annotationManager.selectAnnotation(viewerAnnotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
async acceptResize($event: MouseEvent, annotation: AnnotationWrapper): Promise<void> {
|
async acceptResize($event: MouseEvent, annotation: AnnotationWrapper): Promise<void> {
|
||||||
@ -435,9 +437,9 @@ export class AnnotationActionsService {
|
|||||||
|
|
||||||
annotationWrapper.resizing = false;
|
annotationWrapper.resizing = false;
|
||||||
|
|
||||||
this._pdf.deleteAnnotations([annotationWrapper.id]);
|
this._reusablePdf.deleteAnnotations([annotationWrapper.id]);
|
||||||
await this._annotationDrawService.draw([annotationWrapper]);
|
await this._annotationDrawService.draw([annotationWrapper]);
|
||||||
this._pdf.annotationManager.deselectAllAnnotations();
|
this._reusablePdf.deselectAnnotations();
|
||||||
await this._fileDataService.annotationsChanged();
|
await this._fileDataService.annotationsChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -461,8 +463,8 @@ export class AnnotationActionsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#generateRectangle(annotationWrapper: AnnotationWrapper) {
|
#generateRectangle(annotationWrapper: AnnotationWrapper) {
|
||||||
const annotation = new this._pdf.Annotations.RectangleAnnotation();
|
const annotation = this._reusablePdf.rectangle();
|
||||||
const pageHeight = this._pdf.documentViewer.getPageHeight(annotationWrapper.pageNumber);
|
const pageHeight = this._reusablePdf.documentViewer.getPageHeight(annotationWrapper.pageNumber);
|
||||||
const rectangle: IRectangle = annotationWrapper.positions[0];
|
const rectangle: IRectangle = annotationWrapper.positions[0];
|
||||||
annotation.PageNumber = annotationWrapper.pageNumber;
|
annotation.PageNumber = annotationWrapper.pageNumber;
|
||||||
annotation.X = rectangle.topLeft.x;
|
annotation.X = rectangle.topLeft.x;
|
||||||
@ -527,17 +529,17 @@ export class AnnotationActionsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async _extractTextAndPositions(annotationId: string) {
|
private async _extractTextAndPositions(annotationId: string) {
|
||||||
const viewerAnnotation = this._pdf.annotationManager.getAnnotationById(annotationId);
|
const viewerAnnotation = this._reusablePdf.annotationManager.getAnnotationById(annotationId);
|
||||||
|
|
||||||
const document = await this._pdf.documentViewer.getDocument().getPDFDoc();
|
const document = await this._reusablePdf.PDFDoc;
|
||||||
const page = await document.getPage(viewerAnnotation.getPageNumber());
|
const page = await document.getPage(viewerAnnotation.getPageNumber());
|
||||||
if (viewerAnnotation instanceof this._pdf.Annotations.TextHighlightAnnotation) {
|
if (this._reusablePdf.isTextHighlight(viewerAnnotation)) {
|
||||||
const words = [];
|
const words = [];
|
||||||
const rectangles: IRectangle[] = [];
|
const rectangles: IRectangle[] = [];
|
||||||
for (const quad of viewerAnnotation.Quads) {
|
for (const quad of viewerAnnotation.Quads) {
|
||||||
const rect = toPosition(
|
const rect = toPosition(
|
||||||
viewerAnnotation.getPageNumber(),
|
viewerAnnotation.getPageNumber(),
|
||||||
this._pdf.documentViewer.getPageHeight(viewerAnnotation.getPageNumber()),
|
this._reusablePdf.getPageHeight(viewerAnnotation.getPageNumber()),
|
||||||
this._translateQuads(viewerAnnotation.getPageNumber(), quad),
|
this._translateQuads(viewerAnnotation.getPageNumber(), quad),
|
||||||
);
|
);
|
||||||
rectangles.push(rect);
|
rectangles.push(rect);
|
||||||
@ -548,7 +550,7 @@ export class AnnotationActionsService {
|
|||||||
*/
|
*/
|
||||||
const percentHeightOffset = rect.height / 10;
|
const percentHeightOffset = rect.height / 10;
|
||||||
|
|
||||||
const pdfNetRect = new this._pdf.instance.Core.PDFNet.Rect(
|
const pdfNetRect = new this._reusablePdf.PDFNet.Rect(
|
||||||
rect.topLeft.x,
|
rect.topLeft.x,
|
||||||
rect.topLeft.y + percentHeightOffset,
|
rect.topLeft.y + percentHeightOffset,
|
||||||
rect.topLeft.x + rect.width,
|
rect.topLeft.x + rect.width,
|
||||||
@ -565,7 +567,7 @@ export class AnnotationActionsService {
|
|||||||
} else {
|
} else {
|
||||||
const rect = toPosition(
|
const rect = toPosition(
|
||||||
viewerAnnotation.getPageNumber(),
|
viewerAnnotation.getPageNumber(),
|
||||||
this._pdf.documentViewer.getPageHeight(viewerAnnotation.getPageNumber()),
|
this._reusablePdf.documentViewer.getPageHeight(viewerAnnotation.getPageNumber()),
|
||||||
this._annotationDrawService.annotationToQuads(viewerAnnotation),
|
this._annotationDrawService.annotationToQuads(viewerAnnotation),
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
@ -576,13 +578,13 @@ export class AnnotationActionsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _translateQuads(page: number, quad: Quad): Quad {
|
private _translateQuads(page: number, quad: Quad): Quad {
|
||||||
const rotation = this._pdf.documentViewer.getCompleteRotation(page);
|
const rotation = this._reusablePdf.documentViewer.getCompleteRotation(page);
|
||||||
return translateQuads(page, rotation, quad);
|
return translateQuads(page, rotation, quad);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _extractTextFromRect(page: Core.PDFNet.Page, rect: Core.PDFNet.Rect) {
|
private async _extractTextFromRect(page: Core.PDFNet.Page, rect: Core.PDFNet.Rect) {
|
||||||
const txt = await this._pdf.PDFNet.TextExtractor.create();
|
const txt = await this._reusablePdf.PDFNet.TextExtractor.create();
|
||||||
txt.begin(page, rect); // Read the page.
|
await txt.begin(page, rect); // Read the page.
|
||||||
|
|
||||||
const words: string[] = [];
|
const words: string[] = [];
|
||||||
// Extract words one by one.
|
// Extract words one by one.
|
||||||
|
|||||||
@ -15,6 +15,7 @@ import { FilePreviewStateService } from './file-preview-state.service';
|
|||||||
import { ViewModeService } from './view-mode.service';
|
import { ViewModeService } from './view-mode.service';
|
||||||
import { FileDataService } from './file-data.service';
|
import { FileDataService } from './file-data.service';
|
||||||
import { SuperTypes } from '@models/file/super-types';
|
import { SuperTypes } from '@models/file/super-types';
|
||||||
|
import { ReusablePdfViewer } from '../../shared/components/reusable-pdf-viewer/reusable-pdf-viewer.service';
|
||||||
import Annotation = Core.Annotations.Annotation;
|
import Annotation = Core.Annotations.Annotation;
|
||||||
import Quad = Core.Math.Quad;
|
import Quad = Core.Math.Quad;
|
||||||
|
|
||||||
@ -29,6 +30,7 @@ export class AnnotationDrawService {
|
|||||||
private readonly _userPreferenceService: UserPreferenceService,
|
private readonly _userPreferenceService: UserPreferenceService,
|
||||||
private readonly _skippedService: SkippedService,
|
private readonly _skippedService: SkippedService,
|
||||||
private readonly _pdf: PdfViewer,
|
private readonly _pdf: PdfViewer,
|
||||||
|
private readonly _reusablePdf: ReusablePdfViewer,
|
||||||
private readonly _state: FilePreviewStateService,
|
private readonly _state: FilePreviewStateService,
|
||||||
private readonly _viewModeService: ViewModeService,
|
private readonly _viewModeService: ViewModeService,
|
||||||
private readonly _fileDataService: FileDataService,
|
private readonly _fileDataService: FileDataService,
|
||||||
@ -36,7 +38,7 @@ export class AnnotationDrawService {
|
|||||||
|
|
||||||
draw(annotations: readonly AnnotationWrapper[]) {
|
draw(annotations: readonly AnnotationWrapper[]) {
|
||||||
const licenseKey = environment.licenseKey ? atob(environment.licenseKey) : null;
|
const licenseKey = environment.licenseKey ? atob(environment.licenseKey) : null;
|
||||||
return this._pdf.PDFNet.runWithCleanup(() => this._draw(annotations), licenseKey);
|
return this._reusablePdf.PDFNet.runWithCleanup(() => this._draw(annotations), licenseKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
getColor(superType: string, dictionary?: string) {
|
getColor(superType: string, dictionary?: string) {
|
||||||
@ -64,8 +66,7 @@ export class AnnotationDrawService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
convertColor(hexColor: string) {
|
convertColor(hexColor: string) {
|
||||||
const rgbColor = hexToRgb(hexColor);
|
return this._reusablePdf.color(hexToRgb(hexColor));
|
||||||
return new this._pdf.Annotations.Color(rgbColor.r, rgbColor.g, rgbColor.b);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
annotationToQuads(annotation: Annotation) {
|
annotationToQuads(annotation: Annotation) {
|
||||||
@ -81,13 +82,13 @@ export class AnnotationDrawService {
|
|||||||
const x4 = annotation.getRect().x1;
|
const x4 = annotation.getRect().x1;
|
||||||
const y4 = annotation.getRect().y1;
|
const y4 = annotation.getRect().y1;
|
||||||
|
|
||||||
return new this._pdf.instance.Core.Math.Quad(x1, y1, x2, y2, x3, y3, x4, y4);
|
return this._reusablePdf.quad(x1, y1, x2, y2, x3, y3, x4, y4);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _draw(annotationWrappers: readonly AnnotationWrapper[]) {
|
private async _draw(annotationWrappers: readonly AnnotationWrapper[]) {
|
||||||
const annotations = annotationWrappers.map(annotation => this._computeAnnotation(annotation)).filter(a => !!a);
|
const annotations = annotationWrappers.map(annotation => this._computeAnnotation(annotation)).filter(a => !!a);
|
||||||
this._pdf.annotationManager.addAnnotations(annotations, { imported: true });
|
this._reusablePdf.annotationManager.addAnnotations(annotations, { imported: true });
|
||||||
await this._pdf.annotationManager.drawAnnotationsFromList(annotations);
|
await this._reusablePdf.annotationManager.drawAnnotationsFromList(annotations);
|
||||||
|
|
||||||
if (this._userPreferenceService.areDevFeaturesEnabled) {
|
if (this._userPreferenceService.areDevFeaturesEnabled) {
|
||||||
const { dossierId, fileId } = this._state;
|
const { dossierId, fileId } = this._state;
|
||||||
@ -108,14 +109,13 @@ export class AnnotationDrawService {
|
|||||||
// })
|
// })
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const annotationManager = this._pdf.annotationManager;
|
this._reusablePdf.annotationManager.addAnnotations(sections, { imported: true });
|
||||||
annotationManager.addAnnotations(sections, { imported: true });
|
await this._reusablePdf.annotationManager.drawAnnotationsFromList(sections);
|
||||||
await annotationManager.drawAnnotationsFromList(sections);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _computeSection(pageNumber: number, sectionRectangle: ISectionRectangle) {
|
private _computeSection(pageNumber: number, sectionRectangle: ISectionRectangle) {
|
||||||
const rectangleAnnot = new this._pdf.Annotations.RectangleAnnotation();
|
const rectangleAnnot = this._reusablePdf.rectangle();
|
||||||
const pageHeight = this._pdf.documentViewer.getPageHeight(pageNumber);
|
const pageHeight = this._reusablePdf.getPageHeight(pageNumber);
|
||||||
const rectangle: IRectangle = {
|
const rectangle: IRectangle = {
|
||||||
topLeft: sectionRectangle.topLeft,
|
topLeft: sectionRectangle.topLeft,
|
||||||
page: pageNumber,
|
page: pageNumber,
|
||||||
@ -136,14 +136,14 @@ export class AnnotationDrawService {
|
|||||||
|
|
||||||
private _computeAnnotation(annotationWrapper: AnnotationWrapper) {
|
private _computeAnnotation(annotationWrapper: AnnotationWrapper) {
|
||||||
const pageNumber = this._viewModeService.isCompare ? annotationWrapper.pageNumber * 2 - 1 : annotationWrapper.pageNumber;
|
const pageNumber = this._viewModeService.isCompare ? annotationWrapper.pageNumber * 2 - 1 : annotationWrapper.pageNumber;
|
||||||
if (pageNumber > this._pdf.pageCount) {
|
if (pageNumber > this._reusablePdf.pageCount) {
|
||||||
// skip imported annotations from files that have more pages than the current one
|
// skip imported annotations from files that have more pages than the current one
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (annotationWrapper.superType === SuperTypes.TextHighlight) {
|
if (annotationWrapper.superType === SuperTypes.TextHighlight) {
|
||||||
const rectangleAnnot = new this._pdf.Annotations.RectangleAnnotation();
|
const rectangleAnnot = this._reusablePdf.rectangle();
|
||||||
const pageHeight = this._pdf.documentViewer.getPageHeight(pageNumber);
|
const pageHeight = this._reusablePdf.getPageHeight(pageNumber);
|
||||||
const rectangle: IRectangle = annotationWrapper.positions[0];
|
const rectangle: IRectangle = annotationWrapper.positions[0];
|
||||||
rectangleAnnot.PageNumber = pageNumber;
|
rectangleAnnot.PageNumber = pageNumber;
|
||||||
rectangleAnnot.X = rectangle.topLeft.x;
|
rectangleAnnot.X = rectangle.topLeft.x;
|
||||||
@ -158,7 +158,7 @@ export class AnnotationDrawService {
|
|||||||
return rectangleAnnot;
|
return rectangleAnnot;
|
||||||
}
|
}
|
||||||
|
|
||||||
const annotation = new this._pdf.Annotations.TextHighlightAnnotation();
|
const annotation = this._reusablePdf.textHighlight();
|
||||||
annotation.Quads = this._rectanglesToQuads(annotationWrapper.positions, pageNumber);
|
annotation.Quads = this._rectanglesToQuads(annotationWrapper.positions, pageNumber);
|
||||||
annotation.Opacity = annotationWrapper.isChangeLogRemoved ? DEFAULT_REMOVED_ANNOTATION_OPACITY : DEFAULT_TEXT_ANNOTATION_OPACITY;
|
annotation.Opacity = annotationWrapper.isChangeLogRemoved ? DEFAULT_REMOVED_ANNOTATION_OPACITY : DEFAULT_TEXT_ANNOTATION_OPACITY;
|
||||||
annotation.setContents(annotationWrapper.content);
|
annotation.setContents(annotationWrapper.content);
|
||||||
@ -185,7 +185,7 @@ export class AnnotationDrawService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _rectanglesToQuads(positions: IRectangle[], pageNumber: number): Quad[] {
|
private _rectanglesToQuads(positions: IRectangle[], pageNumber: number): Quad[] {
|
||||||
const pageHeight = this._pdf.documentViewer.getPageHeight(pageNumber);
|
const pageHeight = this._reusablePdf.getPageHeight(pageNumber);
|
||||||
return positions.map(p => this._rectangleToQuad(p, pageHeight));
|
return positions.map(p => this._rectangleToQuad(p, pageHeight));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,6 +202,6 @@ export class AnnotationDrawService {
|
|||||||
const x4 = rectangle.topLeft.x;
|
const x4 = rectangle.topLeft.x;
|
||||||
const y4 = pageHeight - rectangle.topLeft.y;
|
const y4 = pageHeight - rectangle.topLeft.y;
|
||||||
|
|
||||||
return new this._pdf.instance.Core.Math.Quad(x1, y1, x2, y2, x3, y3, x4, y4);
|
return this._reusablePdf.quad(x1, y1, x2, y2, x3, y3, x4, y4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -71,14 +71,16 @@ export class FilePreviewStateService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get #blob$() {
|
get #blob$() {
|
||||||
const reloadBlob$ = this.#reloadBlob$.pipe(
|
const file$ = this.file$.pipe(
|
||||||
withLatestFrom(this.file$),
|
|
||||||
map(([, file]) => file),
|
|
||||||
);
|
|
||||||
return merge(this.file$, reloadBlob$).pipe(
|
|
||||||
startWith(undefined),
|
startWith(undefined),
|
||||||
pairwise(),
|
pairwise(),
|
||||||
filter(([oldFile, newFile]) => oldFile?.cacheIdentifier !== newFile.cacheIdentifier),
|
filter(([oldFile, newFile]) => oldFile?.cacheIdentifier !== newFile.cacheIdentifier),
|
||||||
|
);
|
||||||
|
const reloadBlob$ = this.#reloadBlob$.pipe(
|
||||||
|
withLatestFrom(file$),
|
||||||
|
map(([, file]) => file),
|
||||||
|
);
|
||||||
|
return merge(file$, reloadBlob$).pipe(
|
||||||
switchMap(([oldFile, newFile]) => this.#downloadOriginalFile(newFile.cacheIdentifier, !!oldFile?.cacheIdentifier)),
|
switchMap(([oldFile, newFile]) => this.#downloadOriginalFile(newFile.cacheIdentifier, !!oldFile?.cacheIdentifier)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,6 +17,7 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
|||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
import { ViewerHeaderConfigService } from './viewer-header-config.service';
|
import { ViewerHeaderConfigService } from './viewer-header-config.service';
|
||||||
import { FilesService } from '@services/files/files.service';
|
import { FilesService } from '@services/files/files.service';
|
||||||
|
import { ReusablePdfViewer } from '../../shared/components/reusable-pdf-viewer/reusable-pdf-viewer.service';
|
||||||
|
|
||||||
const ACTION_BUTTONS = [HeaderElements.APPLY_ROTATION, HeaderElements.DISCARD_ROTATION];
|
const ACTION_BUTTONS = [HeaderElements.APPLY_ROTATION, HeaderElements.DISCARD_ROTATION];
|
||||||
const ONE_ROTATION_DEGREE = 90;
|
const ONE_ROTATION_DEGREE = 90;
|
||||||
@ -27,6 +28,7 @@ export class PageRotationService {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _pdf: PdfViewer,
|
private readonly _pdf: PdfViewer,
|
||||||
|
private readonly _reusablePdf: ReusablePdfViewer,
|
||||||
private readonly _dialog: MatDialog,
|
private readonly _dialog: MatDialog,
|
||||||
private readonly _loadingService: LoadingService,
|
private readonly _loadingService: LoadingService,
|
||||||
private readonly _screenState: FilePreviewStateService,
|
private readonly _screenState: FilePreviewStateService,
|
||||||
@ -68,7 +70,7 @@ export class PageRotationService {
|
|||||||
for (const page of Object.keys(rotations)) {
|
for (const page of Object.keys(rotations)) {
|
||||||
const times = rotations[page] / ONE_ROTATION_DEGREE;
|
const times = rotations[page] / ONE_ROTATION_DEGREE;
|
||||||
for (let i = 1; i <= times; i++) {
|
for (let i = 1; i <= times; i++) {
|
||||||
this._pdf?.documentViewer?.rotateCounterClockwise(Number(page));
|
this._reusablePdf.documentViewer.rotateCounterClockwise(Number(page));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,9 +85,9 @@ export class PageRotationService {
|
|||||||
this.#rotations$.next({ ...this.#rotations$.value, [pageNumber]: rotationValue });
|
this.#rotations$.next({ ...this.#rotations$.value, [pageNumber]: rotationValue });
|
||||||
|
|
||||||
if (rotation === RotationTypes.LEFT) {
|
if (rotation === RotationTypes.LEFT) {
|
||||||
this._pdf.documentViewer.rotateCounterClockwise(pageNumber);
|
this._reusablePdf.documentViewer.rotateCounterClockwise(pageNumber);
|
||||||
} else {
|
} else {
|
||||||
this._pdf.documentViewer.rotateClockwise(pageNumber);
|
this._reusablePdf.documentViewer.rotateClockwise(pageNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.hasRotations()) {
|
if (this.hasRotations()) {
|
||||||
|
|||||||
@ -1,69 +1,21 @@
|
|||||||
import { translateQuads } from '../../../utils';
|
import { translateQuads } from '../../../utils';
|
||||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||||
import WebViewer, { Core, WebViewerInstance } from '@pdftron/webviewer';
|
import { Core } from '@pdftron/webviewer';
|
||||||
import { ViewModeService } from './view-mode.service';
|
import { ViewModeService } from './view-mode.service';
|
||||||
import { File } from '@red/domain';
|
import { File } from '@red/domain';
|
||||||
import { Inject, Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { BASE_HREF } from '../../../tokens';
|
import { ListingService } from '@iqser/common-ui';
|
||||||
import { environment } from '@environments/environment';
|
|
||||||
import { DISABLED_HOTKEYS } from '../utils/constants';
|
|
||||||
import { Observable, Subject } from 'rxjs';
|
|
||||||
import { NGXLogger } from 'ngx-logger';
|
|
||||||
import { map, tap } from 'rxjs/operators';
|
|
||||||
import { ListingService, shareDistinctLast, shareLast } from '@iqser/common-ui';
|
|
||||||
import { MultiSelectService } from './multi-select.service';
|
import { MultiSelectService } from './multi-select.service';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ReusablePdfViewer } from '../../shared/components/reusable-pdf-viewer/reusable-pdf-viewer.service';
|
||||||
import Annotation = Core.Annotations.Annotation;
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PdfViewer {
|
export class PdfViewer {
|
||||||
instance?: WebViewerInstance;
|
|
||||||
|
|
||||||
readonly documentLoaded$: Observable<boolean>;
|
|
||||||
readonly currentPage$ = this._activatedRoute.queryParamMap.pipe(
|
|
||||||
map(params => Number(params.get('page') ?? '1')),
|
|
||||||
shareDistinctLast(),
|
|
||||||
);
|
|
||||||
readonly #documentLoaded$ = new Subject<boolean>();
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(BASE_HREF) private readonly _baseHref: string,
|
|
||||||
private readonly _viewModeService: ViewModeService,
|
private readonly _viewModeService: ViewModeService,
|
||||||
private readonly _activatedRoute: ActivatedRoute,
|
|
||||||
private readonly _multiSelectService: MultiSelectService,
|
private readonly _multiSelectService: MultiSelectService,
|
||||||
|
private readonly _reusablePdf: ReusablePdfViewer,
|
||||||
private readonly _listingService: ListingService<AnnotationWrapper>,
|
private readonly _listingService: ListingService<AnnotationWrapper>,
|
||||||
private readonly _logger: NGXLogger,
|
) {}
|
||||||
) {
|
|
||||||
this.documentLoaded$ = this.#documentLoaded$.asObservable().pipe(
|
|
||||||
tap(() => this._logger.debug('[PDF] Loaded')),
|
|
||||||
tap(() => this.#setCurrentPage()),
|
|
||||||
shareLast(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
get documentViewer() {
|
|
||||||
return this.instance?.Core.documentViewer;
|
|
||||||
}
|
|
||||||
|
|
||||||
get annotationManager() {
|
|
||||||
return this.instance?.Core.annotationManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
get UI() {
|
|
||||||
return this.instance.UI;
|
|
||||||
}
|
|
||||||
|
|
||||||
get Annotations() {
|
|
||||||
return this.instance.Core.Annotations;
|
|
||||||
}
|
|
||||||
|
|
||||||
get PDFNet(): typeof Core.PDFNet {
|
|
||||||
return this.instance.Core.PDFNet;
|
|
||||||
}
|
|
||||||
|
|
||||||
get hasAnnotations() {
|
|
||||||
return this.annotationManager?.getAnnotationsList().length > 0 ?? false;
|
|
||||||
}
|
|
||||||
|
|
||||||
get paginationOffset() {
|
get paginationOffset() {
|
||||||
return this._viewModeService.isCompare ? 2 : 1;
|
return this._viewModeService.isCompare ? 2 : 1;
|
||||||
@ -74,72 +26,16 @@ export class PdfViewer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get totalPages() {
|
get totalPages() {
|
||||||
return this._viewModeService.isCompare ? Math.ceil(this.pageCount / 2) : this.pageCount;
|
const pageCount = this._reusablePdf.pageCount;
|
||||||
}
|
return this._viewModeService.isCompare ? Math.ceil(pageCount / 2) : pageCount;
|
||||||
|
|
||||||
get pageCount() {
|
|
||||||
try {
|
|
||||||
return this.instance?.Core.documentViewer?.getPageCount() ?? 1;
|
|
||||||
} catch {
|
|
||||||
// might throw Error: getPageCount was called before the 'documentLoaded' event
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private get _currentInternalPage() {
|
private get _currentInternalPage() {
|
||||||
return this.instance?.Core.documentViewer?.getCurrentPage() ?? 1;
|
return this._reusablePdf.documentViewer?.getCurrentPage() ?? 1;
|
||||||
}
|
|
||||||
|
|
||||||
async lockDocument() {
|
|
||||||
const document = await this.documentViewer.getDocument()?.getPDFDoc();
|
|
||||||
if (!document) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
await document.lock();
|
|
||||||
this._logger.debug('[PDF] Locked');
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
hideAnnotations(annotations: Annotation[]): void {
|
|
||||||
this.annotationManager.hideAnnotations(annotations);
|
|
||||||
}
|
|
||||||
|
|
||||||
showAnnotations(annotations: Annotation[]): void {
|
|
||||||
this.annotationManager.showAnnotations(annotations);
|
|
||||||
}
|
|
||||||
|
|
||||||
getAnnotations(predicate?: (value) => boolean) {
|
|
||||||
const annotations = this.annotationManager?.getAnnotationsList() ?? [];
|
|
||||||
return predicate ? annotations.filter(predicate) : annotations;
|
|
||||||
}
|
|
||||||
|
|
||||||
getAnnotationsById(ids: readonly string[]) {
|
|
||||||
if (this.annotationManager) {
|
|
||||||
return ids.map(id => this.annotationManager.getAnnotationById(id)).filter(a => !!a);
|
|
||||||
}
|
|
||||||
|
|
||||||
return [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
emitDocumentLoaded() {
|
emitDocumentLoaded() {
|
||||||
this.deleteAnnotations();
|
this._reusablePdf.deleteAnnotations();
|
||||||
this.#documentLoaded$.next(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
async loadViewer(htmlElement: HTMLElement) {
|
|
||||||
this.instance = await WebViewer(
|
|
||||||
{
|
|
||||||
licenseKey: environment.licenseKey ? atob(environment.licenseKey) : null,
|
|
||||||
fullAPI: true,
|
|
||||||
path: this.#convertPath('/assets/wv-resources'),
|
|
||||||
css: this.#convertPath('/assets/pdftron/stylesheet.css'),
|
|
||||||
backendType: 'ems',
|
|
||||||
},
|
|
||||||
htmlElement,
|
|
||||||
);
|
|
||||||
|
|
||||||
return this.instance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isCurrentPageExcluded(file: File) {
|
isCurrentPageExcluded(file: File) {
|
||||||
@ -159,55 +55,26 @@ export class PdfViewer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
navigateNextPage() {
|
navigateNextPage() {
|
||||||
if (this._currentInternalPage < this.pageCount) {
|
const pageCount = this._reusablePdf.pageCount;
|
||||||
this._navigateToPage(Math.min(this._currentInternalPage + this.paginationOffset, this.pageCount));
|
if (this._currentInternalPage < pageCount) {
|
||||||
|
this._navigateToPage(Math.min(this._currentInternalPage + this.paginationOffset, pageCount));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
disableHotkeys(): void {
|
|
||||||
DISABLED_HOTKEYS.forEach(key => this.instance.UI.hotkeys.off(key));
|
|
||||||
}
|
|
||||||
|
|
||||||
translateQuad(page: number, quad: Core.Math.Quad) {
|
translateQuad(page: number, quad: Core.Math.Quad) {
|
||||||
const rotation = this.documentViewer.getCompleteRotation(page);
|
const rotation = this._reusablePdf.documentViewer.getCompleteRotation(page);
|
||||||
return translateQuads(page, rotation, quad);
|
return translateQuads(page, rotation, quad);
|
||||||
}
|
}
|
||||||
|
|
||||||
deselectAllAnnotations() {
|
|
||||||
this.annotationManager?.deselectAllAnnotations();
|
|
||||||
}
|
|
||||||
|
|
||||||
selectAnnotations(annotations?: AnnotationWrapper[]) {
|
selectAnnotations(annotations?: AnnotationWrapper[]) {
|
||||||
if (!annotations) {
|
if (!annotations) {
|
||||||
return this.deselectAllAnnotations();
|
return this._reusablePdf.deselectAnnotations();
|
||||||
}
|
}
|
||||||
|
|
||||||
const annotationsToSelect = this._multiSelectService.isActive ? [...this._listingService.selected, ...annotations] : annotations;
|
const annotationsToSelect = this._multiSelectService.isActive ? [...this._listingService.selected, ...annotations] : annotations;
|
||||||
this.#selectAnnotations(annotationsToSelect);
|
this.#selectAnnotations(annotationsToSelect);
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteAnnotations(annotationsIds?: readonly string[]) {
|
|
||||||
let annotations: Annotation[];
|
|
||||||
if (!annotationsIds) {
|
|
||||||
annotations = this.getAnnotations();
|
|
||||||
} else {
|
|
||||||
annotations = this.getAnnotationsById(annotationsIds);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
this.annotationManager.deleteAnnotations(annotations, {
|
|
||||||
force: true,
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.log('Error while deleting annotations: ', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
deselectAnnotations(annotations: AnnotationWrapper[]) {
|
|
||||||
const ann = this.getAnnotationsById(annotations.map(a => a.id));
|
|
||||||
this.annotationManager.deselectAnnotations(ann);
|
|
||||||
}
|
|
||||||
|
|
||||||
#selectAnnotations(annotations: AnnotationWrapper[] = []) {
|
#selectAnnotations(annotations: AnnotationWrapper[] = []) {
|
||||||
const filteredAnnotationsIds = annotations.filter(a => !!a).map(a => a.id);
|
const filteredAnnotationsIds = annotations.filter(a => !!a).map(a => a.id);
|
||||||
|
|
||||||
@ -216,7 +83,7 @@ export class PdfViewer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!this._multiSelectService.isActive) {
|
if (!this._multiSelectService.isActive) {
|
||||||
this.deselectAllAnnotations();
|
this._reusablePdf.deselectAnnotations();
|
||||||
}
|
}
|
||||||
|
|
||||||
const pageNumber = annotations[0].pageNumber;
|
const pageNumber = annotations[0].pageNumber;
|
||||||
@ -231,24 +98,15 @@ export class PdfViewer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#jumpAndSelectAnnotations(annotationIds: readonly string[]) {
|
#jumpAndSelectAnnotations(annotationIds: readonly string[]) {
|
||||||
const annotationsFromViewer = this.getAnnotationsById(annotationIds);
|
const annotationsFromViewer = this._reusablePdf.getAnnotationsById(annotationIds);
|
||||||
|
|
||||||
this.annotationManager.jumpToAnnotation(annotationsFromViewer[0]);
|
this._reusablePdf.annotationManager.jumpToAnnotation(annotationsFromViewer[0]);
|
||||||
this.annotationManager.selectAnnotations(annotationsFromViewer);
|
this._reusablePdf.annotationManager.selectAnnotations(annotationsFromViewer);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _navigateToPage(pageNumber: number) {
|
private _navigateToPage(pageNumber: number) {
|
||||||
if (this._currentInternalPage !== pageNumber) {
|
if (this._currentInternalPage !== pageNumber) {
|
||||||
this.documentViewer.displayPageLocation(pageNumber, 0, 0);
|
this._reusablePdf.documentViewer.displayPageLocation(pageNumber, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#convertPath(path: string) {
|
|
||||||
return `${this._baseHref}${path}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
#setCurrentPage() {
|
|
||||||
const currentDocPage = this._activatedRoute.snapshot.queryParamMap.get('page');
|
|
||||||
this.documentViewer.setCurrentPage(Number(currentDocPage ?? '1'));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,14 +2,14 @@ import { Injectable } from '@angular/core';
|
|||||||
import { BehaviorSubject, Observable } from 'rxjs';
|
import { BehaviorSubject, Observable } from 'rxjs';
|
||||||
import { skip, tap } from 'rxjs/operators';
|
import { skip, tap } from 'rxjs/operators';
|
||||||
import { shareDistinctLast } from '@iqser/common-ui';
|
import { shareDistinctLast } from '@iqser/common-ui';
|
||||||
import { PdfViewer } from './pdf-viewer.service';
|
import { ReusablePdfViewer } from '../../shared/components/reusable-pdf-viewer/reusable-pdf-viewer.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SkippedService {
|
export class SkippedService {
|
||||||
readonly hideSkipped$: Observable<boolean>;
|
readonly hideSkipped$: Observable<boolean>;
|
||||||
readonly #hideSkipped$ = new BehaviorSubject(false);
|
readonly #hideSkipped$ = new BehaviorSubject(false);
|
||||||
|
|
||||||
constructor(private readonly _pdf: PdfViewer) {
|
constructor(private readonly _pdf: ReusablePdfViewer) {
|
||||||
this.hideSkipped$ = this.#hideSkipped$.pipe(
|
this.hideSkipped$ = this.#hideSkipped$.pipe(
|
||||||
tap(hideSkipped => this._handleIgnoreAnnotationsDrawing(hideSkipped)),
|
tap(hideSkipped => this._handleIgnoreAnnotationsDrawing(hideSkipped)),
|
||||||
shareDistinctLast(),
|
shareDistinctLast(),
|
||||||
@ -28,7 +28,7 @@ export class SkippedService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _handleIgnoreAnnotationsDrawing(hideSkipped: boolean): void {
|
private _handleIgnoreAnnotationsDrawing(hideSkipped: boolean): void {
|
||||||
const ignored = this._pdf.getAnnotations(a => a.getCustomData('skipped'));
|
const ignored = this._pdf.getAnnotations(a => Boolean(a.getCustomData('skipped')));
|
||||||
if (hideSkipped) {
|
if (hideSkipped) {
|
||||||
this._pdf.hideAnnotations(ignored);
|
this._pdf.hideAnnotations(ignored);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -8,12 +8,14 @@ import { TranslateService } from '@ngx-translate/core';
|
|||||||
import { Core } from '@pdftron/webviewer';
|
import { Core } from '@pdftron/webviewer';
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
import { WatermarkService } from '@services/entity-services/watermark.service';
|
import { WatermarkService } from '@services/entity-services/watermark.service';
|
||||||
|
import { ReusablePdfViewer } from '../../shared/components/reusable-pdf-viewer/reusable-pdf-viewer.service';
|
||||||
import PDFNet = Core.PDFNet;
|
import PDFNet = Core.PDFNet;
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class StampService {
|
export class StampService {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _pdf: PdfViewer,
|
private readonly _pdf: PdfViewer,
|
||||||
|
private readonly _reusablePdf: ReusablePdfViewer,
|
||||||
private readonly _state: FilePreviewStateService,
|
private readonly _state: FilePreviewStateService,
|
||||||
private readonly _logger: NGXLogger,
|
private readonly _logger: NGXLogger,
|
||||||
private readonly _viewModeService: ViewModeService,
|
private readonly _viewModeService: ViewModeService,
|
||||||
@ -22,7 +24,7 @@ export class StampService {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
async stampPDF(): Promise<void> {
|
async stampPDF(): Promise<void> {
|
||||||
const pdfDoc = await this._pdf.documentViewer.getDocument()?.getPDFDoc();
|
const pdfDoc = await this._reusablePdf.PDFDoc;
|
||||||
if (!pdfDoc) {
|
if (!pdfDoc) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -31,9 +33,9 @@ export class StampService {
|
|||||||
const allPages = [...Array(file.numberOfPages).keys()].map(page => page + 1);
|
const allPages = [...Array(file.numberOfPages).keys()].map(page => page + 1);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await clearStamps(pdfDoc, this._pdf.PDFNet, allPages);
|
await clearStamps(pdfDoc, this._reusablePdf.PDFNet, allPages);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this._logger.error('Error clearing stamps: ', e);
|
console.error('Error clearing stamps: ', e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,15 +48,15 @@ export class StampService {
|
|||||||
await this._stampExcludedPages(pdfDoc, file.excludedPages);
|
await this._stampExcludedPages(pdfDoc, file.excludedPages);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._pdf.documentViewer.refreshAll();
|
this._reusablePdf.documentViewer.refreshAll();
|
||||||
this._pdf.documentViewer.updateView([this._pdf.currentPage], this._pdf.currentPage);
|
this._reusablePdf.documentViewer.updateView([this._pdf.currentPage], this._pdf.currentPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _stampExcludedPages(document: PDFNet.PDFDoc, excludedPages: number[]): Promise<void> {
|
private async _stampExcludedPages(document: PDFNet.PDFDoc, excludedPages: number[]): Promise<void> {
|
||||||
if (excludedPages && excludedPages.length > 0) {
|
if (excludedPages && excludedPages.length > 0) {
|
||||||
await stampPDFPage(
|
await stampPDFPage(
|
||||||
document,
|
document,
|
||||||
this._pdf.PDFNet,
|
this._reusablePdf.PDFNet,
|
||||||
this._translateService.instant('file-preview.excluded-from-redaction') as string,
|
this._translateService.instant('file-preview.excluded-from-redaction') as string,
|
||||||
17,
|
17,
|
||||||
'courier',
|
'courier',
|
||||||
@ -70,7 +72,7 @@ export class StampService {
|
|||||||
const watermark = await firstValueFrom(this._watermarkService.getWatermark(dossierTemplateId));
|
const watermark = await firstValueFrom(this._watermarkService.getWatermark(dossierTemplateId));
|
||||||
await stampPDFPage(
|
await stampPDFPage(
|
||||||
document,
|
document,
|
||||||
this._pdf.PDFNet,
|
this._reusablePdf.PDFNet,
|
||||||
watermark.text,
|
watermark.text,
|
||||||
watermark.fontSize,
|
watermark.fontSize,
|
||||||
watermark.fontType,
|
watermark.fontType,
|
||||||
|
|||||||
@ -1,16 +1,16 @@
|
|||||||
import { Inject, Injectable } from '@angular/core';
|
import { Inject, Injectable } from '@angular/core';
|
||||||
import { PdfViewer } from './pdf-viewer.service';
|
|
||||||
import { UserPreferenceService } from '@services/user-preference.service';
|
import { UserPreferenceService } from '@services/user-preference.service';
|
||||||
import { HeaderElements } from '../utils/constants';
|
import { HeaderElements } from '../utils/constants';
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { BASE_HREF } from '../../../tokens';
|
import { BASE_HREF } from '../../../tokens';
|
||||||
|
import { ReusablePdfViewer } from '../../shared/components/reusable-pdf-viewer/reusable-pdf-viewer.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TooltipsService {
|
export class TooltipsService {
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(BASE_HREF) private readonly _baseHref: string,
|
@Inject(BASE_HREF) private readonly _baseHref: string,
|
||||||
private readonly _pdfViewer: PdfViewer,
|
private readonly _pdfViewer: ReusablePdfViewer,
|
||||||
private readonly _userPreferenceService: UserPreferenceService,
|
private readonly _userPreferenceService: UserPreferenceService,
|
||||||
private readonly _translateService: TranslateService,
|
private readonly _translateService: TranslateService,
|
||||||
) {}
|
) {}
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import { environment } from '@environments/environment';
|
|||||||
import { ViewModeService } from './view-mode.service';
|
import { ViewModeService } from './view-mode.service';
|
||||||
import { FilePreviewStateService } from './file-preview-state.service';
|
import { FilePreviewStateService } from './file-preview-state.service';
|
||||||
import { PageRotationService } from './page-rotation.service';
|
import { PageRotationService } from './page-rotation.service';
|
||||||
|
import { ReusablePdfViewer } from '../../shared/components/reusable-pdf-viewer/reusable-pdf-viewer.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ViewerHeaderConfigService {
|
export class ViewerHeaderConfigService {
|
||||||
@ -33,6 +34,7 @@ export class ViewerHeaderConfigService {
|
|||||||
private readonly _injector: Injector,
|
private readonly _injector: Injector,
|
||||||
private readonly _translateService: TranslateService,
|
private readonly _translateService: TranslateService,
|
||||||
private readonly _pdfViewer: PdfViewer,
|
private readonly _pdfViewer: PdfViewer,
|
||||||
|
private readonly _reusablePdf: ReusablePdfViewer,
|
||||||
private readonly _tooltipsService: TooltipsService,
|
private readonly _tooltipsService: TooltipsService,
|
||||||
private readonly _viewModeService: ViewModeService,
|
private readonly _viewModeService: ViewModeService,
|
||||||
private readonly _stateService: FilePreviewStateService,
|
private readonly _stateService: FilePreviewStateService,
|
||||||
@ -163,13 +165,13 @@ export class ViewerHeaderConfigService {
|
|||||||
|
|
||||||
private async _closeCompareMode() {
|
private async _closeCompareMode() {
|
||||||
this._viewModeService.compareMode = false;
|
this._viewModeService.compareMode = false;
|
||||||
const pdfNet = this._pdfViewer.instance.Core.PDFNet;
|
const pdfNet = this._reusablePdf.PDFNet;
|
||||||
await pdfNet.initialize(environment.licenseKey ? atob(environment.licenseKey) : null);
|
await pdfNet.initialize(environment.licenseKey ? atob(environment.licenseKey) : null);
|
||||||
const blob = await this._stateService.blob;
|
const blob = await this._stateService.blob;
|
||||||
const currentDocument = await pdfNet.PDFDoc.createFromBuffer(await blob.arrayBuffer());
|
const currentDocument = await pdfNet.PDFDoc.createFromBuffer(await blob.arrayBuffer());
|
||||||
|
|
||||||
const filename = this._stateService.file.filename ?? 'document.pdf';
|
const filename = this._stateService.file.filename ?? 'document.pdf';
|
||||||
this._pdfViewer.instance.UI.loadDocument(currentDocument, { filename });
|
this._reusablePdf.instance.UI.loadDocument(currentDocument, { filename });
|
||||||
|
|
||||||
this.disable([HeaderElements.CLOSE_COMPARE_BUTTON]);
|
this.disable([HeaderElements.CLOSE_COMPARE_BUTTON]);
|
||||||
this.enable([HeaderElements.COMPARE_BUTTON]);
|
this.enable([HeaderElements.COMPARE_BUTTON]);
|
||||||
@ -197,7 +199,7 @@ export class ViewerHeaderConfigService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _updateElements(): void {
|
private _updateElements(): void {
|
||||||
this._pdfViewer.instance.UI.setHeaderItems(header => {
|
this._reusablePdf.instance.UI.setHeaderItems(header => {
|
||||||
const enabledItems: IHeaderElement[] = [];
|
const enabledItems: IHeaderElement[] = [];
|
||||||
const groups: HeaderElementType[][] = [
|
const groups: HeaderElementType[][] = [
|
||||||
[HeaderElements.COMPARE_BUTTON, HeaderElements.CLOSE_COMPARE_BUTTON],
|
[HeaderElements.COMPARE_BUTTON, HeaderElements.CLOSE_COMPARE_BUTTON],
|
||||||
|
|||||||
@ -42,36 +42,3 @@ export const TextPopups = {
|
|||||||
ADD_RECTANGLE: 'add-rectangle',
|
ADD_RECTANGLE: 'add-rectangle',
|
||||||
ADD_FALSE_POSITIVE: 'add-false-positive',
|
ADD_FALSE_POSITIVE: 'add-false-positive',
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const DISABLED_HOTKEYS = [
|
|
||||||
'CTRL+SHIFT+EQUAL',
|
|
||||||
'COMMAND+SHIFT+EQUAL',
|
|
||||||
'CTRL+SHIFT+MINUS',
|
|
||||||
'COMMAND+SHIFT+MINUS',
|
|
||||||
'CTRL+V',
|
|
||||||
'COMMAND+V',
|
|
||||||
'CTRL+Y',
|
|
||||||
'COMMAND+Y',
|
|
||||||
'CTRL+O',
|
|
||||||
'COMMAND+O',
|
|
||||||
'CTRL+P',
|
|
||||||
'COMMAND+P',
|
|
||||||
'SPACE',
|
|
||||||
'UP',
|
|
||||||
'DOWN',
|
|
||||||
'R',
|
|
||||||
'P',
|
|
||||||
'A',
|
|
||||||
'C',
|
|
||||||
'E',
|
|
||||||
'I',
|
|
||||||
'L',
|
|
||||||
'N',
|
|
||||||
'O',
|
|
||||||
'T',
|
|
||||||
'S',
|
|
||||||
'G',
|
|
||||||
'H',
|
|
||||||
'K',
|
|
||||||
'U',
|
|
||||||
] as const;
|
|
||||||
|
|||||||
@ -0,0 +1,60 @@
|
|||||||
|
import { CustomError } from '@iqser/common-ui';
|
||||||
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
|
|
||||||
|
export const DOCUMENT_LOADING_ERROR = new CustomError(_('error.file-preview.label'), _('error.file-preview.action'), 'iqser:refresh');
|
||||||
|
|
||||||
|
export const USELESS_ELEMENTS = [
|
||||||
|
'pageNavOverlay',
|
||||||
|
'menuButton',
|
||||||
|
'selectToolButton',
|
||||||
|
'textHighlightToolButton',
|
||||||
|
'textUnderlineToolButton',
|
||||||
|
'textSquigglyToolButton',
|
||||||
|
'textStrikeoutToolButton',
|
||||||
|
'viewControlsButton',
|
||||||
|
'contextMenuPopup',
|
||||||
|
'linkButton',
|
||||||
|
'toggleNotesButton',
|
||||||
|
'notesPanel',
|
||||||
|
'thumbnailControl',
|
||||||
|
'documentControl',
|
||||||
|
'ribbons',
|
||||||
|
'toolsHeader',
|
||||||
|
'rotateClockwiseButton',
|
||||||
|
'rotateCounterClockwiseButton',
|
||||||
|
'annotationStyleEditButton',
|
||||||
|
'annotationGroupButton',
|
||||||
|
];
|
||||||
|
|
||||||
|
export const DISABLED_HOTKEYS = [
|
||||||
|
'CTRL+SHIFT+EQUAL',
|
||||||
|
'COMMAND+SHIFT+EQUAL',
|
||||||
|
'CTRL+SHIFT+MINUS',
|
||||||
|
'COMMAND+SHIFT+MINUS',
|
||||||
|
'CTRL+V',
|
||||||
|
'COMMAND+V',
|
||||||
|
'CTRL+Y',
|
||||||
|
'COMMAND+Y',
|
||||||
|
'CTRL+O',
|
||||||
|
'COMMAND+O',
|
||||||
|
'CTRL+P',
|
||||||
|
'COMMAND+P',
|
||||||
|
'SPACE',
|
||||||
|
'UP',
|
||||||
|
'DOWN',
|
||||||
|
'R',
|
||||||
|
'P',
|
||||||
|
'A',
|
||||||
|
'C',
|
||||||
|
'E',
|
||||||
|
'I',
|
||||||
|
'L',
|
||||||
|
'N',
|
||||||
|
'O',
|
||||||
|
'T',
|
||||||
|
'S',
|
||||||
|
'G',
|
||||||
|
'H',
|
||||||
|
'K',
|
||||||
|
'U',
|
||||||
|
] as const;
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
|
import { Component, ElementRef, ViewChild } from '@angular/core';
|
||||||
import { ReusablePdfViewer } from './reusable-pdf-viewer.service';
|
import { ReusablePdfViewer } from './reusable-pdf-viewer.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -16,12 +16,18 @@ import { ReusablePdfViewer } from './reusable-pdf-viewer.service';
|
|||||||
`,
|
`,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class ReusablePdfViewerComponent implements OnInit {
|
export class ReusablePdfViewerComponent {
|
||||||
@ViewChild('viewer', { static: true }) readonly viewer: ElementRef;
|
#viewer: ElementRef;
|
||||||
|
|
||||||
constructor(readonly reusablePdfViewer: ReusablePdfViewer) {}
|
constructor(readonly reusablePdfViewer: ReusablePdfViewer) {}
|
||||||
|
|
||||||
ngOnInit() {
|
@ViewChild('viewer', { static: true })
|
||||||
return this.reusablePdfViewer.init(this.viewer.nativeElement);
|
set viewer(value: ElementRef) {
|
||||||
|
if (this.#viewer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#viewer = value;
|
||||||
|
this.reusablePdfViewer.init(value.nativeElement as HTMLElement).then();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,47 +1,28 @@
|
|||||||
import { Inject, Injectable, Injector } from '@angular/core';
|
import { Inject, Injectable, Injector } from '@angular/core';
|
||||||
import WebViewer, { Core, WebViewerInstance, WebViewerOptions } from '@pdftron/webviewer';
|
import WebViewer, { Core, WebViewerInstance, WebViewerOptions } from '@pdftron/webviewer';
|
||||||
import { environment } from '../../../../../environments/environment';
|
import { environment } from '@environments/environment';
|
||||||
import { BASE_HREF_FN, BaseHrefFn } from '../../../../tokens';
|
import { BASE_HREF_FN, BaseHrefFn } from '../../../../tokens';
|
||||||
import { File } from '@red/domain';
|
import { File } from '@red/domain';
|
||||||
import { CustomError, ErrorService, LoadingService, log, shareDistinctLast, shareLast } from '@iqser/common-ui';
|
import { ErrorService, List, shareDistinctLast, shareLast } from '@iqser/common-ui';
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker/';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { ActivatedRoute, ActivationStart, Router } from '@angular/router';
|
import { debounceTime, map, tap } from 'rxjs/operators';
|
||||||
import { filter, map } from 'rxjs/operators';
|
import { fromEvent, merge, Observable, Subject } from 'rxjs';
|
||||||
import { fromEvent, merge, Observable } from 'rxjs';
|
import { ConfigService } from '@services/config.service';
|
||||||
import { ConfigService } from '../../../../services/config.service';
|
import { NGXLogger } from 'ngx-logger';
|
||||||
|
import { DISABLED_HOTKEYS, DOCUMENT_LOADING_ERROR, USELESS_ELEMENTS } from './constants';
|
||||||
|
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||||
|
import { Rgb } from '@shared/components/reusable-pdf-viewer/types';
|
||||||
import DocumentViewer = Core.DocumentViewer;
|
import DocumentViewer = Core.DocumentViewer;
|
||||||
import AnnotationManager = Core.AnnotationManager;
|
import AnnotationManager = Core.AnnotationManager;
|
||||||
import TextTool = Core.Tools.TextTool;
|
import TextTool = Core.Tools.TextTool;
|
||||||
|
import Annotation = Core.Annotations.Annotation;
|
||||||
const DocLoadingError = new CustomError(_('error.file-preview.label'), _('error.file-preview.action'), 'iqser:refresh');
|
import TextHighlightAnnotation = Core.Annotations.TextHighlightAnnotation;
|
||||||
const uselessElements = [
|
|
||||||
'pageNavOverlay',
|
|
||||||
'menuButton',
|
|
||||||
'selectToolButton',
|
|
||||||
'textHighlightToolButton',
|
|
||||||
'textUnderlineToolButton',
|
|
||||||
'textSquigglyToolButton',
|
|
||||||
'textStrikeoutToolButton',
|
|
||||||
'viewControlsButton',
|
|
||||||
'contextMenuPopup',
|
|
||||||
'linkButton',
|
|
||||||
'toggleNotesButton',
|
|
||||||
'notesPanel',
|
|
||||||
'thumbnailControl',
|
|
||||||
'documentControl',
|
|
||||||
'ribbons',
|
|
||||||
'toolsHeader',
|
|
||||||
'rotateClockwiseButton',
|
|
||||||
'rotateCounterClockwiseButton',
|
|
||||||
'annotationStyleEditButton',
|
|
||||||
'annotationGroupButton',
|
|
||||||
];
|
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
export class ReusablePdfViewer {
|
export class ReusablePdfViewer {
|
||||||
readonly currentPage$ = this._injector.get(ActivatedRoute).queryParamMap.pipe(
|
readonly currentPage$ = this._activatedRoute.queryParamMap.pipe(
|
||||||
map(params => Number(params.get('page') ?? '1')),
|
map(params => Number(params.get('page') ?? '1')),
|
||||||
shareDistinctLast(),
|
shareDistinctLast(),
|
||||||
);
|
);
|
||||||
@ -50,51 +31,155 @@ export class ReusablePdfViewer {
|
|||||||
|
|
||||||
show$: Observable<boolean>;
|
show$: Observable<boolean>;
|
||||||
documentLoaded$: Observable<boolean>;
|
documentLoaded$: Observable<boolean>;
|
||||||
|
annotationSelected$: Observable<unknown>;
|
||||||
|
pageComplete$: Observable<unknown>;
|
||||||
|
|
||||||
#instance: WebViewerInstance;
|
#instance: WebViewerInstance;
|
||||||
|
#documentClosed$ = new Subject<boolean>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(BASE_HREF_FN) private readonly _convertPath: BaseHrefFn,
|
@Inject(BASE_HREF_FN) private readonly _convertPath: BaseHrefFn,
|
||||||
private readonly _loadingService: LoadingService,
|
|
||||||
private readonly _errorService: ErrorService,
|
private readonly _errorService: ErrorService,
|
||||||
private readonly _router: Router,
|
private readonly _router: Router,
|
||||||
|
private readonly _logger: NGXLogger,
|
||||||
private readonly _configService: ConfigService,
|
private readonly _configService: ConfigService,
|
||||||
|
private readonly _activatedRoute: ActivatedRoute,
|
||||||
private readonly _injector: Injector,
|
private readonly _injector: Injector,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
get #documentLoaded$() {
|
get instance() {
|
||||||
const docLoadedEvent = this.#instance.UI.Events.DOCUMENT_LOADED;
|
return this.#instance;
|
||||||
const event$ = fromEvent(this.documentViewer, docLoadedEvent).pipe(map(() => true));
|
|
||||||
|
|
||||||
return event$.pipe(log('[PDF] Document loaded'), shareLast());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get #show$() {
|
get PDFDoc() {
|
||||||
const routeChanged = this._router.events.pipe(
|
return this.document?.getPDFDoc();
|
||||||
filter(event => event instanceof ActivationStart),
|
}
|
||||||
map(() => false),
|
|
||||||
);
|
|
||||||
|
|
||||||
return merge(routeChanged, this.documentLoaded$);
|
get document() {
|
||||||
|
return this.documentViewer.getDocument();
|
||||||
|
}
|
||||||
|
|
||||||
|
get PDFNet(): typeof Core.PDFNet {
|
||||||
|
return this.#instance.Core.PDFNet;
|
||||||
|
}
|
||||||
|
|
||||||
|
get pageCount() {
|
||||||
|
try {
|
||||||
|
return this.#instance.Core.documentViewer.getPageCount();
|
||||||
|
} catch {
|
||||||
|
// might throw Error: getPageCount was called before the 'documentLoaded' event
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get #pageComplete$() {
|
||||||
|
return fromEvent(this.documentViewer, 'pageComplete').pipe(debounceTime(300));
|
||||||
|
}
|
||||||
|
|
||||||
|
get #documentLoaded$() {
|
||||||
|
const event$ = fromEvent(this.documentViewer, this.#instance.UI.Events.DOCUMENT_LOADED);
|
||||||
|
const toBool$ = event$.pipe(map(() => true));
|
||||||
|
const updateCurrentPage$ = toBool$.pipe(tap(() => this.#setCurrentPage()));
|
||||||
|
|
||||||
|
const log = tap<boolean>(() => this._logger.info('[PDF] Document loaded'));
|
||||||
|
return updateCurrentPage$.pipe(log, shareLast());
|
||||||
|
}
|
||||||
|
|
||||||
|
get #annotationSelected$() {
|
||||||
|
const onSelect$ = fromEvent<[Annotation[], string]>(this.annotationManager, 'annotationSelected');
|
||||||
|
return onSelect$.pipe(tap(value => console.log('Annotation selected: ', value)));
|
||||||
}
|
}
|
||||||
|
|
||||||
async init(htmlElement: HTMLElement) {
|
async init(htmlElement: HTMLElement) {
|
||||||
this.#instance = await this.#getInstance(htmlElement);
|
this.#instance = await this.#getInstance(htmlElement);
|
||||||
console.log('[PDF] Initialized');
|
this._logger.info('[PDF] Initialized');
|
||||||
|
|
||||||
this.documentViewer = this.#instance.Core.documentViewer;
|
this.documentViewer = this.#instance.Core.documentViewer;
|
||||||
this.annotationManager = this.#instance.Core.annotationManager;
|
this.annotationManager = this.#instance.Core.annotationManager;
|
||||||
|
|
||||||
this.documentLoaded$ = this.#documentLoaded$;
|
this.documentLoaded$ = this.#documentLoaded$;
|
||||||
this.show$ = this.#show$;
|
this.show$ = merge(this.#documentClosed$, this.documentLoaded$).pipe(shareLast());
|
||||||
|
this.annotationSelected$ = this.#annotationSelected$;
|
||||||
|
this.pageComplete$ = this.#pageComplete$;
|
||||||
this.#setSelectionMode();
|
this.#setSelectionMode();
|
||||||
this.#configureElements();
|
this.#configureElements();
|
||||||
|
this.#disableHotkeys();
|
||||||
}
|
}
|
||||||
|
|
||||||
loadDocument(blob: Blob, file: File) {
|
deleteAnnotations(annotationsIds?: List) {
|
||||||
console.log('[PDF] Loading document', blob, file);
|
let annotations: Annotation[];
|
||||||
|
if (!annotationsIds) {
|
||||||
|
annotations = this.getAnnotations();
|
||||||
|
} else {
|
||||||
|
annotations = this.getAnnotationsById(annotationsIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.annotationManager.deleteAnnotations(annotations, {
|
||||||
|
force: true,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.log('Error while deleting annotations: ', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getAnnotations(predicate?: (value: Annotation) => boolean) {
|
||||||
|
const annotations = this.annotationManager.getAnnotationsList();
|
||||||
|
return predicate ? annotations.filter(predicate) : annotations;
|
||||||
|
}
|
||||||
|
|
||||||
|
getAnnotationsById(ids: List) {
|
||||||
|
return ids.map(id => this.annotationManager.getAnnotationById(id)).filter(a => !!a);
|
||||||
|
}
|
||||||
|
|
||||||
|
deselectAnnotations(annotations?: List<AnnotationWrapper>) {
|
||||||
|
if (!annotations) {
|
||||||
|
return this.annotationManager.deselectAllAnnotations();
|
||||||
|
}
|
||||||
|
|
||||||
|
const ann = this.getAnnotationsById(annotations.map(a => a.id));
|
||||||
|
this.annotationManager.deselectAnnotations(ann);
|
||||||
|
}
|
||||||
|
|
||||||
|
hideAnnotations(annotations: Annotation[]): void {
|
||||||
|
this.annotationManager.hideAnnotations(annotations);
|
||||||
|
}
|
||||||
|
|
||||||
|
showAnnotations(annotations: Annotation[]): void {
|
||||||
|
this.annotationManager.showAnnotations(annotations);
|
||||||
|
}
|
||||||
|
|
||||||
|
getPageHeight(page: number) {
|
||||||
|
try {
|
||||||
|
return this.documentViewer.getPageHeight(page);
|
||||||
|
} catch {
|
||||||
|
// might throw Error: getPageHeight was called before the 'documentLoaded' event
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
closeDocument() {
|
||||||
|
this._logger.info('[PDF] Closing document');
|
||||||
|
this.#documentClosed$.next(false);
|
||||||
|
this.#instance.Core.documentViewer.closeDocument();
|
||||||
|
}
|
||||||
|
|
||||||
|
async lockDocument() {
|
||||||
|
const document = await this.PDFDoc;
|
||||||
|
if (!document) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
await document.lock();
|
||||||
|
this._logger.info('[PDF] Locked');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async loadDocument(blob: Blob, file: File) {
|
||||||
|
this._logger.info('[PDF] Loading document', blob, file);
|
||||||
const onError = () => {
|
const onError = () => {
|
||||||
this._loadingService.stop();
|
this._errorService.set(DOCUMENT_LOADING_ERROR);
|
||||||
this._errorService.set(DocLoadingError);
|
this._logger.error('[PDF] Error while loading document');
|
||||||
// this.stateService.reloadBlob();
|
// this.stateService.reloadBlob();
|
||||||
};
|
};
|
||||||
// const pdfNet = this._instance.Core.PDFNet;
|
// const pdfNet = this._instance.Core.PDFNet;
|
||||||
@ -105,11 +190,40 @@ export class ReusablePdfViewer {
|
|||||||
// console.log('document initialized');
|
// console.log('document initialized');
|
||||||
// await document.flattenAnnotations(false);
|
// await document.flattenAnnotations(false);
|
||||||
// console.log(document);
|
// console.log(document);
|
||||||
this.#instance.UI.loadDocument(blob, { filename: file?.filename + '.pdf' ?? 'document.pdf', onError });
|
const pdfNet = this.#instance.Core.PDFNet;
|
||||||
|
|
||||||
|
await pdfNet.initialize(environment.licenseKey ? atob(environment.licenseKey) : null);
|
||||||
|
const document = await pdfNet.PDFDoc.createFromBuffer(await blob.arrayBuffer());
|
||||||
|
await document.flattenAnnotations(false);
|
||||||
|
this.#instance.UI.loadDocument(document, { filename: file?.filename + '.pdf' ?? 'document.pdf', onError });
|
||||||
|
}
|
||||||
|
|
||||||
|
quad(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, x4: number, y4: number) {
|
||||||
|
return new this.#instance.Core.Math.Quad(x1, y1, x2, y2, x3, y3, x4, y4);
|
||||||
|
}
|
||||||
|
|
||||||
|
color(rgb: Rgb) {
|
||||||
|
return new this.#instance.Core.Annotations.Color(rgb.r, rgb.g, rgb.b);
|
||||||
|
}
|
||||||
|
|
||||||
|
rectangle() {
|
||||||
|
return new this.#instance.Core.Annotations.RectangleAnnotation();
|
||||||
|
}
|
||||||
|
|
||||||
|
textHighlight() {
|
||||||
|
return new this.#instance.Core.Annotations.TextHighlightAnnotation();
|
||||||
|
}
|
||||||
|
|
||||||
|
isTextHighlight(annotation: Annotation): annotation is TextHighlightAnnotation {
|
||||||
|
return annotation instanceof this.#instance.Core.Annotations.TextHighlightAnnotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
#disableHotkeys(): void {
|
||||||
|
DISABLED_HOTKEYS.forEach(key => this.#instance.UI.hotkeys.off(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
#configureElements() {
|
#configureElements() {
|
||||||
this.#instance.UI.disableElements(uselessElements);
|
this.#instance.UI.disableElements(USELESS_ELEMENTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
#setSelectionMode(): void {
|
#setSelectionMode(): void {
|
||||||
@ -128,4 +242,9 @@ export class ReusablePdfViewer {
|
|||||||
|
|
||||||
return WebViewer(options, htmlElement);
|
return WebViewer(options, htmlElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#setCurrentPage() {
|
||||||
|
const currentDocPage = this._activatedRoute.snapshot.queryParamMap.get('page');
|
||||||
|
this.documentViewer.setCurrentPage(Number(currentDocPage ?? '1'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,5 @@
|
|||||||
|
export interface Rgb {
|
||||||
|
readonly r: number;
|
||||||
|
readonly g: number;
|
||||||
|
readonly b: number;
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user