@epam/pdf-highlighter-kit 0.0.8 → 0.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -2
- package/dist/PDFHighlightViewer.d.ts +14 -4
- package/dist/PDFHighlightViewer.d.ts.map +1 -1
- package/dist/PDFHighlightViewer.js +104 -50
- package/dist/PDFHighlightViewer.js.map +1 -1
- package/dist/api.d.ts +2 -2
- package/dist/api.d.ts.map +1 -1
- package/dist/core/pdf-engine.d.ts.map +1 -1
- package/dist/core/pdf-engine.js +1 -0
- package/dist/core/pdf-engine.js.map +1 -1
- package/dist/core/unified-layer-builder.d.ts.map +1 -1
- package/dist/core/unified-layer-builder.js +10 -5
- package/dist/core/unified-layer-builder.js.map +1 -1
- package/dist/core/viewport-manager.d.ts +15 -4
- package/dist/core/viewport-manager.d.ts.map +1 -1
- package/dist/core/viewport-manager.js +82 -33
- package/dist/core/viewport-manager.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +11 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/label-style.d.ts +6 -0
- package/dist/utils/label-style.d.ts.map +1 -1
- package/dist/utils/label-style.js +36 -0
- package/dist/utils/label-style.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -82,6 +82,8 @@ Each highlight carries its own style. No categories are required.
|
|
|
82
82
|
|
|
83
83
|
**Icon before label:** Optionally set `beforeIcon` to an inline SVG string (e.g. from [Tabler Icons](https://tabler.io/icons)) to render an icon inside the label frame, to the left of the text. The icon inherits the label color via `currentColor`. Use `labelStyle.iconSize` to set the icon size (e.g. `14` or `'14px'`) and `labelStyle.iconColor` to set the icon color (e.g. `'#ff6b6b'`); if `iconColor` is not set, the icon uses the label text color. Only pass trusted SVG content (e.g. from your bundle or `@tabler/icons`); in React with Vite you can use `import iconSvg from '@tabler/icons/icons/outline/alert-circle.svg?raw'` and pass `iconSvg` as `beforeIcon`.
|
|
84
84
|
|
|
85
|
+
**Scalable label:** Set `isLabelScalable: true` on the highlight so the label’s size (font, padding, icon, border radius) scales with zoom; the label stays proportional to the highlight. Border and outline thickness are not scaled (they stay fixed like the highlight’s outline). Best used with `labelStyle` values in `px` or numbers.
|
|
86
|
+
|
|
85
87
|
```ts
|
|
86
88
|
export interface BBox {
|
|
87
89
|
x1: number;
|
|
@@ -139,6 +141,7 @@ export interface InputHighlightData {
|
|
|
139
141
|
style?: HighlightStyle;
|
|
140
142
|
label?: string;
|
|
141
143
|
beforeIcon?: string; // inline SVG string (trusted content only, e.g. Tabler icons)
|
|
144
|
+
isLabelScalable?: boolean; // when true, label size scales with zoom (borders stay fixed)
|
|
142
145
|
labelStyle?: HighlightLabelStyle;
|
|
143
146
|
tooltipText?: string;
|
|
144
147
|
metadata?: Record<string, any>;
|
|
@@ -198,9 +201,14 @@ export interface ViewerOptions {
|
|
|
198
201
|
|
|
199
202
|
// Page dimensions for which bbox coordinates were calculated
|
|
200
203
|
bboxSourceDimensions?: BBoxDimensions;
|
|
204
|
+
|
|
205
|
+
// Only show these document page numbers (1-based). If empty or omitted, all pages are shown.
|
|
206
|
+
selectedPages?: number[];
|
|
201
207
|
}
|
|
202
208
|
```
|
|
203
209
|
|
|
210
|
+
**Displaying only selected pages:** Use `selectedPages` to show a subset of the document (e.g. `[1, 3, 5]` or `[2, 4, 6, 8]`). The full PDF is still loaded; only the listed pages get DOM containers and participate in scrolling and rendering. Page numbers in the API (e.g. `setPage`, events, highlights) remain physical document page numbers. You can pass `selectedPages` in `init` options or per load via `loadPDF(source, { selectedPages })`; the latter overrides the init value for that load. `getTotalPages()` returns the length of the displayed list when `selectedPages` is set.
|
|
211
|
+
|
|
204
212
|
## API Reference
|
|
205
213
|
|
|
206
214
|
### Main Methods
|
|
@@ -209,9 +217,18 @@ export interface ViewerOptions {
|
|
|
209
217
|
|
|
210
218
|
Initialize the viewer with a container element and optional configuration.
|
|
211
219
|
|
|
212
|
-
#### `loadPDF(source: string | ArrayBuffer | Blob): Promise<void>`
|
|
220
|
+
#### `loadPDF(source: string | ArrayBuffer | Blob, options?: LoadPDFOptions): Promise<void>`
|
|
221
|
+
|
|
222
|
+
Load a PDF document from URL or ArrayBuffer. Optional second argument:
|
|
223
|
+
|
|
224
|
+
```ts
|
|
225
|
+
interface LoadPDFOptions {
|
|
226
|
+
/** Only show these document page numbers (1-based). If empty or omitted, all pages are shown. */
|
|
227
|
+
selectedPages?: number[];
|
|
228
|
+
}
|
|
229
|
+
```
|
|
213
230
|
|
|
214
|
-
|
|
231
|
+
Example: `await viewer.loadPDF(url, { selectedPages: [1, 3, 5] });` shows only pages 1, 3, and 5. When `options.selectedPages` is provided, it overrides `ViewerOptions.selectedPages` for this load.
|
|
215
232
|
|
|
216
233
|
#### `loadHighlights(highlights: InputHighlightData[]): void`
|
|
217
234
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PDFHighlightViewer as IPDFHighlightViewer } from './api';
|
|
2
|
-
import { ViewerOptions, TextRange, SelectionWithMetadata, PerformanceMetrics, HighlightAnalytics, AccessibilityFeatures, InteractionMode, InputHighlightData, HighlightStyle, HighlightLabelStyle, ZoomValue, ThumbnailOptions } from './types';
|
|
2
|
+
import { ViewerOptions, LoadPDFOptions, TextRange, SelectionWithMetadata, PerformanceMetrics, HighlightAnalytics, AccessibilityFeatures, InteractionMode, InputHighlightData, HighlightStyle, HighlightLabelStyle, ZoomValue, ThumbnailOptions } from './types';
|
|
3
3
|
type EventCallback = (data?: unknown) => void;
|
|
4
4
|
export declare class PDFHighlightViewer implements IPDFHighlightViewer {
|
|
5
5
|
private pdfEngine;
|
|
@@ -15,6 +15,10 @@ export declare class PDFHighlightViewer implements IPDFHighlightViewer {
|
|
|
15
15
|
private currentPage;
|
|
16
16
|
private currentScale;
|
|
17
17
|
private totalPages;
|
|
18
|
+
/** Actual number of pages in the loaded PDF (1..documentNumPages). */
|
|
19
|
+
private documentNumPages;
|
|
20
|
+
/** When set, only these physical page numbers are shown; null = all pages. */
|
|
21
|
+
private selectedPages;
|
|
18
22
|
private selectedTermId;
|
|
19
23
|
private isInitialized;
|
|
20
24
|
private navIndex;
|
|
@@ -58,9 +62,15 @@ export declare class PDFHighlightViewer implements IPDFHighlightViewer {
|
|
|
58
62
|
* Handle keyboard navigation
|
|
59
63
|
*/
|
|
60
64
|
private handleKeyboardNavigation;
|
|
61
|
-
loadPDF(source: string | ArrayBuffer | Blob): Promise<void>;
|
|
65
|
+
loadPDF(source: string | ArrayBuffer | Blob, options?: LoadPDFOptions): Promise<void>;
|
|
62
66
|
/**
|
|
63
|
-
*
|
|
67
|
+
* Normalize selectedPages: filter to 1..numPages, dedupe, sort. Empty result => null (show all).
|
|
68
|
+
*/
|
|
69
|
+
private normalizeSelectedPages;
|
|
70
|
+
/** List of physical page numbers to display (either selectedPages or 1..documentNumPages). */
|
|
71
|
+
private getDisplayedPageList;
|
|
72
|
+
/**
|
|
73
|
+
* Create DOM containers for displayed pages only (selectedPages or all)
|
|
64
74
|
*/
|
|
65
75
|
private createPageContainers;
|
|
66
76
|
/**
|
|
@@ -188,7 +198,7 @@ export declare class PDFHighlightViewer implements IPDFHighlightViewer {
|
|
|
188
198
|
private toPageCoordinateDimensions;
|
|
189
199
|
private normalizeBBoxForPage;
|
|
190
200
|
/**
|
|
191
|
-
* Build spatial indices for
|
|
201
|
+
* Build spatial indices for displayed pages only
|
|
192
202
|
*/
|
|
193
203
|
private buildAllSpatialIndices;
|
|
194
204
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PDFHighlightViewer.d.ts","sourceRoot":"","sources":["../src/PDFHighlightViewer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,IAAI,mBAAmB,EAAE,MAAM,OAAO,CAAC;AAClE,OAAO,EACL,aAAa,
|
|
1
|
+
{"version":3,"file":"PDFHighlightViewer.d.ts","sourceRoot":"","sources":["../src/PDFHighlightViewer.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,7 +6,7 @@ import { UnifiedInteractionHandler } from './core/interaction-handler';
|
|
|
6
6
|
import { PerformanceOptimizer } from './core/performance-optimizer';
|
|
7
7
|
import { buildHighlightsIndex } from './utils/highlight-adapter';
|
|
8
8
|
import { applyHighlightVisualStyle, getHighlightBaseOpacity, getHighlightHoverOpacity, resolveHighlightStyle, } from './utils/highlight-style';
|
|
9
|
-
import { appendLabelIcon, applyBaseOutlineStyle, applyLabelOutlineStyle, applyLabelStyle, } from './utils/label-style';
|
|
9
|
+
import { appendLabelIcon, applyBaseOutlineStyle, applyLabelOutlineStyle, applyLabelStyle, scaleLabelStyle, } from './utils/label-style';
|
|
10
10
|
const CONTAINER_PADDING = 40;
|
|
11
11
|
const ZOOM_STEP = 1.2;
|
|
12
12
|
export class PDFHighlightViewer {
|
|
@@ -23,6 +23,10 @@ export class PDFHighlightViewer {
|
|
|
23
23
|
this.currentPage = 1;
|
|
24
24
|
this.currentScale = 1.5;
|
|
25
25
|
this.totalPages = 0;
|
|
26
|
+
/** Actual number of pages in the loaded PDF (1..documentNumPages). */
|
|
27
|
+
this.documentNumPages = 0;
|
|
28
|
+
/** When set, only these physical page numbers are shown; null = all pages. */
|
|
29
|
+
this.selectedPages = null;
|
|
26
30
|
this.selectedTermId = null;
|
|
27
31
|
this.isInitialized = false;
|
|
28
32
|
this.navIndex = -1;
|
|
@@ -441,23 +445,30 @@ export class PDFHighlightViewer {
|
|
|
441
445
|
* Handle keyboard navigation
|
|
442
446
|
*/
|
|
443
447
|
handleKeyboardNavigation(event) {
|
|
448
|
+
const displayed = this.getDisplayedPageList();
|
|
449
|
+
if (displayed.length === 0)
|
|
450
|
+
return;
|
|
451
|
+
const currentIdx = displayed.indexOf(this.currentPage);
|
|
452
|
+
const idx = currentIdx >= 0 ? currentIdx : 0;
|
|
444
453
|
switch (event.key) {
|
|
445
454
|
case 'PageDown':
|
|
446
455
|
case 'ArrowDown':
|
|
447
|
-
|
|
456
|
+
if (idx < displayed.length - 1)
|
|
457
|
+
this.setPage(displayed[idx + 1]);
|
|
448
458
|
event.preventDefault();
|
|
449
459
|
break;
|
|
450
460
|
case 'PageUp':
|
|
451
461
|
case 'ArrowUp':
|
|
452
|
-
|
|
462
|
+
if (idx > 0)
|
|
463
|
+
this.setPage(displayed[idx - 1]);
|
|
453
464
|
event.preventDefault();
|
|
454
465
|
break;
|
|
455
466
|
case 'Home':
|
|
456
|
-
this.setPage(
|
|
467
|
+
this.setPage(displayed[0]);
|
|
457
468
|
event.preventDefault();
|
|
458
469
|
break;
|
|
459
470
|
case 'End':
|
|
460
|
-
this.setPage(
|
|
471
|
+
this.setPage(displayed[displayed.length - 1]);
|
|
461
472
|
event.preventDefault();
|
|
462
473
|
break;
|
|
463
474
|
case '+':
|
|
@@ -474,20 +485,23 @@ export class PDFHighlightViewer {
|
|
|
474
485
|
// =============================================================================
|
|
475
486
|
// PDF Management
|
|
476
487
|
// =============================================================================
|
|
477
|
-
async loadPDF(source) {
|
|
488
|
+
async loadPDF(source, options) {
|
|
478
489
|
if (!this.isInitialized) {
|
|
479
490
|
throw new Error('Viewer must be initialized before loading PDF');
|
|
480
491
|
}
|
|
481
492
|
try {
|
|
482
|
-
// Load document with PDF engine
|
|
483
493
|
await this.pdfEngine.loadDocument(source);
|
|
484
494
|
const docInfo = this.pdfEngine.getDocumentInfo();
|
|
485
|
-
this.
|
|
486
|
-
|
|
495
|
+
this.documentNumPages = docInfo.numPages;
|
|
496
|
+
const rawSelected = options?.selectedPages ?? this.options.selectedPages;
|
|
497
|
+
const selectedPages = this.normalizeSelectedPages(rawSelected, this.documentNumPages);
|
|
498
|
+
this.selectedPages = selectedPages;
|
|
499
|
+
this.totalPages = selectedPages !== null ? selectedPages.length : this.documentNumPages;
|
|
500
|
+
this.viewportManager.setSelectedPages(selectedPages);
|
|
487
501
|
this.viewportManager.setTotalPages(this.totalPages);
|
|
488
|
-
// Create page containers
|
|
489
502
|
await this.createPageContainers();
|
|
490
|
-
|
|
503
|
+
const displayed = this.getDisplayedPageList();
|
|
504
|
+
this.currentPage = displayed.length > 0 ? displayed[0] : 1;
|
|
491
505
|
await this.loadInitialPages();
|
|
492
506
|
if (this.options.enableVirtualScrolling === false) {
|
|
493
507
|
await this.renderAllPagesBatched(2);
|
|
@@ -500,17 +514,45 @@ export class PDFHighlightViewer {
|
|
|
500
514
|
}
|
|
501
515
|
}
|
|
502
516
|
/**
|
|
503
|
-
*
|
|
517
|
+
* Normalize selectedPages: filter to 1..numPages, dedupe, sort. Empty result => null (show all).
|
|
518
|
+
*/
|
|
519
|
+
normalizeSelectedPages(raw, numPages) {
|
|
520
|
+
if (!raw || raw.length === 0)
|
|
521
|
+
return null;
|
|
522
|
+
const set = new Set();
|
|
523
|
+
for (const p of raw) {
|
|
524
|
+
if (Number.isInteger(p) && p >= 1 && p <= numPages)
|
|
525
|
+
set.add(p);
|
|
526
|
+
}
|
|
527
|
+
if (set.size === 0)
|
|
528
|
+
return null;
|
|
529
|
+
return Array.from(set).sort((a, b) => a - b);
|
|
530
|
+
}
|
|
531
|
+
/** List of physical page numbers to display (either selectedPages or 1..documentNumPages). */
|
|
532
|
+
getDisplayedPageList() {
|
|
533
|
+
if (this.selectedPages !== null && this.selectedPages.length > 0) {
|
|
534
|
+
return this.selectedPages;
|
|
535
|
+
}
|
|
536
|
+
const list = [];
|
|
537
|
+
for (let i = 1; i <= this.documentNumPages; i++) {
|
|
538
|
+
list.push(i);
|
|
539
|
+
}
|
|
540
|
+
return list;
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* Create DOM containers for displayed pages only (selectedPages or all)
|
|
504
544
|
*/
|
|
505
545
|
async createPageContainers() {
|
|
506
546
|
if (!this.pdfContainer)
|
|
507
547
|
return;
|
|
508
|
-
// Clear existing containers
|
|
509
548
|
this.pdfContainer.innerHTML = '';
|
|
510
549
|
this.pageContainers.clear();
|
|
511
|
-
const
|
|
550
|
+
const displayed = this.getDisplayedPageList();
|
|
551
|
+
if (displayed.length === 0)
|
|
552
|
+
return;
|
|
553
|
+
const firstPageDimensions = await this.getPageDimensions(displayed[0]);
|
|
512
554
|
const avgPageHeight = firstPageDimensions.height;
|
|
513
|
-
for (
|
|
555
|
+
for (const pageNumber of displayed) {
|
|
514
556
|
const pageContainer = document.createElement('div');
|
|
515
557
|
pageContainer.className = 'pdf-page-container';
|
|
516
558
|
pageContainer.setAttribute('data-page-number', pageNumber.toString());
|
|
@@ -522,7 +564,6 @@ export class PDFHighlightViewer {
|
|
|
522
564
|
this.pdfContainer.appendChild(pageContainer);
|
|
523
565
|
this.pageContainers.set(pageNumber, pageContainer);
|
|
524
566
|
}
|
|
525
|
-
// Update viewport manager with real page dimensions
|
|
526
567
|
this.viewportManager.updateDimensions(this.container?.clientHeight || 600, avgPageHeight);
|
|
527
568
|
}
|
|
528
569
|
/**
|
|
@@ -531,19 +572,19 @@ export class PDFHighlightViewer {
|
|
|
531
572
|
async loadInitialPages() {
|
|
532
573
|
if (!this.container)
|
|
533
574
|
return;
|
|
534
|
-
// Get what should be visible at the top
|
|
535
575
|
const visiblePages = this.viewportManager.getVisiblePages(0, this.container.clientHeight);
|
|
536
|
-
|
|
576
|
+
const firstPage = this.getDisplayedPageList()[0];
|
|
577
|
+
if (firstPage === undefined)
|
|
578
|
+
return;
|
|
537
579
|
try {
|
|
538
|
-
await this.renderPage(
|
|
580
|
+
await this.renderPage(firstPage);
|
|
539
581
|
}
|
|
540
582
|
catch (error) {
|
|
541
583
|
console.error('Failed to load initial page:', error);
|
|
542
584
|
}
|
|
543
|
-
// Load other visible pages with small delay
|
|
544
585
|
setTimeout(() => {
|
|
545
586
|
visiblePages
|
|
546
|
-
.filter((page) => page
|
|
587
|
+
.filter((page) => page !== firstPage)
|
|
547
588
|
.forEach((pageNumber) => {
|
|
548
589
|
this.renderPage(pageNumber).catch((error) => {
|
|
549
590
|
console.error(`Failed to load initial page ${pageNumber}:`, error);
|
|
@@ -552,13 +593,15 @@ export class PDFHighlightViewer {
|
|
|
552
593
|
}, 100);
|
|
553
594
|
}
|
|
554
595
|
async renderAllPagesBatched(batchSize = 2) {
|
|
555
|
-
|
|
596
|
+
const displayed = this.getDisplayedPageList();
|
|
597
|
+
for (let i = 0; i < displayed.length; i += batchSize) {
|
|
556
598
|
const batch = [];
|
|
557
|
-
for (let j = i; j < i + batchSize && j
|
|
558
|
-
const
|
|
599
|
+
for (let j = i; j < i + batchSize && j < displayed.length; j++) {
|
|
600
|
+
const pageNumber = displayed[j];
|
|
601
|
+
const pageContainer = this.pageContainers.get(pageNumber);
|
|
559
602
|
if (pageContainer && !pageContainer.classList.contains('rendered')) {
|
|
560
|
-
batch.push(this.renderPage(
|
|
561
|
-
console.debug(`Failed to render page ${
|
|
603
|
+
batch.push(this.renderPage(pageNumber).catch((error) => {
|
|
604
|
+
console.debug(`Failed to render page ${pageNumber}:`, error);
|
|
562
605
|
}));
|
|
563
606
|
}
|
|
564
607
|
}
|
|
@@ -615,7 +658,8 @@ export class PDFHighlightViewer {
|
|
|
615
658
|
});
|
|
616
659
|
}
|
|
617
660
|
setPage(pageNumber) {
|
|
618
|
-
|
|
661
|
+
const displayed = this.getDisplayedPageList();
|
|
662
|
+
if (!displayed.includes(pageNumber))
|
|
619
663
|
return;
|
|
620
664
|
if (this.container) {
|
|
621
665
|
this.container.scrollTop = this.getPageScrollTop(pageNumber);
|
|
@@ -881,6 +925,7 @@ export class PDFHighlightViewer {
|
|
|
881
925
|
async reRenderVisiblePages() {
|
|
882
926
|
if (!this.container)
|
|
883
927
|
return;
|
|
928
|
+
const pageToKeep = this.currentPage;
|
|
884
929
|
console.log('Zoom changed to:', this.currentScale);
|
|
885
930
|
// Clear ALL cached data for the new scale
|
|
886
931
|
this.pdfEngine.clearAllPageCache();
|
|
@@ -909,12 +954,15 @@ export class PDFHighlightViewer {
|
|
|
909
954
|
const avgPageHeight = validDimensions > 0 ? totalHeight / validDimensions : this.defaultPageHeight;
|
|
910
955
|
this.viewportManager.updateDimensions(this.container?.clientHeight || 600, avgPageHeight);
|
|
911
956
|
// Render currently visible pages
|
|
912
|
-
const scrollTop = this.container.scrollTop;
|
|
913
957
|
const containerHeight = this.container.clientHeight;
|
|
914
|
-
|
|
915
|
-
|
|
958
|
+
// Restore scroll so we stay on the same page after zoom
|
|
959
|
+
this.container.scrollTop = this.getPageScrollTop(pageToKeep);
|
|
960
|
+
// Recompute visible pages after scroll restore for correct rendering
|
|
961
|
+
const scrollTopAfterRestore = this.container.scrollTop;
|
|
962
|
+
const visiblePagesAfterRestore = this.viewportManager.getVisiblePages(scrollTopAfterRestore, containerHeight);
|
|
963
|
+
console.log('Re-rendering visible pages at new scale:', visiblePagesAfterRestore);
|
|
916
964
|
// Render visible pages immediately
|
|
917
|
-
for (const pageNumber of
|
|
965
|
+
for (const pageNumber of visiblePagesAfterRestore) {
|
|
918
966
|
try {
|
|
919
967
|
await this.renderPage(pageNumber);
|
|
920
968
|
}
|
|
@@ -1191,10 +1239,10 @@ export class PDFHighlightViewer {
|
|
|
1191
1239
|
highlightDiv.setAttribute('data-term-id', highlight.id);
|
|
1192
1240
|
highlightDiv.setAttribute('data-page', String(pageNumber));
|
|
1193
1241
|
highlightDiv.setAttribute('data-bbox-index', String(bboxIndex));
|
|
1194
|
-
const left = normalizedBBox.x1 * scale;
|
|
1195
|
-
const top = normalizedBBox.y1 * scale;
|
|
1196
|
-
const width = (normalizedBBox.x2 - normalizedBBox.x1) * scale;
|
|
1197
|
-
const height = (normalizedBBox.y2 - normalizedBBox.y1) * scale;
|
|
1242
|
+
const left = Math.round(normalizedBBox.x1 * scale);
|
|
1243
|
+
const top = Math.round(normalizedBBox.y1 * scale);
|
|
1244
|
+
const width = Math.round((normalizedBBox.x2 - normalizedBBox.x1) * scale);
|
|
1245
|
+
const height = Math.round((normalizedBBox.y2 - normalizedBBox.y1) * scale);
|
|
1198
1246
|
highlightDiv.style.position = 'absolute';
|
|
1199
1247
|
highlightDiv.style.left = `${left}px`;
|
|
1200
1248
|
highlightDiv.style.top = `${top}px`;
|
|
@@ -1289,13 +1337,16 @@ export class PDFHighlightViewer {
|
|
|
1289
1337
|
});
|
|
1290
1338
|
highlightLayer.appendChild(highlightDiv);
|
|
1291
1339
|
if (highlight.label || highlight.beforeIcon) {
|
|
1340
|
+
const effectiveLabelStyle = highlight.isLabelScalable && scale !== 1
|
|
1341
|
+
? scaleLabelStyle(highlight.labelStyle, scale)
|
|
1342
|
+
: highlight.labelStyle;
|
|
1292
1343
|
const labelEl = document.createElement('span');
|
|
1293
1344
|
labelEl.className = 'highlight-label';
|
|
1294
1345
|
labelEl.setAttribute('data-term-id', highlight.id);
|
|
1295
1346
|
labelEl.setAttribute('data-bbox-index', String(bboxIndex));
|
|
1296
1347
|
labelEl.dataset.baseLeft = String(left);
|
|
1297
1348
|
labelEl.dataset.baseTop = String(top);
|
|
1298
|
-
const { left: labelOffsetLeft, top: labelOffsetTop } = this.getLabelOffsets(
|
|
1349
|
+
const { left: labelOffsetLeft, top: labelOffsetTop } = this.getLabelOffsets(effectiveLabelStyle);
|
|
1299
1350
|
labelEl.style.position = 'absolute';
|
|
1300
1351
|
labelEl.style.left = `${left + labelOffsetLeft}px`;
|
|
1301
1352
|
labelEl.style.top = `${top + labelOffsetTop}px`;
|
|
@@ -1305,20 +1356,20 @@ export class PDFHighlightViewer {
|
|
|
1305
1356
|
labelEl.style.display = 'flex';
|
|
1306
1357
|
labelEl.style.alignItems = 'center';
|
|
1307
1358
|
labelEl.style.justifyContent = 'flex-end';
|
|
1308
|
-
labelEl.style.gap = '4px';
|
|
1359
|
+
labelEl.style.gap = highlight.isLabelScalable ? `${4 * scale}px` : '4px';
|
|
1309
1360
|
labelEl.style.pointerEvents = 'auto';
|
|
1310
1361
|
labelEl.style.cursor = 'pointer';
|
|
1311
1362
|
labelEl.style.whiteSpace = 'nowrap';
|
|
1312
|
-
if (
|
|
1313
|
-
|
|
1314
|
-
const borderColor =
|
|
1315
|
-
const borderWidth =
|
|
1363
|
+
if (effectiveLabelStyle?.borderColor !== undefined ||
|
|
1364
|
+
effectiveLabelStyle?.borderWidth !== undefined) {
|
|
1365
|
+
const borderColor = effectiveLabelStyle?.borderColor ?? 'currentColor';
|
|
1366
|
+
const borderWidth = effectiveLabelStyle?.borderWidth ?? '1px';
|
|
1316
1367
|
labelEl.style.border = `${borderWidth} solid ${borderColor}`;
|
|
1317
1368
|
}
|
|
1318
|
-
applyLabelOutlineStyle(labelEl,
|
|
1319
|
-
applyLabelStyle(labelEl,
|
|
1369
|
+
applyLabelOutlineStyle(labelEl, effectiveLabelStyle);
|
|
1370
|
+
applyLabelStyle(labelEl, effectiveLabelStyle);
|
|
1320
1371
|
labelEl.style.opacity = String(effectiveOpacity);
|
|
1321
|
-
appendLabelIcon(labelEl, highlight.beforeIcon,
|
|
1372
|
+
appendLabelIcon(labelEl, highlight.beforeIcon, effectiveLabelStyle);
|
|
1322
1373
|
if (highlight.label) {
|
|
1323
1374
|
labelEl.appendChild(document.createTextNode(highlight.label));
|
|
1324
1375
|
}
|
|
@@ -1389,7 +1440,10 @@ export class PDFHighlightViewer {
|
|
|
1389
1440
|
: parseFloat(el.dataset.originalOpacity ?? '0.3');
|
|
1390
1441
|
const bboxIdx = el.getAttribute('data-bbox-index');
|
|
1391
1442
|
const isWrapper = el.classList.contains('highlight-wrapper');
|
|
1392
|
-
const
|
|
1443
|
+
const scale = this.currentScale;
|
|
1444
|
+
const effectiveLabelStyle = highlight?.isLabelScalable && scale !== 1
|
|
1445
|
+
? scaleLabelStyle(highlight.labelStyle, scale)
|
|
1446
|
+
: highlight?.labelStyle;
|
|
1393
1447
|
const labelEl = isWrapper
|
|
1394
1448
|
? el.querySelector('.highlight-label')
|
|
1395
1449
|
: bboxIdx !== null
|
|
@@ -1399,12 +1453,12 @@ export class PDFHighlightViewer {
|
|
|
1399
1453
|
if (!isWrapper) {
|
|
1400
1454
|
const baseLabelLeft = parseFloat(labelEl.dataset.baseLeft ?? '0');
|
|
1401
1455
|
const baseLabelTop = parseFloat(labelEl.dataset.baseTop ?? '0');
|
|
1402
|
-
const { left: labelOffsetLeft, top: labelOffsetTop } = this.getLabelOffsets(
|
|
1456
|
+
const { left: labelOffsetLeft, top: labelOffsetTop } = this.getLabelOffsets(effectiveLabelStyle);
|
|
1403
1457
|
labelEl.style.left = `${baseLabelLeft + labelOffsetLeft}px`;
|
|
1404
1458
|
labelEl.style.top = `${baseLabelTop + labelOffsetTop}px`;
|
|
1405
1459
|
}
|
|
1406
|
-
applyLabelStyle(labelEl,
|
|
1407
|
-
applyLabelOutlineStyle(labelEl,
|
|
1460
|
+
applyLabelStyle(labelEl, effectiveLabelStyle);
|
|
1461
|
+
applyLabelOutlineStyle(labelEl, effectiveLabelStyle);
|
|
1408
1462
|
labelEl.style.opacity = String(highlightBaseOpacity);
|
|
1409
1463
|
}
|
|
1410
1464
|
if (this.options.highlightsConfig?.enableMultilineHover &&
|
|
@@ -1497,10 +1551,10 @@ export class PDFHighlightViewer {
|
|
|
1497
1551
|
};
|
|
1498
1552
|
}
|
|
1499
1553
|
/**
|
|
1500
|
-
* Build spatial indices for
|
|
1554
|
+
* Build spatial indices for displayed pages only
|
|
1501
1555
|
*/
|
|
1502
1556
|
buildAllSpatialIndices() {
|
|
1503
|
-
for (
|
|
1557
|
+
for (const pageNumber of this.getDisplayedPageList()) {
|
|
1504
1558
|
this.buildSpatialIndexForPage(pageNumber);
|
|
1505
1559
|
}
|
|
1506
1560
|
}
|