@epam/pdf-highlighter-kit 0.0.7 → 0.0.8

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
@@ -96,10 +96,15 @@ export interface BBoxDimensions {
96
96
  height: number;
97
97
  }
98
98
 
99
+ export type BBoxOrigin = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
100
+
101
+ export type InteractionMode = 'select' | 'highlight' | 'hybrid';
102
+
99
103
  export interface HighlightStyle {
100
104
  backgroundColor: string;
101
105
  borderColor?: string;
102
106
  borderWidth?: string;
107
+ outline?: string;
103
108
  opacity?: number;
104
109
  hoverOpacity?: number;
105
110
  pulseAnimation?: boolean;
@@ -107,21 +112,29 @@ export interface HighlightStyle {
107
112
 
108
113
  export interface HighlightLabelStyle {
109
114
  fontSize?: string | number;
115
+ opacity?: number;
110
116
  color?: string;
111
117
  backgroundColor?: string;
112
118
  padding?: string;
113
- borderRadius?: string;
119
+ borderRadius?: string; // e.g. '2px' or shorthand '2px 2px 0 0'
114
120
  fontFamily?: string;
115
121
  fontWeight?: string | number;
116
122
  border?: string;
123
+ borderColor?: string;
124
+ borderWidth?: string;
125
+ outline?: string;
117
126
  whiteSpace?: string;
118
127
  iconSize?: string | number; // size for beforeIcon (e.g. 14 or '14px')
128
+ iconColor?: string;
129
+ offsetLeft?: number;
130
+ offsetTop?: number;
131
+ outlineRight?: string;
119
132
  }
120
133
 
121
134
  export interface InputHighlightData {
122
135
  id: string;
123
136
  bboxes: BBox[];
124
- bboxOrigin?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
137
+ bboxOrigin?: BBoxOrigin;
125
138
  bboxSourceDimensions?: BBoxDimensions;
126
139
  style?: HighlightStyle;
127
140
  label?: string;
@@ -146,10 +159,15 @@ Priority (highest to lowest):
146
159
 
147
160
  ## Configuration Options
148
161
 
149
- ### ViewerConfig
162
+ ### ViewerOptions
150
163
 
151
164
  ```ts
152
- interface ViewerConfig {
165
+ export interface HighlightsConfig {
166
+ enableMultilineHover?: boolean;
167
+ defaultStyle?: HighlightStyle;
168
+ }
169
+
170
+ export interface ViewerOptions {
153
171
  // Enable text selection functionality
154
172
  enableTextSelection?: boolean;
155
173
 
@@ -163,25 +181,23 @@ interface ViewerConfig {
163
181
  maxCachedPages?: number;
164
182
 
165
183
  // Interaction mode: 'select' | 'highlight' | 'hybrid'
166
- interactionMode?: 'select' | 'highlight' | 'hybrid';
184
+ interactionMode?: InteractionMode;
167
185
 
168
- // Custom styles configuration (viewer/selection CSS). Highlight styles are per-highlight.
169
- customStyles?: StyleConfig;
186
+ // Performance mode (smaller frame budget)
187
+ performanceMode?: boolean;
170
188
 
171
- // PDF.js worker source URL
172
- workerSrc?: string;
189
+ // Enable accessibility helpers
190
+ accessibility?: boolean;
173
191
 
174
192
  // Highlight UI config (style is per highlight)
175
- highlightsConfig?: {
176
- enableMultilineHover?: boolean;
177
- };
193
+ highlightsConfig?: HighlightsConfig;
178
194
 
179
195
  // Coordinate origin for incoming bbox values
180
196
  // Default: 'bottom-right'
181
- bboxOrigin?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
197
+ bboxOrigin?: BBoxOrigin;
182
198
 
183
199
  // Page dimensions for which bbox coordinates were calculated
184
- bboxSourceDimensions?: { width: number; height: number };
200
+ bboxSourceDimensions?: BBoxDimensions;
185
201
  }
186
202
  ```
187
203
 
@@ -189,11 +205,11 @@ interface ViewerConfig {
189
205
 
190
206
  ### Main Methods
191
207
 
192
- #### `init(container: HTMLElement, config?: ViewerConfig): Promise<void>`
208
+ #### `init(container: HTMLElement, config?: ViewerOptions): Promise<void>`
193
209
 
194
210
  Initialize the viewer with a container element and optional configuration.
195
211
 
196
- #### `loadPDF(source: string | ArrayBuffer): Promise<void>`
212
+ #### `loadPDF(source: string | ArrayBuffer | Blob): Promise<void>`
197
213
 
198
214
  Load a PDF document from URL or ArrayBuffer.
199
215
 
@@ -209,22 +225,19 @@ Add a single highlight (incremental update).
209
225
 
210
226
  Remove highlight by its `id`.
211
227
 
212
- #### `updateHighlightStyle(termId: string, stylePatch: Partial<HighlightStyle>): void`
213
-
214
- Update highlight style by id (patch merge).
228
+ #### `updateHighlightStyle(termId: string, stylePatch: Partial<HighlightStyle>, labelStylePatch?: Partial<HighlightLabelStyle>): void`
215
229
 
216
- #### `goToHighlight(termId: string, bboxIndex?: number): void`
230
+ Update highlight style by id (patch merge). Optionally pass `labelStylePatch` to update label styles as well.
217
231
 
218
- Navigate to a specific highlight occurrence. `bboxIndex` defaults to `0`.
232
+ #### `goToHighlight(termId: string, occurrenceIndex?: number): void`
219
233
 
220
- #### `nextHighlight(termId?: string): void` / `previousHighlight(termId?: string): void`
234
+ Navigate to a specific highlight occurrence. `occurrenceIndex` defaults to `0`.
221
235
 
222
- Navigate across highlight occurrences:
236
+ #### `nextHighlight(): void` / `previousHighlight(): void`
223
237
 
224
- - without `termId` → across all highlights in document order
225
- - with `termId` → only within that highlight’s occurrences
238
+ Navigate across all highlight occurrences in document order.
226
239
 
227
- #### `goToPage(pageNumber: number): void`
240
+ #### `setPage(pageNumber: number): void`
228
241
 
229
242
  Navigate to a specific page (1-based).
230
243
 
@@ -232,7 +245,7 @@ Navigate to a specific page (1-based).
232
245
 
233
246
  Zoom in or out of the PDF.
234
247
 
235
- #### `setZoom(scale: number): void`
248
+ #### `setZoom(value: ZoomMode | number): void`
236
249
 
237
250
  Set a specific zoom level (e.g., 1.0 for 100%, 1.5 for 150%).
238
251
 
@@ -262,7 +275,7 @@ viewer.addEventListener('pdfLoaded', (e) => {
262
275
  });
263
276
 
264
277
  viewer.addEventListener('pageChanged', (e) => {
265
- console.log('Current page:', e.pageNumber);
278
+ console.log('Current page:', e.currentPage);
266
279
  });
267
280
 
268
281
  viewer.addEventListener('zoomChanged', (e) => {
@@ -278,7 +291,7 @@ viewer.addEventListener('renderError', (e) => {
278
291
  });
279
292
 
280
293
  viewer.addEventListener('highlightsLoaded', (e) => {
281
- console.log('Highlights loaded:', e.data?.length ?? 0);
294
+ console.log('Highlights loaded:', e.count);
282
295
  });
283
296
 
284
297
  viewer.addEventListener('highlightHover', (e) => {
@@ -1,5 +1,6 @@
1
1
  import { PDFHighlightViewer as IPDFHighlightViewer } from './api';
2
- import { ViewerOptions, TextRange, SelectionWithMetadata, PerformanceMetrics, HighlightAnalytics, AccessibilityFeatures, InteractionMode, InputHighlightData, HighlightStyle, ZoomValue, ThumbnailOptions } from './types';
2
+ import { ViewerOptions, TextRange, SelectionWithMetadata, PerformanceMetrics, HighlightAnalytics, AccessibilityFeatures, InteractionMode, InputHighlightData, HighlightStyle, HighlightLabelStyle, ZoomValue, ThumbnailOptions } from './types';
3
+ type EventCallback = (data?: unknown) => void;
3
4
  export declare class PDFHighlightViewer implements IPDFHighlightViewer {
4
5
  private pdfEngine;
5
6
  private viewportManager;
@@ -105,7 +106,7 @@ export declare class PDFHighlightViewer implements IPDFHighlightViewer {
105
106
  loadHighlights(data: InputHighlightData[]): void;
106
107
  addHighlight(highlight: InputHighlightData): void;
107
108
  removeHighlight(termId: string): void;
108
- updateHighlightStyle(termId: string, stylePatch: Partial<HighlightStyle>): void;
109
+ updateHighlightStyle(termId: string, stylePatch: Partial<HighlightStyle>, labelStylePatch?: Partial<HighlightLabelStyle>): void;
109
110
  /**
110
111
  * Update unified layer for a specific page
111
112
  */
@@ -150,9 +151,9 @@ export declare class PDFHighlightViewer implements IPDFHighlightViewer {
150
151
  enableProfiling(): void;
151
152
  disableProfiling(): void;
152
153
  accessibility: AccessibilityFeatures;
153
- addEventListener(event: string, callback: (...args: any[]) => void): void;
154
- removeEventListener(event: string, callback: (...args: any[]) => void): void;
155
- emit(event: string, data?: any): void;
154
+ addEventListener(event: string, callback: EventCallback): void;
155
+ removeEventListener(event: string, callback: EventCallback): void;
156
+ emit(event: string, data?: unknown): void;
156
157
  exportAsImage(format?: 'png' | 'jpeg', quality?: number): Promise<Blob>;
157
158
  getViewport(): {
158
159
  pageNumber: number;
@@ -173,6 +174,8 @@ export declare class PDFHighlightViewer implements IPDFHighlightViewer {
173
174
  private addHighlightsToPage;
174
175
  private getHighlightById;
175
176
  private getHighlightStyle;
177
+ private getLabelOffsets;
178
+ private getLabelsForTerm;
176
179
  private getHighlightElements;
177
180
  /**
178
181
  * Update highlights colors for specified page
@@ -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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"}
@@ -5,8 +5,8 @@ import { UnifiedLayerBuilder } from './core/unified-layer-builder';
5
5
  import { UnifiedInteractionHandler } from './core/interaction-handler';
6
6
  import { PerformanceOptimizer } from './core/performance-optimizer';
7
7
  import { buildHighlightsIndex } from './utils/highlight-adapter';
8
- import { applyLabelStyle, applyIconStyle, normalizeSize } from './utils/label-style';
9
- import { sanitizeIconHtml } from './utils/sanitize-icon-html';
8
+ import { applyHighlightVisualStyle, getHighlightBaseOpacity, getHighlightHoverOpacity, resolveHighlightStyle, } from './utils/highlight-style';
9
+ import { appendLabelIcon, applyBaseOutlineStyle, applyLabelOutlineStyle, applyLabelStyle, } from './utils/label-style';
10
10
  const CONTAINER_PADDING = 40;
11
11
  const ZOOM_STEP = 1.2;
12
12
  export class PDFHighlightViewer {
@@ -797,7 +797,7 @@ export class PDFHighlightViewer {
797
797
  }
798
798
  this.emit('highlightRemoved', { termId, pages: Array.from(affectedPages) });
799
799
  }
800
- updateHighlightStyle(termId, stylePatch) {
800
+ updateHighlightStyle(termId, stylePatch, labelStylePatch) {
801
801
  const prev = this.highlightsIndex.byId.get(termId);
802
802
  if (!prev)
803
803
  return;
@@ -807,6 +807,12 @@ export class PDFHighlightViewer {
807
807
  ...(prev.style ?? {}),
808
808
  ...stylePatch,
809
809
  },
810
+ labelStyle: labelStylePatch
811
+ ? {
812
+ ...(prev.labelStyle ?? {}),
813
+ ...labelStylePatch,
814
+ }
815
+ : prev.labelStyle,
810
816
  };
811
817
  const affectedPages = new Set();
812
818
  prev.bboxes.forEach((b) => affectedPages.add(b.page));
@@ -1174,9 +1180,7 @@ export class PDFHighlightViewer {
1174
1180
  highlightLayer.style.pointerEvents = 'none';
1175
1181
  for (const highlight of this.highlightsIndex.highlights) {
1176
1182
  const style = highlight.style;
1177
- const backgroundColor = style?.backgroundColor ?? '#666666';
1178
- const borderColor = style?.borderColor ?? backgroundColor;
1179
- const borderWidth = style?.borderWidth ?? '1px';
1183
+ const resolvedStyle = resolveHighlightStyle(style);
1180
1184
  for (let bboxIndex = 0; bboxIndex < highlight.bboxes.length; bboxIndex++) {
1181
1185
  const bbox = highlight.bboxes[bboxIndex];
1182
1186
  if (bbox.page !== pageNumber)
@@ -1196,62 +1200,108 @@ export class PDFHighlightViewer {
1196
1200
  highlightDiv.style.top = `${top}px`;
1197
1201
  highlightDiv.style.width = `${width}px`;
1198
1202
  highlightDiv.style.height = `${height}px`;
1199
- highlightDiv.style.backgroundColor = backgroundColor;
1200
- highlightDiv.style.border = `${borderWidth} solid ${borderColor}`;
1201
1203
  highlightDiv.style.pointerEvents = 'auto';
1202
1204
  highlightDiv.style.cursor = 'pointer';
1203
1205
  highlightDiv.style.boxSizing = 'border-box';
1204
1206
  highlightDiv.style.userSelect = 'none';
1205
- highlightDiv.style.mixBlendMode = 'multiply';
1206
- const baseOpacity = typeof style?.opacity === 'number' ? style.opacity : 0.3;
1207
+ const highlightVisual = document.createElement('span');
1208
+ highlightVisual.className = 'highlight-visual';
1209
+ applyHighlightVisualStyle(highlightVisual, resolvedStyle);
1210
+ applyBaseOutlineStyle(highlightVisual, style);
1211
+ highlightDiv.appendChild(highlightVisual);
1212
+ const baseOpacity = getHighlightBaseOpacity(style);
1207
1213
  const overlappingCount = this.countOverlappingHighlights(highlightLayer, normalizedBBox, scale);
1208
1214
  const effectiveOpacity = Math.max(0.05, baseOpacity / Math.max(1, overlappingCount * 0.7));
1209
- highlightDiv.style.opacity = effectiveOpacity.toString();
1215
+ highlightVisual.style.opacity = effectiveOpacity.toString();
1210
1216
  highlightDiv.dataset.originalOpacity = effectiveOpacity.toString();
1211
- const hoverOpacity = typeof style?.hoverOpacity === 'number'
1212
- ? style.hoverOpacity
1213
- : Math.min(0.6, effectiveOpacity + 0.2);
1214
- const labelColor = style?.borderColor ?? style?.backgroundColor ?? '#666666';
1217
+ const hoverOpacity = getHighlightHoverOpacity(style, effectiveOpacity);
1218
+ const termLabelsSelector = `.highlight-label[data-term-id="${highlight.id}"]`;
1215
1219
  highlightDiv.addEventListener('mouseenter', () => {
1216
1220
  if (this.options.highlightsConfig?.enableMultilineHover) {
1217
1221
  const same = highlightLayer.querySelectorAll(`.highlight[data-term-id="${highlight.id}"]`);
1218
- same.forEach((el) => (el.style.opacity = String(hoverOpacity)));
1222
+ same.forEach((el) => {
1223
+ const htmlEl = el;
1224
+ const visual = htmlEl.querySelector('.highlight-visual');
1225
+ if (visual)
1226
+ visual.style.opacity = String(hoverOpacity);
1227
+ });
1228
+ this.getLabelsForTerm(highlightLayer, highlight.id).forEach((sameLabel) => {
1229
+ sameLabel.style.opacity = String(hoverOpacity);
1230
+ });
1219
1231
  const other = highlightLayer.querySelectorAll(`.highlight[data-term-id]:not([data-term-id="${highlight.id}"])`);
1220
- other.forEach((el) => (el.style.opacity = '0.1'));
1232
+ other.forEach((el) => {
1233
+ const htmlEl = el;
1234
+ const visual = htmlEl.querySelector('.highlight-visual');
1235
+ if (visual)
1236
+ visual.style.opacity = '0.1';
1237
+ });
1238
+ highlightLayer
1239
+ .querySelectorAll(`.highlight-label:not([data-term-id="${highlight.id}"])`)
1240
+ .forEach((otherLabel) => {
1241
+ otherLabel.style.opacity = '0.1';
1242
+ });
1221
1243
  }
1222
1244
  else {
1223
- highlightDiv.style.opacity = String(hoverOpacity);
1224
- }
1225
- const labelEl = highlightDiv.querySelector('.highlight-label');
1226
- if (labelEl) {
1227
- labelEl.style.borderColor = borderColor;
1245
+ highlightVisual.style.opacity = String(hoverOpacity);
1228
1246
  }
1247
+ highlightLayer.querySelectorAll(termLabelsSelector).forEach((labelEl) => {
1248
+ labelEl.style.opacity = String(hoverOpacity);
1249
+ });
1229
1250
  });
1230
1251
  highlightDiv.addEventListener('mouseleave', () => {
1231
1252
  if (this.options.highlightsConfig?.enableMultilineHover) {
1232
1253
  const all = highlightLayer.querySelectorAll('.highlight[data-term-id]');
1254
+ const baseOpacityByTermId = new Map();
1233
1255
  all.forEach((el) => {
1234
- const original = el.dataset.originalOpacity ?? '0.3';
1235
- el.style.opacity = original;
1256
+ const htmlEl = el;
1257
+ const original = htmlEl.dataset.originalOpacity ?? '0.3';
1258
+ const visual = htmlEl.querySelector('.highlight-visual');
1259
+ if (visual)
1260
+ visual.style.opacity = original;
1261
+ const termId = htmlEl.getAttribute('data-term-id');
1262
+ if (termId) {
1263
+ baseOpacityByTermId.set(termId, original);
1264
+ }
1265
+ });
1266
+ highlightLayer
1267
+ .querySelectorAll('.highlight-label')
1268
+ .forEach((allLabel) => {
1269
+ const allTermId = allLabel.getAttribute('data-term-id');
1270
+ if (!allTermId)
1271
+ return;
1272
+ const fallbackBaseOpacity = baseOpacityByTermId.get(allTermId);
1273
+ if (fallbackBaseOpacity !== undefined) {
1274
+ allLabel.style.opacity = fallbackBaseOpacity;
1275
+ return;
1276
+ }
1277
+ const allHighlight = this.getHighlightById(allTermId);
1278
+ allLabel.style.opacity = String(typeof allHighlight?.style?.opacity === 'number'
1279
+ ? allHighlight.style.opacity
1280
+ : 0.3);
1236
1281
  });
1237
1282
  }
1238
1283
  else {
1239
- highlightDiv.style.opacity = highlightDiv.dataset.originalOpacity ?? '0.3';
1240
- }
1241
- const labelEl = highlightDiv.querySelector('.highlight-label');
1242
- if (labelEl) {
1243
- labelEl.style.borderColor = labelColor;
1284
+ highlightVisual.style.opacity = highlightDiv.dataset.originalOpacity ?? '0.3';
1244
1285
  }
1286
+ highlightLayer.querySelectorAll(termLabelsSelector).forEach((labelEl) => {
1287
+ labelEl.style.opacity = String(effectiveOpacity);
1288
+ });
1245
1289
  });
1246
1290
  highlightLayer.appendChild(highlightDiv);
1247
1291
  if (highlight.label || highlight.beforeIcon) {
1248
1292
  const labelEl = document.createElement('span');
1249
1293
  labelEl.className = 'highlight-label';
1250
1294
  labelEl.setAttribute('data-term-id', highlight.id);
1295
+ labelEl.setAttribute('data-bbox-index', String(bboxIndex));
1296
+ labelEl.dataset.baseLeft = String(left);
1297
+ labelEl.dataset.baseTop = String(top);
1298
+ const { left: labelOffsetLeft, top: labelOffsetTop } = this.getLabelOffsets(highlight.labelStyle);
1251
1299
  labelEl.style.position = 'absolute';
1252
- labelEl.style.left = '0';
1300
+ labelEl.style.left = `${left + labelOffsetLeft}px`;
1301
+ labelEl.style.top = `${top + labelOffsetTop}px`;
1302
+ labelEl.style.boxSizing = 'border-box';
1303
+ labelEl.style.zIndex = '3';
1253
1304
  labelEl.style.transform = 'translateX(-100%)';
1254
- labelEl.style.top = '-1px';
1255
1305
  labelEl.style.display = 'flex';
1256
1306
  labelEl.style.alignItems = 'center';
1257
1307
  labelEl.style.justifyContent = 'flex-end';
@@ -1259,28 +1309,20 @@ export class PDFHighlightViewer {
1259
1309
  labelEl.style.pointerEvents = 'auto';
1260
1310
  labelEl.style.cursor = 'pointer';
1261
1311
  labelEl.style.whiteSpace = 'nowrap';
1262
- labelEl.style.border = `1px solid ${labelColor}`;
1263
- applyLabelStyle(labelEl, highlight.labelStyle);
1264
- if (highlight.beforeIcon) {
1265
- const iconWrap = document.createElement('span');
1266
- iconWrap.className = 'highlight-label-icon';
1267
- iconWrap.innerHTML = sanitizeIconHtml(highlight.beforeIcon);
1268
- const svg = iconWrap.querySelector('svg');
1269
- if (svg) {
1270
- svg.removeAttribute('width');
1271
- svg.removeAttribute('height');
1272
- }
1273
- const iconSize = highlight.labelStyle?.iconSize;
1274
- const size = normalizeSize(iconSize);
1275
- iconWrap.style.width = size;
1276
- iconWrap.style.height = size;
1277
- applyIconStyle(iconWrap, highlight.labelStyle);
1278
- labelEl.appendChild(iconWrap);
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';
1316
+ labelEl.style.border = `${borderWidth} solid ${borderColor}`;
1279
1317
  }
1318
+ applyLabelOutlineStyle(labelEl, highlight.labelStyle);
1319
+ applyLabelStyle(labelEl, highlight.labelStyle);
1320
+ labelEl.style.opacity = String(effectiveOpacity);
1321
+ appendLabelIcon(labelEl, highlight.beforeIcon, highlight.labelStyle);
1280
1322
  if (highlight.label) {
1281
1323
  labelEl.appendChild(document.createTextNode(highlight.label));
1282
1324
  }
1283
- highlightDiv.appendChild(labelEl);
1325
+ highlightLayer.appendChild(labelEl);
1284
1326
  }
1285
1327
  }
1286
1328
  }
@@ -1305,6 +1347,15 @@ export class PDFHighlightViewer {
1305
1347
  getHighlightStyle(termId) {
1306
1348
  return this.getHighlightById(termId)?.style;
1307
1349
  }
1350
+ getLabelOffsets(style) {
1351
+ return {
1352
+ left: style?.offsetLeft ?? 0,
1353
+ top: style?.offsetTop ?? -1,
1354
+ };
1355
+ }
1356
+ getLabelsForTerm(highlightLayer, termId) {
1357
+ return Array.from(highlightLayer.querySelectorAll(`.highlight-label[data-term-id="${termId}"]`));
1358
+ }
1308
1359
  getHighlightElements(root, termId) {
1309
1360
  const selector = termId
1310
1361
  ? `.highlight[data-term-id="${termId}"], .highlight-wrapper[data-term-id="${termId}"]`
@@ -1326,29 +1377,60 @@ export class PDFHighlightViewer {
1326
1377
  const termId = el.getAttribute('data-term-id');
1327
1378
  if (!termId)
1328
1379
  return;
1380
+ const highlight = this.getHighlightById(termId);
1329
1381
  const style = this.getHighlightStyle(termId);
1330
- const bg = style?.backgroundColor ?? el.style.backgroundColor ?? '#666666';
1331
- const borderColor = style?.borderColor ?? bg;
1332
- const borderWidth = style?.borderWidth ?? '1px';
1333
- el.style.backgroundColor = bg;
1334
- el.style.border = `${borderWidth} solid ${borderColor}`;
1382
+ const { backgroundColor: bg, borderColor, borderWidth, } = resolveHighlightStyle(style, el.style.backgroundColor || '#666666');
1383
+ const visual = el.querySelector('.highlight-visual') ?? el;
1384
+ visual.style.backgroundColor = bg;
1385
+ visual.style.border = `${borderWidth} solid ${borderColor}`;
1386
+ applyBaseOutlineStyle(visual, style);
1387
+ const highlightBaseOpacity = typeof style?.opacity === 'number'
1388
+ ? style.opacity
1389
+ : parseFloat(el.dataset.originalOpacity ?? '0.3');
1390
+ const bboxIdx = el.getAttribute('data-bbox-index');
1391
+ const isWrapper = el.classList.contains('highlight-wrapper');
1392
+ const labelStyle = highlight?.labelStyle;
1393
+ const labelEl = isWrapper
1394
+ ? el.querySelector('.highlight-label')
1395
+ : bboxIdx !== null
1396
+ ? highlightLayer.querySelector(`.highlight-label[data-term-id="${termId}"][data-bbox-index="${bboxIdx}"]`)
1397
+ : highlightLayer.querySelector(`.highlight-label[data-term-id="${termId}"]`);
1398
+ if (labelEl) {
1399
+ if (!isWrapper) {
1400
+ const baseLabelLeft = parseFloat(labelEl.dataset.baseLeft ?? '0');
1401
+ const baseLabelTop = parseFloat(labelEl.dataset.baseTop ?? '0');
1402
+ const { left: labelOffsetLeft, top: labelOffsetTop } = this.getLabelOffsets(labelStyle);
1403
+ labelEl.style.left = `${baseLabelLeft + labelOffsetLeft}px`;
1404
+ labelEl.style.top = `${baseLabelTop + labelOffsetTop}px`;
1405
+ }
1406
+ applyLabelStyle(labelEl, labelStyle);
1407
+ applyLabelOutlineStyle(labelEl, labelStyle);
1408
+ labelEl.style.opacity = String(highlightBaseOpacity);
1409
+ }
1335
1410
  if (this.options.highlightsConfig?.enableMultilineHover &&
1336
1411
  hoveredIds &&
1337
1412
  Array.isArray(hoveredIds)) {
1338
- const baseOpacity = typeof style?.opacity === 'number'
1339
- ? style.opacity
1413
+ const baseOpacity = style
1414
+ ? getHighlightBaseOpacity(style)
1340
1415
  : parseFloat(el.dataset.originalOpacity ?? '0.3');
1341
- const hoverOpacity = typeof style?.hoverOpacity === 'number'
1342
- ? style.hoverOpacity
1343
- : Math.min(0.6, baseOpacity + 0.2);
1416
+ const hoverOpacity = getHighlightHoverOpacity(style, baseOpacity);
1344
1417
  if (hoveredIds.includes(termId)) {
1345
- el.style.opacity = String(hoverOpacity);
1418
+ visual.style.opacity = String(hoverOpacity);
1419
+ if (labelEl) {
1420
+ labelEl.style.opacity = String(hoverOpacity);
1421
+ }
1346
1422
  }
1347
1423
  else if (hoveredIds.length > 0) {
1348
- el.style.opacity = '0.1';
1424
+ visual.style.opacity = '0.1';
1425
+ if (labelEl) {
1426
+ labelEl.style.opacity = '0.1';
1427
+ }
1349
1428
  }
1350
1429
  else {
1351
- el.style.opacity = String(baseOpacity);
1430
+ visual.style.opacity = String(baseOpacity);
1431
+ if (labelEl) {
1432
+ labelEl.style.opacity = String(baseOpacity);
1433
+ }
1352
1434
  }
1353
1435
  }
1354
1436
  });
@@ -1566,6 +1648,9 @@ export class PDFHighlightViewer {
1566
1648
  // textLayer.style.opacity = '0.1'; // Uncomment for debugging text boundaries
1567
1649
  // Process text items with proper PDF coordinate transformation
1568
1650
  textContent.items.forEach((item) => {
1651
+ if (!('str' in item) || !('transform' in item) || !('fontName' in item)) {
1652
+ return;
1653
+ }
1569
1654
  if (!item.str || !item.str.trim())
1570
1655
  return; // Skip empty text
1571
1656
  const textSpan = document.createElement('span');