@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 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
- Load a PDF document from URL or ArrayBuffer.
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
- * Create DOM containers for all pages with real PDF dimensions
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 all pages
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":"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"}
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
- this.setPage(Math.min(this.totalPages, this.currentPage + 1));
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
- this.setPage(Math.max(1, this.currentPage - 1));
462
+ if (idx > 0)
463
+ this.setPage(displayed[idx - 1]);
453
464
  event.preventDefault();
454
465
  break;
455
466
  case 'Home':
456
- this.setPage(1);
467
+ this.setPage(displayed[0]);
457
468
  event.preventDefault();
458
469
  break;
459
470
  case 'End':
460
- this.setPage(this.totalPages);
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.totalPages = docInfo.numPages;
486
- // Update viewport manager with total pages
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
- // Load initial pages
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
- * Create DOM containers for all pages with real PDF dimensions
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 firstPageDimensions = await this.getPageDimensions(1);
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 (let pageNumber = 1; pageNumber <= this.totalPages; pageNumber++) {
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
- // Load first page immediately
576
+ const firstPage = this.getDisplayedPageList()[0];
577
+ if (firstPage === undefined)
578
+ return;
537
579
  try {
538
- await this.renderPage(1);
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 > 1)
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
- for (let i = 1; i <= this.totalPages; i += batchSize) {
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 <= this.totalPages; j++) {
558
- const pageContainer = this.pageContainers.get(j);
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(j).catch((error) => {
561
- console.debug(`Failed to render page ${j}:`, error);
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
- if (pageNumber < 1 || pageNumber > this.totalPages)
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
- const visiblePages = this.viewportManager.getVisiblePages(scrollTop, containerHeight);
915
- console.log('Re-rendering visible pages at new scale:', visiblePages);
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 visiblePages) {
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(highlight.labelStyle);
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 (highlight.labelStyle?.borderColor !== undefined ||
1313
- highlight.labelStyle?.borderWidth !== undefined) {
1314
- const borderColor = highlight.labelStyle?.borderColor ?? 'currentColor';
1315
- const borderWidth = highlight.labelStyle?.borderWidth ?? '1px';
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, highlight.labelStyle);
1319
- applyLabelStyle(labelEl, highlight.labelStyle);
1369
+ applyLabelOutlineStyle(labelEl, effectiveLabelStyle);
1370
+ applyLabelStyle(labelEl, effectiveLabelStyle);
1320
1371
  labelEl.style.opacity = String(effectiveOpacity);
1321
- appendLabelIcon(labelEl, highlight.beforeIcon, highlight.labelStyle);
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 labelStyle = highlight?.labelStyle;
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(labelStyle);
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, labelStyle);
1407
- applyLabelOutlineStyle(labelEl, labelStyle);
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 all pages
1554
+ * Build spatial indices for displayed pages only
1501
1555
  */
1502
1556
  buildAllSpatialIndices() {
1503
- for (let pageNumber = 1; pageNumber <= this.totalPages; pageNumber++) {
1557
+ for (const pageNumber of this.getDisplayedPageList()) {
1504
1558
  this.buildSpatialIndexForPage(pageNumber);
1505
1559
  }
1506
1560
  }