RED-3988: add document viewer service
This commit is contained in:
parent
94ab93cfaf
commit
669ec2ba1d
@ -1,6 +1,6 @@
|
||||
<router-outlet></router-outlet>
|
||||
|
||||
<redaction-pdf-viewer [style.visibility]="(pdf.loaded$ | async) ? 'visible' : 'hidden'"></redaction-pdf-viewer>
|
||||
<redaction-pdf-viewer [style.visibility]="(documentViewer.loaded$ | async) ? 'visible' : 'hidden'"></redaction-pdf-viewer>
|
||||
|
||||
<iqser-full-page-loading-indicator></iqser-full-page-loading-indicator>
|
||||
<iqser-connection-status></iqser-connection-status>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { Component, ViewContainerRef } from '@angular/core';
|
||||
import { RouterHistoryService } from '@services/router-history.service';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { PdfViewer } from './modules/pdf-viewer/services/pdf-viewer.service';
|
||||
import { REDDocumentViewer } from './modules/pdf-viewer/services/document-viewer.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-root',
|
||||
@ -15,6 +15,6 @@ export class AppComponent {
|
||||
public viewContainerRef: ViewContainerRef,
|
||||
private readonly _routerHistoryService: RouterHistoryService,
|
||||
private readonly _userService: UserService,
|
||||
readonly pdf: PdfViewer,
|
||||
readonly documentViewer: REDDocumentViewer,
|
||||
) {}
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ import {
|
||||
EventEmitter,
|
||||
HostListener,
|
||||
Input,
|
||||
OnDestroy,
|
||||
Output,
|
||||
TemplateRef,
|
||||
ViewChild,
|
||||
@ -15,6 +16,7 @@ import { AnnotationProcessingService } from '../../services/annotation-processin
|
||||
import { MatDialogRef, MatDialogState } from '@angular/material/dialog';
|
||||
import scrollIntoView from 'scroll-into-view-if-needed';
|
||||
import {
|
||||
AutoUnsubscribe,
|
||||
CircleButtonTypes,
|
||||
Debounce,
|
||||
FilterService,
|
||||
@ -24,7 +26,7 @@ import {
|
||||
shareDistinctLast,
|
||||
shareLast,
|
||||
} from '@iqser/common-ui';
|
||||
import { combineLatest, firstValueFrom, Observable, takeWhile } from 'rxjs';
|
||||
import { combineLatest, firstValueFrom, Observable } from 'rxjs';
|
||||
import { map, tap } from 'rxjs/operators';
|
||||
import { File } from '@red/domain';
|
||||
import { ExcludedPagesService } from '../../services/excluded-pages.service';
|
||||
@ -38,6 +40,7 @@ import { FileDataService } from '../../services/file-data.service';
|
||||
import { PdfViewer } from '../../../pdf-viewer/services/pdf-viewer.service';
|
||||
import { REDAnnotationManager } from '../../../pdf-viewer/services/annotation-manager.service';
|
||||
import { AnnotationsListingService } from '../../services/annotations-listing.service';
|
||||
import { REDDocumentViewer } from '../../../pdf-viewer/services/document-viewer.service';
|
||||
|
||||
const COMMAND_KEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Escape'];
|
||||
const ALL_HOTKEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'];
|
||||
@ -48,7 +51,7 @@ const ALL_HOTKEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'];
|
||||
styleUrls: ['./file-workload.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class FileWorkloadComponent {
|
||||
export class FileWorkloadComponent extends AutoUnsubscribe implements OnDestroy {
|
||||
readonly iconButtonTypes = IconButtonTypes;
|
||||
readonly circleButtonTypes = CircleButtonTypes;
|
||||
|
||||
@ -77,22 +80,31 @@ export class FileWorkloadComponent {
|
||||
readonly viewModeService: ViewModeService,
|
||||
readonly multiSelectService: MultiSelectService,
|
||||
readonly annotationManager: REDAnnotationManager,
|
||||
private readonly _documentViewer: REDDocumentViewer,
|
||||
readonly documentInfoService: DocumentInfoService,
|
||||
readonly listingService: AnnotationsListingService,
|
||||
readonly excludedPagesService: ExcludedPagesService,
|
||||
private readonly _changeDetectorRef: ChangeDetectorRef,
|
||||
private readonly _annotationProcessingService: AnnotationProcessingService,
|
||||
) {
|
||||
this.pdf.currentPage$.pipe(takeWhile(() => !!this)).subscribe(pageNumber => {
|
||||
super();
|
||||
|
||||
this.addActiveScreenSubscription = this.pdf.currentPage$.subscribe(pageNumber => {
|
||||
this._scrollViews();
|
||||
this.scrollAnnotationsToPage(pageNumber, 'always');
|
||||
});
|
||||
this.listingService.selected$.pipe(takeWhile(() => !!this)).subscribe(annotationIds => {
|
||||
|
||||
this.addActiveScreenSubscription = this.listingService.selected$.subscribe(annotationIds => {
|
||||
if (annotationIds.length > 0) {
|
||||
this.pagesPanelActive = false;
|
||||
}
|
||||
this.scrollToSelectedAnnotation();
|
||||
});
|
||||
|
||||
this.addActiveScreenSubscription = this._documentViewer.keyUp$.subscribe($event => {
|
||||
this.handleKeyEvent($event);
|
||||
});
|
||||
|
||||
this.displayedAnnotations$ = this._displayedAnnotations$;
|
||||
this.multiSelectInactive$ = this._multiSelectInactive$;
|
||||
this.showExcludedPages$ = this._showExcludedPages$;
|
||||
|
||||
@ -32,6 +32,7 @@ import {
|
||||
ROTATION_ACTION_BUTTONS,
|
||||
TEXT_POPUPS_TO_TOGGLE,
|
||||
} from '../../../pdf-viewer/utils/constants';
|
||||
import { REDDocumentViewer } from '../../../pdf-viewer/services/document-viewer.service';
|
||||
import Annotation = Core.Annotations.Annotation;
|
||||
|
||||
@Component({
|
||||
@ -45,7 +46,6 @@ export class PdfPaginatorComponent extends AutoUnsubscribe implements OnInit, On
|
||||
@Output() readonly annotationSelected = this.#annotationSelected$;
|
||||
@Output() readonly manualAnnotationRequested = new EventEmitter<ManualRedactionEntryWrapper>();
|
||||
@Output() readonly pageChanged = this.pdf.pageChanged$.pipe(tap(() => this._handleCustomActions()));
|
||||
@Output() readonly keyUp = this.pdf.keyUp$;
|
||||
instance: WebViewerInstance;
|
||||
private _selectedText = '';
|
||||
readonly #visibilityOffIcon = this._convertPath('/assets/icons/general/visibility-off.svg');
|
||||
@ -67,6 +67,7 @@ export class PdfPaginatorComponent extends AutoUnsubscribe implements OnInit, On
|
||||
private readonly _fileDataService: FileDataService,
|
||||
private readonly _viewerHeaderService: ViewerHeaderService,
|
||||
private readonly _errorService: ErrorService,
|
||||
private readonly _documentViewer: REDDocumentViewer,
|
||||
private readonly _annotationManager: REDAnnotationManager,
|
||||
readonly pdf: PdfViewer,
|
||||
private readonly _state: FilePreviewStateService,
|
||||
@ -85,7 +86,7 @@ export class PdfPaginatorComponent extends AutoUnsubscribe implements OnInit, On
|
||||
this.addActiveScreenSubscription = this._state.blob$
|
||||
.pipe(
|
||||
log('Reload blob'),
|
||||
switchMap(blob => from(this.pdf.lockDocument()).pipe(map(() => blob))),
|
||||
switchMap(blob => from(this._documentViewer.lock()).pipe(map(() => blob))),
|
||||
withLatestFrom(this._state.file$),
|
||||
tap(() => this._errorService.clear()),
|
||||
tap(([blob, file]) => this.pdf.loadDocument(blob, file)),
|
||||
@ -109,24 +110,18 @@ export class PdfPaginatorComponent extends AutoUnsubscribe implements OnInit, On
|
||||
if (action === 'deselected') {
|
||||
// Remove deselected annotations from selected list
|
||||
nextAnnotations = this._annotationManager.selected.filter(ann => !annotations.some(a => a.Id === ann.Id));
|
||||
this.pdf.disable(TextPopups.ADD_RECTANGLE);
|
||||
return nextAnnotations.map(ann => ann.Id);
|
||||
} else if (!this._multiSelectService.isEnabled) {
|
||||
// Only choose the last selected annotation, to bypass viewer multi select
|
||||
nextAnnotations = annotations;
|
||||
const notSelected = this._fileDataService.all.filter(wrapper => !nextAnnotations.some(ann => ann.Id === wrapper.id));
|
||||
this._annotationManager.deselect(notSelected);
|
||||
} else {
|
||||
// Get selected annotations from the manager, no intervention needed
|
||||
nextAnnotations = this._annotationManager.selected;
|
||||
}
|
||||
|
||||
if (action === 'deselected') {
|
||||
this.pdf.disable(TextPopups.ADD_RECTANGLE);
|
||||
return nextAnnotations.map(ann => ann.Id);
|
||||
}
|
||||
|
||||
if (!this._multiSelectService.isEnabled) {
|
||||
const notSelected = this._fileDataService.all.filter(wrapper => !nextAnnotations.some(ann => ann.Id === wrapper.id));
|
||||
this._annotationManager.deselect(notSelected);
|
||||
}
|
||||
|
||||
this.#configureAnnotationSpecificActions(annotations);
|
||||
this._toggleRectangleAnnotationAction(annotations.length === 1 && annotations[0].ReadOnly);
|
||||
return nextAnnotations.map(ann => ann.Id);
|
||||
@ -169,7 +164,7 @@ export class PdfPaginatorComponent extends AutoUnsubscribe implements OnInit, On
|
||||
private _configureElements() {
|
||||
const dossierTemplateId = this.dossier.dossierTemplateId;
|
||||
const color = this._annotationDrawService.getAndConvertColor(dossierTemplateId, dossierTemplateId, 'manual');
|
||||
this.pdf.setRectangleToolStyles(color);
|
||||
this._documentViewer.setRectangleToolStyles(color);
|
||||
}
|
||||
|
||||
#configureAnnotationSpecificActions(viewerAnnotations: Annotation[]) {
|
||||
@ -293,6 +288,7 @@ export class PdfPaginatorComponent extends AutoUnsubscribe implements OnInit, On
|
||||
title: this.#getTitle(ManualRedactionEntryTypes.DICTIONARY),
|
||||
onClick: () => this._addManualRedactionOfType(ManualRedactionEntryTypes.DICTIONARY),
|
||||
});
|
||||
console.log(popups);
|
||||
|
||||
this.instance.UI.textPopup.add(popups);
|
||||
|
||||
@ -315,7 +311,7 @@ export class PdfPaginatorComponent extends AutoUnsubscribe implements OnInit, On
|
||||
|
||||
if (this.canPerformActions && !isCurrentPageExcluded) {
|
||||
try {
|
||||
this.instance.UI.enableTools(['AnnotationCreateRectangle']);
|
||||
this.pdf.instance.UI.enableTools(['AnnotationCreateRectangle']);
|
||||
} catch (e) {
|
||||
// happens
|
||||
}
|
||||
@ -338,7 +334,7 @@ export class PdfPaginatorComponent extends AutoUnsubscribe implements OnInit, On
|
||||
);
|
||||
headerElementsToDisable = headerElementsToDisable.filter(element => !ALLOWED_ACTIONS_WHEN_PAGE_EXCLUDED.includes(element));
|
||||
} else {
|
||||
this.instance.UI.disableTools(['AnnotationCreateRectangle']);
|
||||
this.pdf.instance.UI.disableTools(['AnnotationCreateRectangle']);
|
||||
}
|
||||
|
||||
this.pdf.disable(textPopupElementsToDisable);
|
||||
|
||||
@ -65,7 +65,6 @@
|
||||
<div class="content-container">
|
||||
<redaction-pdf-viewer
|
||||
(annotationSelected)="handleAnnotationSelected($event)"
|
||||
(keyUp)="handleKeyEvent($event); workloadComponent.handleKeyEvent($event)"
|
||||
(manualAnnotationRequested)="openManualAnnotationDialog($event)"
|
||||
(pageChanged)="viewerPageChanged($event)"
|
||||
[canPerformActions]="canPerformAnnotationActions$ | async"
|
||||
|
||||
@ -28,7 +28,6 @@ import { PermissionsService } from '@services/permissions.service';
|
||||
import { combineLatest, firstValueFrom, Observable, of, pairwise } from 'rxjs';
|
||||
import { UserPreferenceService } from '@services/user-preference.service';
|
||||
import { download, handleFilterDelta } from '../../utils';
|
||||
import { FileWorkloadComponent } from './components/file-workload/file-workload.component';
|
||||
import { FilesService } from '@services/files/files.service';
|
||||
import { FileManagementService } from '@services/files/file-management.service';
|
||||
import { catchError, filter, map, startWith, switchMap, tap } from 'rxjs/operators';
|
||||
@ -55,6 +54,7 @@ import { REDAnnotationManager } from '../pdf-viewer/services/annotation-manager.
|
||||
import { ViewerHeaderService } from '../pdf-viewer/services/viewer-header.service';
|
||||
import { ROTATION_ACTION_BUTTONS } from '../pdf-viewer/utils/constants';
|
||||
import { SkippedService } from './services/skipped.service';
|
||||
import { REDDocumentViewer } from '../pdf-viewer/services/document-viewer.service';
|
||||
import Annotation = Core.Annotations.Annotation;
|
||||
|
||||
@Component({
|
||||
@ -71,7 +71,6 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
readonly fileId = this.state.fileId;
|
||||
readonly dossierId = this.state.dossierId;
|
||||
readonly file$ = this.state.file$.pipe(tap(file => this._fileDataService.loadAnnotations(file)));
|
||||
@ViewChild(FileWorkloadComponent) readonly workloadComponent: FileWorkloadComponent;
|
||||
private _lastPage: string;
|
||||
@ViewChild('annotationFilterTemplate', {
|
||||
read: TemplateRef,
|
||||
@ -102,6 +101,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
private readonly _skippedService: SkippedService,
|
||||
private readonly _fileDataService: FileDataService,
|
||||
private readonly _viewModeService: ViewModeService,
|
||||
private readonly _documentViewer: REDDocumentViewer,
|
||||
private readonly _changeDetectorRef: ChangeDetectorRef,
|
||||
private readonly _dialogService: FilePreviewDialogService,
|
||||
private readonly _pageRotationService: PageRotationService,
|
||||
@ -198,7 +198,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
|
||||
ngOnDetach(): void {
|
||||
this._pageRotationService.clearRotations();
|
||||
this.pdf.closeDocument();
|
||||
this._documentViewer.close();
|
||||
super.ngOnDetach();
|
||||
this._changeDetectorRef.markForCheck();
|
||||
}
|
||||
@ -354,7 +354,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
}
|
||||
|
||||
loadAnnotations() {
|
||||
const documentLoaded$ = this.pdf.loaded$.pipe(
|
||||
const documentLoaded$ = this._documentViewer.loaded$.pipe(
|
||||
filter(s => s),
|
||||
tap(() => this.viewerReady()),
|
||||
);
|
||||
@ -525,9 +525,13 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
)
|
||||
.subscribe();
|
||||
|
||||
this.addActiveScreenSubscription = this.pdf.pageComplete$.subscribe(() => {
|
||||
this.addActiveScreenSubscription = this._documentViewer.pageComplete$.subscribe(() => {
|
||||
this._setExcludedPageStyles();
|
||||
});
|
||||
|
||||
this.addActiveScreenSubscription = this._documentViewer.keyUp$.subscribe($event => {
|
||||
this.handleKeyEvent($event);
|
||||
});
|
||||
}
|
||||
|
||||
private _handleDeletedDossier(): void {
|
||||
|
||||
@ -37,6 +37,7 @@ import { FileDataService } from './file-data.service';
|
||||
import { PdfViewer } from '../../pdf-viewer/services/pdf-viewer.service';
|
||||
import { REDAnnotationManager } from '../../pdf-viewer/services/annotation-manager.service';
|
||||
import { SkippedService } from './skipped.service';
|
||||
import { REDDocumentViewer } from '../../pdf-viewer/services/document-viewer.service';
|
||||
import Quad = Core.Math.Quad;
|
||||
|
||||
@Injectable()
|
||||
@ -51,6 +52,7 @@ export class AnnotationActionsService {
|
||||
private readonly _dialogService: FilePreviewDialogService,
|
||||
private readonly _dialog: MatDialog,
|
||||
private readonly _pdf: PdfViewer,
|
||||
private readonly _documentViewer: REDDocumentViewer,
|
||||
private readonly _annotationManager: REDAnnotationManager,
|
||||
private readonly _annotationDrawService: AnnotationDrawService,
|
||||
private readonly _activeDossiersService: ActiveDossiersService,
|
||||
@ -538,7 +540,7 @@ export class AnnotationActionsService {
|
||||
private async _extractTextAndPositions(annotationId: string) {
|
||||
const viewerAnnotation = this._annotationManager.get(annotationId);
|
||||
|
||||
const document = await this._pdf.PDFDoc;
|
||||
const document = await this._documentViewer.PDFDoc;
|
||||
const page = await document.getPage(viewerAnnotation.getPageNumber());
|
||||
if (this._pdf.isTextHighlight(viewerAnnotation)) {
|
||||
const words = [];
|
||||
@ -546,7 +548,7 @@ export class AnnotationActionsService {
|
||||
for (const quad of viewerAnnotation.Quads) {
|
||||
const rect = toPosition(
|
||||
viewerAnnotation.getPageNumber(),
|
||||
this._pdf.getPageHeight(viewerAnnotation.getPageNumber()),
|
||||
this._documentViewer.getHeight(viewerAnnotation.getPageNumber()),
|
||||
this._translateQuads(viewerAnnotation.getPageNumber(), quad),
|
||||
);
|
||||
rectangles.push(rect);
|
||||
|
||||
@ -8,12 +8,14 @@ import { Core } from '@pdftron/webviewer';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { WatermarkService } from '@services/entity-services/watermark.service';
|
||||
import { PdfViewer } from '../../pdf-viewer/services/pdf-viewer.service';
|
||||
import { REDDocumentViewer } from '../../pdf-viewer/services/document-viewer.service';
|
||||
import PDFNet = Core.PDFNet;
|
||||
|
||||
@Injectable()
|
||||
export class StampService {
|
||||
constructor(
|
||||
private readonly _pdf: PdfViewer,
|
||||
private readonly _documentViewer: REDDocumentViewer,
|
||||
private readonly _state: FilePreviewStateService,
|
||||
private readonly _logger: NGXLogger,
|
||||
private readonly _viewModeService: ViewModeService,
|
||||
@ -22,7 +24,7 @@ export class StampService {
|
||||
) {}
|
||||
|
||||
async stampPDF(): Promise<void> {
|
||||
const pdfDoc = await this._pdf.PDFDoc;
|
||||
const pdfDoc = await this._documentViewer.PDFDoc;
|
||||
if (!pdfDoc) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
<div [style.visibility]="(pdf.loaded$ | async) ? 'visible' : 'hidden'" class="pagination noselect">
|
||||
<div [style.visibility]="(documentViewer.loaded$ | async) ? 'visible' : 'hidden'" class="pagination noselect">
|
||||
<div (click)="pdf.navigatePreviousPage()">
|
||||
<mat-icon class="chevron-icon" svgIcon="red:nav-prev"></mat-icon>
|
||||
</div>
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { PdfViewer } from '../../services/pdf-viewer.service';
|
||||
import { REDDocumentViewer } from '../../services/document-viewer.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-paginator',
|
||||
@ -7,5 +8,5 @@ import { PdfViewer } from '../../services/pdf-viewer.service';
|
||||
styleUrls: ['./paginator.component.scss'],
|
||||
})
|
||||
export class PaginatorComponent {
|
||||
constructor(readonly pdf: PdfViewer) {}
|
||||
constructor(readonly pdf: PdfViewer, readonly documentViewer: REDDocumentViewer) {}
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ import { PdfViewer } from './services/pdf-viewer.service';
|
||||
import { REDAnnotationManager } from './services/annotation-manager.service';
|
||||
import { ViewerHeaderService } from './services/viewer-header.service';
|
||||
import { CompareFileInputComponent } from './components/compare-file-input/compare-file-input.component';
|
||||
import { REDDocumentViewer } from './services/document-viewer.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-pdf-viewer',
|
||||
@ -16,6 +17,7 @@ export class PdfViewerComponent implements OnInit {
|
||||
constructor(
|
||||
private readonly _pdf: PdfViewer,
|
||||
private readonly _annotationManager: REDAnnotationManager,
|
||||
private readonly _documentViewer: REDDocumentViewer,
|
||||
private readonly _viewerHeaderService: ViewerHeaderService,
|
||||
) {}
|
||||
|
||||
@ -23,6 +25,7 @@ export class PdfViewerComponent implements OnInit {
|
||||
const pdfInit = this._pdf.init(this._viewer.nativeElement as HTMLElement);
|
||||
return pdfInit.then(instance => {
|
||||
this._annotationManager.init(instance.Core.annotationManager);
|
||||
this._documentViewer.init(instance.Core.documentViewer);
|
||||
this._viewerHeaderService.init(this._compare.input);
|
||||
});
|
||||
}
|
||||
|
||||
@ -10,11 +10,20 @@ import { ViewerHeaderService } from './services/viewer-header.service';
|
||||
import { PaginatorComponent } from './components/paginator/paginator.component';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { AnnotationDrawService } from './services/annotation-draw.service';
|
||||
import { REDDocumentViewer } from './services/document-viewer.service';
|
||||
|
||||
@NgModule({
|
||||
declarations: [PdfViewerComponent, CompareFileInputComponent, PaginatorComponent],
|
||||
exports: [PdfViewerComponent],
|
||||
imports: [CommonModule, MatIconModule],
|
||||
providers: [PdfViewer, REDAnnotationManager, PageRotationService, TooltipsService, ViewerHeaderService, AnnotationDrawService],
|
||||
providers: [
|
||||
PdfViewer,
|
||||
REDDocumentViewer,
|
||||
REDAnnotationManager,
|
||||
PageRotationService,
|
||||
TooltipsService,
|
||||
ViewerHeaderService,
|
||||
AnnotationDrawService,
|
||||
],
|
||||
})
|
||||
export class PdfViewerModule {}
|
||||
|
||||
@ -14,6 +14,7 @@ import { PdfViewer } from './pdf-viewer.service';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { REDAnnotationManager } from './annotation-manager.service';
|
||||
import { List } from '@iqser/common-ui';
|
||||
import { REDDocumentViewer } from './document-viewer.service';
|
||||
import Annotation = Core.Annotations.Annotation;
|
||||
import Quad = Core.Math.Quad;
|
||||
|
||||
@ -29,6 +30,7 @@ export class AnnotationDrawService {
|
||||
private readonly _activatedRoute: ActivatedRoute,
|
||||
private readonly _annotationManager: REDAnnotationManager,
|
||||
private readonly _pdf: PdfViewer,
|
||||
private readonly _documentViewer: REDDocumentViewer,
|
||||
) {}
|
||||
|
||||
draw(annotations: List<AnnotationWrapper>, dossierTemplateId: string, hideSkipped: boolean) {
|
||||
@ -113,7 +115,7 @@ export class AnnotationDrawService {
|
||||
|
||||
private _computeSection(dossierTemplateId: string, pageNumber: number, sectionRectangle: ISectionRectangle) {
|
||||
const rectangleAnnot = this._pdf.rectangle();
|
||||
const pageHeight = this._pdf.getPageHeight(pageNumber);
|
||||
const pageHeight = this._documentViewer.getHeight(pageNumber);
|
||||
const rectangle: IRectangle = {
|
||||
topLeft: sectionRectangle.topLeft,
|
||||
page: pageNumber,
|
||||
@ -141,7 +143,7 @@ export class AnnotationDrawService {
|
||||
|
||||
if (annotationWrapper.superType === SuperTypes.TextHighlight) {
|
||||
const rectangleAnnot = this._pdf.rectangle();
|
||||
const pageHeight = this._pdf.getPageHeight(pageNumber);
|
||||
const pageHeight = this._documentViewer.getHeight(pageNumber);
|
||||
const rectangle: IRectangle = annotationWrapper.positions[0];
|
||||
rectangleAnnot.PageNumber = pageNumber;
|
||||
rectangleAnnot.X = rectangle.topLeft.x;
|
||||
@ -186,7 +188,7 @@ export class AnnotationDrawService {
|
||||
}
|
||||
|
||||
private _rectanglesToQuads(positions: IRectangle[], pageNumber: number): Quad[] {
|
||||
const pageHeight = this._pdf.getPageHeight(pageNumber);
|
||||
const pageHeight = this._documentViewer.getHeight(pageNumber);
|
||||
return positions.map(p => this._rectangleToQuad(p, pageHeight));
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,129 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Core } from '@pdftron/webviewer';
|
||||
import { NGXLogger } from 'ngx-logger';
|
||||
import { fromEvent, merge, Observable } from 'rxjs';
|
||||
import { debounceTime, filter, map, tap } from 'rxjs/operators';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { PdfViewer } from './pdf-viewer.service';
|
||||
import { UserPreferenceService } from '@services/user-preference.service';
|
||||
import { log, shareLast } from '@iqser/common-ui';
|
||||
import { stopAndPrevent, stopAndPreventIfNotAllowed } from '../utils/functions';
|
||||
import DocumentViewer = Core.DocumentViewer;
|
||||
import Color = Core.Annotations.Color;
|
||||
|
||||
@Injectable()
|
||||
export class REDDocumentViewer {
|
||||
loaded$: Observable<boolean>;
|
||||
pageComplete$: Observable<boolean>;
|
||||
keyUp$: Observable<KeyboardEvent>;
|
||||
|
||||
#document: DocumentViewer;
|
||||
|
||||
constructor(
|
||||
private readonly _logger: NGXLogger,
|
||||
private readonly _userPreferenceService: UserPreferenceService,
|
||||
private readonly _pdf: PdfViewer,
|
||||
private readonly _activatedRoute: ActivatedRoute,
|
||||
) {}
|
||||
|
||||
get PDFDoc() {
|
||||
return this.document?.getPDFDoc();
|
||||
}
|
||||
|
||||
get document() {
|
||||
return this.#document.getDocument();
|
||||
}
|
||||
|
||||
get #documentUnloaded$() {
|
||||
const event$ = fromEvent(this.#document, 'documentUnloaded');
|
||||
const toBool$ = event$.pipe(map(() => false));
|
||||
|
||||
return toBool$.pipe(tap(() => this._logger.info('[PDF] Document unloaded')));
|
||||
}
|
||||
|
||||
get #documentLoaded$() {
|
||||
const event$ = fromEvent(this.#document, 'documentLoaded');
|
||||
const toBool$ = event$.pipe(map(() => true));
|
||||
|
||||
return toBool$.pipe(
|
||||
tap(() => this.#setCurrentPage()),
|
||||
tap(() => this.#setInitialDisplayMode()),
|
||||
tap(() => this.updateTooltipsVisibility()),
|
||||
tap(() => this._logger.info('[PDF] Document loaded')),
|
||||
);
|
||||
}
|
||||
|
||||
get #keyUp$() {
|
||||
return fromEvent<KeyboardEvent>(this.#document, 'keyUp').pipe(
|
||||
tap(stopAndPreventIfNotAllowed),
|
||||
filter($event => ($event.target as HTMLElement)?.tagName?.toLowerCase() !== 'input'),
|
||||
filter($event => $event.key.startsWith('Arrow') || $event.key === 'f'),
|
||||
tap(stopAndPrevent),
|
||||
log('[PDF] Keyboard shortcut'),
|
||||
);
|
||||
}
|
||||
|
||||
get #pageComplete$() {
|
||||
return fromEvent(this.#document, 'pageComplete').pipe(debounceTime(300));
|
||||
}
|
||||
|
||||
close() {
|
||||
this._logger.info('[PDF] Closing document');
|
||||
this.#document.closeDocument();
|
||||
this._pdf.closeCompareMode();
|
||||
}
|
||||
|
||||
updateTooltipsVisibility(): void {
|
||||
const current = this._userPreferenceService.getFilePreviewTooltipsPreference();
|
||||
this._pdf.instance.UI.setAnnotationContentOverlayHandler(() => (current ? undefined : false));
|
||||
}
|
||||
|
||||
init(document: DocumentViewer) {
|
||||
this.#document = document;
|
||||
this.loaded$ = merge(this.#documentUnloaded$, this.#documentLoaded$).pipe(shareLast());
|
||||
this.pageComplete$ = this.#pageComplete$.pipe(shareLast());
|
||||
this.keyUp$ = this.#keyUp$;
|
||||
}
|
||||
|
||||
async lock() {
|
||||
const document = await this.PDFDoc;
|
||||
if (!document) {
|
||||
return false;
|
||||
}
|
||||
|
||||
await document.lock();
|
||||
this._logger.info('[PDF] Locked');
|
||||
return true;
|
||||
}
|
||||
|
||||
setRectangleToolStyles(color: Color) {
|
||||
this.#document.getTool('AnnotationCreateRectangle').setStyles({
|
||||
StrokeThickness: 2,
|
||||
StrokeColor: color,
|
||||
FillColor: color,
|
||||
Opacity: 0.6,
|
||||
});
|
||||
}
|
||||
|
||||
getHeight(page: number) {
|
||||
try {
|
||||
return this.#document.getPageHeight(page);
|
||||
} catch {
|
||||
// might throw Error: getPageHeight was called before the 'documentLoaded' event
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#setCurrentPage() {
|
||||
const currentDocPage = this._activatedRoute.snapshot.queryParamMap.get('page');
|
||||
this.#document.setCurrentPage(Number(currentDocPage ?? '1'));
|
||||
}
|
||||
|
||||
#setInitialDisplayMode() {
|
||||
this._pdf.instance.UI.setFitMode('FitPage');
|
||||
const displayModeManager = this.#document.getDisplayModeManager();
|
||||
const instanceDisplayMode = displayModeManager.getDisplayMode();
|
||||
instanceDisplayMode.mode = this._pdf.isCompare ? 'Facing' : 'Single';
|
||||
displayModeManager.setDisplayMode(instanceDisplayMode);
|
||||
}
|
||||
}
|
||||
@ -3,23 +3,21 @@ import WebViewer, { Core, WebViewerInstance, WebViewerOptions } from '@pdftron/w
|
||||
import { environment } from '../../../../environments/environment';
|
||||
import { BASE_HREF_FN } from '../../../tokens';
|
||||
import { File } from '@red/domain';
|
||||
import { ErrorService, log, shareDistinctLast, shareLast } from '@iqser/common-ui';
|
||||
import { ErrorService, shareDistinctLast, shareLast } from '@iqser/common-ui';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { debounceTime, distinctUntilChanged, filter, map, startWith, tap } from 'rxjs/operators';
|
||||
import { BehaviorSubject, combineLatest, fromEvent, merge, Observable } from 'rxjs';
|
||||
import { distinctUntilChanged, map, startWith, tap } from 'rxjs/operators';
|
||||
import { BehaviorSubject, combineLatest, fromEvent, Observable } from 'rxjs';
|
||||
import { ConfigService } from '../../../services/config.service';
|
||||
import { NGXLogger } from 'ngx-logger';
|
||||
import { DISABLED_HOTKEYS, DOCUMENT_LOADING_ERROR, USELESS_ELEMENTS } from '../utils/constants';
|
||||
import { Rgb } from '../utils/types';
|
||||
import { UserPreferenceService } from '../../../services/user-preference.service';
|
||||
import { asList, stopAndPrevent, stopAndPreventIfNotAllowed } from '../utils/functions';
|
||||
import { asList } from '../utils/functions';
|
||||
import { REDAnnotationManager } from './annotation-manager.service';
|
||||
import AnnotationManager = Core.AnnotationManager;
|
||||
import TextTool = Core.Tools.TextTool;
|
||||
import Annotation = Core.Annotations.Annotation;
|
||||
import TextHighlightAnnotation = Core.Annotations.TextHighlightAnnotation;
|
||||
import DocumentViewer = Core.DocumentViewer;
|
||||
import Color = Core.Annotations.Color;
|
||||
|
||||
@Injectable()
|
||||
export class PdfViewer {
|
||||
@ -27,28 +25,28 @@ export class PdfViewer {
|
||||
map(params => Number(params.get('page') ?? '1')),
|
||||
shareDistinctLast(),
|
||||
);
|
||||
|
||||
/**
|
||||
* @deprecated Use REDDocumentViewer service instead
|
||||
*/
|
||||
documentViewer: DocumentViewer;
|
||||
/**
|
||||
* @deprecated Use REDAnnotationManager service instead
|
||||
*/
|
||||
annotationManager: AnnotationManager;
|
||||
|
||||
loaded$: Observable<boolean>;
|
||||
pageComplete$: Observable<unknown>;
|
||||
pageChanged$: Observable<number>;
|
||||
compareMode$: Observable<boolean>;
|
||||
totalPages$: Observable<number>;
|
||||
keyUp$: Observable<KeyboardEvent>;
|
||||
|
||||
#instance: WebViewerInstance;
|
||||
readonly #compareMode$ = new BehaviorSubject(false);
|
||||
#currentBlob: Blob;
|
||||
readonly #compareMode$ = new BehaviorSubject(false);
|
||||
|
||||
constructor(
|
||||
private readonly _logger: NGXLogger,
|
||||
private readonly _activatedRoute: ActivatedRoute,
|
||||
private readonly _annotationManager: REDAnnotationManager,
|
||||
private readonly _userPreferenceService: UserPreferenceService,
|
||||
private readonly _injector: Injector,
|
||||
) {}
|
||||
|
||||
@ -56,14 +54,6 @@ export class PdfViewer {
|
||||
return this.#instance;
|
||||
}
|
||||
|
||||
get PDFDoc() {
|
||||
return this.document?.getPDFDoc();
|
||||
}
|
||||
|
||||
get document() {
|
||||
return this.documentViewer.getDocument();
|
||||
}
|
||||
|
||||
get blob() {
|
||||
return this.#currentBlob;
|
||||
}
|
||||
@ -90,16 +80,6 @@ export class PdfViewer {
|
||||
return this.isCompare ? Math.ceil(currentInternalPage / 2) : currentInternalPage;
|
||||
}
|
||||
|
||||
get #keyUp$() {
|
||||
return fromEvent<KeyboardEvent>(this.documentViewer, 'keyUp').pipe(
|
||||
tap(stopAndPreventIfNotAllowed),
|
||||
filter($event => ($event.target as HTMLElement)?.tagName?.toLowerCase() !== 'input'),
|
||||
filter($event => $event.key.startsWith('Arrow') || $event.key === 'f'),
|
||||
tap(stopAndPrevent),
|
||||
log('[PDF] Keyboard shortcut'),
|
||||
);
|
||||
}
|
||||
|
||||
get #totalPages$() {
|
||||
const layoutChanged$ = fromEvent(this.documentViewer, 'layoutChanged').pipe(startWith(''));
|
||||
const pageCount$ = layoutChanged$.pipe(
|
||||
@ -111,33 +91,10 @@ export class PdfViewer {
|
||||
return docChanged$.pipe(map(([pageCount, isCompare]) => (isCompare ? Math.ceil(pageCount / 2) : pageCount)));
|
||||
}
|
||||
|
||||
get #pageComplete$() {
|
||||
return fromEvent(this.documentViewer, 'pageComplete').pipe(debounceTime(300));
|
||||
}
|
||||
|
||||
get #paginationOffset() {
|
||||
return this.isCompare ? 2 : 1;
|
||||
}
|
||||
|
||||
get #documentLoaded$() {
|
||||
const event$ = fromEvent(this.documentViewer, 'documentLoaded');
|
||||
const toBool$ = event$.pipe(map(() => true));
|
||||
|
||||
return toBool$.pipe(
|
||||
tap(() => this.#setCurrentPage()),
|
||||
tap(() => this.#setInitialDisplayMode()),
|
||||
tap(() => this.updateTooltipsVisibility()),
|
||||
tap(() => this._logger.info('[PDF] Document loaded')),
|
||||
);
|
||||
}
|
||||
|
||||
get #documentUnloaded$() {
|
||||
const event$ = fromEvent(this.documentViewer, 'documentUnloaded');
|
||||
const toBool$ = event$.pipe(map(() => false));
|
||||
|
||||
return toBool$.pipe(tap(() => this._logger.info('[PDF] Document unloaded')));
|
||||
}
|
||||
|
||||
get #currentInternalPage() {
|
||||
return this.documentViewer.getCurrentPage();
|
||||
}
|
||||
@ -173,12 +130,9 @@ export class PdfViewer {
|
||||
this.documentViewer = this.#instance.Core.documentViewer;
|
||||
this.annotationManager = this.#instance.Core.annotationManager;
|
||||
|
||||
this.loaded$ = merge(this.#documentUnloaded$, this.#documentLoaded$).pipe(shareLast());
|
||||
this.compareMode$ = this.#compareMode$.asObservable();
|
||||
this.pageComplete$ = this.#pageComplete$.pipe(shareLast());
|
||||
this.pageChanged$ = this.#pageChanged$.pipe(shareLast());
|
||||
this.totalPages$ = this.#totalPages$.pipe(shareDistinctLast());
|
||||
this.keyUp$ = this.#keyUp$;
|
||||
this.#setSelectionMode();
|
||||
this.#configureElements();
|
||||
this.#disableHotkeys();
|
||||
@ -187,15 +141,6 @@ export class PdfViewer {
|
||||
return this.#instance;
|
||||
}
|
||||
|
||||
setRectangleToolStyles(color: Color) {
|
||||
this.documentViewer.getTool('AnnotationCreateRectangle').setStyles({
|
||||
StrokeThickness: 2,
|
||||
StrokeColor: color,
|
||||
FillColor: color,
|
||||
Opacity: 0.6,
|
||||
});
|
||||
}
|
||||
|
||||
enable(dataElements: string[] | string) {
|
||||
this.#instance.UI.enableElements(asList(dataElements));
|
||||
}
|
||||
@ -204,21 +149,6 @@ export class PdfViewer {
|
||||
this.#instance.UI.disableElements(asList(dataElements));
|
||||
}
|
||||
|
||||
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.closeCompareMode();
|
||||
this.documentViewer.closeDocument();
|
||||
}
|
||||
|
||||
openCompareMode() {
|
||||
this.#compareMode$.next(true);
|
||||
}
|
||||
@ -227,17 +157,6 @@ export class PdfViewer {
|
||||
this.#compareMode$.next(false);
|
||||
}
|
||||
|
||||
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 = () => {
|
||||
@ -255,11 +174,6 @@ export class PdfViewer {
|
||||
this.#instance.UI.loadDocument(document, { filename: file?.filename + '.pdf' ?? 'document.pdf', onError });
|
||||
}
|
||||
|
||||
updateTooltipsVisibility(): void {
|
||||
const current = this._userPreferenceService.getFilePreviewTooltipsPreference();
|
||||
this.#instance.UI.setAnnotationContentOverlayHandler(() => (current ? undefined : false));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@ -297,14 +211,6 @@ export class PdfViewer {
|
||||
});
|
||||
}
|
||||
|
||||
#setInitialDisplayMode() {
|
||||
this.#instance.UI.setFitMode('FitPage');
|
||||
const displayModeManager = this.documentViewer.getDisplayModeManager();
|
||||
const instanceDisplayMode = displayModeManager.getDisplayMode();
|
||||
instanceDisplayMode.mode = this.isCompare ? 'Facing' : 'Single';
|
||||
displayModeManager.setDisplayMode(instanceDisplayMode);
|
||||
}
|
||||
|
||||
#navigateTo(pageNumber: number) {
|
||||
if (this.#currentInternalPage !== pageNumber) {
|
||||
this.documentViewer.displayPageLocation(pageNumber, 0, 0);
|
||||
@ -337,10 +243,4 @@ export class PdfViewer {
|
||||
|
||||
return WebViewer(options, htmlElement);
|
||||
}
|
||||
|
||||
#setCurrentPage() {
|
||||
const currentDocPage = this._activatedRoute.snapshot.queryParamMap.get('page');
|
||||
console.log(currentDocPage);
|
||||
this.documentViewer.setCurrentPage(Number(currentDocPage ?? '1'));
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { BASE_HREF_FN, BaseHrefFn } from '../../../tokens';
|
||||
import { PdfViewer } from './pdf-viewer.service';
|
||||
import { REDDocumentViewer } from './document-viewer.service';
|
||||
|
||||
@Injectable()
|
||||
export class TooltipsService {
|
||||
@ -14,6 +15,7 @@ export class TooltipsService {
|
||||
constructor(
|
||||
@Inject(BASE_HREF_FN) private readonly _convertPath: BaseHrefFn,
|
||||
private readonly _pdf: PdfViewer,
|
||||
private readonly _documentViewer: REDDocumentViewer,
|
||||
private readonly _userPreferenceService: UserPreferenceService,
|
||||
private readonly _translateService: TranslateService,
|
||||
) {}
|
||||
@ -31,7 +33,7 @@ export class TooltipsService {
|
||||
|
||||
async toggleTooltips(): Promise<void> {
|
||||
await this._userPreferenceService.toggleFilePreviewTooltipsPreference();
|
||||
this._pdf.updateTooltipsVisibility();
|
||||
this._documentViewer.updateTooltipsVisibility();
|
||||
this._pdf.instance.UI.updateElement(HeaderElements.TOGGLE_TOOLTIPS, {
|
||||
title: this.toggleTooltipsBtnTitle,
|
||||
img: this.toggleTooltipsBtnIcon,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user