@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 +42 -29
- package/dist/PDFHighlightViewer.d.ts +8 -5
- package/dist/PDFHighlightViewer.d.ts.map +1 -1
- package/dist/PDFHighlightViewer.js +147 -62
- 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/unified-layer-builder.d.ts +0 -1
- package/dist/core/unified-layer-builder.d.ts.map +1 -1
- package/dist/core/unified-layer-builder.js +9 -32
- package/dist/core/unified-layer-builder.js.map +1 -1
- package/dist/types.d.ts +9 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/highlight-style.d.ts +7 -0
- package/dist/utils/highlight-style.d.ts.map +1 -0
- package/dist/utils/highlight-style.js +31 -0
- package/dist/utils/highlight-style.js.map +1 -0
- package/dist/utils/label-style.d.ts +7 -1
- package/dist/utils/label-style.d.ts.map +1 -1
- package/dist/utils/label-style.js +64 -0
- package/dist/utils/label-style.js.map +1 -1
- package/package.json +1 -1
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?:
|
|
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
|
-
###
|
|
162
|
+
### ViewerOptions
|
|
150
163
|
|
|
151
164
|
```ts
|
|
152
|
-
interface
|
|
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?:
|
|
184
|
+
interactionMode?: InteractionMode;
|
|
167
185
|
|
|
168
|
-
//
|
|
169
|
-
|
|
186
|
+
// Performance mode (smaller frame budget)
|
|
187
|
+
performanceMode?: boolean;
|
|
170
188
|
|
|
171
|
-
//
|
|
172
|
-
|
|
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?:
|
|
197
|
+
bboxOrigin?: BBoxOrigin;
|
|
182
198
|
|
|
183
199
|
// Page dimensions for which bbox coordinates were calculated
|
|
184
|
-
bboxSourceDimensions?:
|
|
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?:
|
|
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
|
-
|
|
230
|
+
Update highlight style by id (patch merge). Optionally pass `labelStylePatch` to update label styles as well.
|
|
217
231
|
|
|
218
|
-
|
|
232
|
+
#### `goToHighlight(termId: string, occurrenceIndex?: number): void`
|
|
219
233
|
|
|
220
|
-
|
|
234
|
+
Navigate to a specific highlight occurrence. `occurrenceIndex` defaults to `0`.
|
|
221
235
|
|
|
222
|
-
|
|
236
|
+
#### `nextHighlight(): void` / `previousHighlight(): void`
|
|
223
237
|
|
|
224
|
-
|
|
225
|
-
- with `termId` → only within that highlight’s occurrences
|
|
238
|
+
Navigate across all highlight occurrences in document order.
|
|
226
239
|
|
|
227
|
-
#### `
|
|
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(
|
|
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.
|
|
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.
|
|
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:
|
|
154
|
-
removeEventListener(event: string, callback:
|
|
155
|
-
emit(event: string, data?:
|
|
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":"AAAA,OAAO,EAAE,kBAAkB,IAAI,mBAAmB,EAAE,MAAM,OAAO,CAAC;AAClE,OAAO,EACL,aAAa,EAKb,SAAS,EACT,qBAAqB,EACrB,kBAAkB,EAClB,kBAAkB,EAClB,qBAAqB,EACrB,eAAe,EACf,kBAAkB,EAElB,cAAc,EACd,SAAS,EAET,gBAAgB,EACjB,MAAM,SAAS,CAAC;
|
|
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 {
|
|
9
|
-
import {
|
|
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
|
|
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
|
-
|
|
1206
|
-
|
|
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
|
-
|
|
1215
|
+
highlightVisual.style.opacity = effectiveOpacity.toString();
|
|
1210
1216
|
highlightDiv.dataset.originalOpacity = effectiveOpacity.toString();
|
|
1211
|
-
const hoverOpacity =
|
|
1212
|
-
|
|
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) =>
|
|
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) =>
|
|
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
|
-
|
|
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
|
|
1235
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
const
|
|
1266
|
-
|
|
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
|
-
|
|
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
|
|
1331
|
-
const
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
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 =
|
|
1339
|
-
? style
|
|
1413
|
+
const baseOpacity = style
|
|
1414
|
+
? getHighlightBaseOpacity(style)
|
|
1340
1415
|
: parseFloat(el.dataset.originalOpacity ?? '0.3');
|
|
1341
|
-
const hoverOpacity =
|
|
1342
|
-
? style.hoverOpacity
|
|
1343
|
-
: Math.min(0.6, baseOpacity + 0.2);
|
|
1416
|
+
const hoverOpacity = getHighlightHoverOpacity(style, baseOpacity);
|
|
1344
1417
|
if (hoveredIds.includes(termId)) {
|
|
1345
|
-
|
|
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
|
-
|
|
1424
|
+
visual.style.opacity = '0.1';
|
|
1425
|
+
if (labelEl) {
|
|
1426
|
+
labelEl.style.opacity = '0.1';
|
|
1427
|
+
}
|
|
1349
1428
|
}
|
|
1350
1429
|
else {
|
|
1351
|
-
|
|
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');
|