@epam/pdf-highlighter-kit 0.0.6 → 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 +62 -27
- package/dist/PDFHighlightViewer.d.ts +8 -5
- package/dist/PDFHighlightViewer.d.ts.map +1 -1
- package/dist/PDFHighlightViewer.js +178 -76
- 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 +16 -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
|
@@ -91,10 +91,20 @@ export interface BBox {
|
|
|
91
91
|
page: number;
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
export interface BBoxDimensions {
|
|
95
|
+
width: number;
|
|
96
|
+
height: number;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export type BBoxOrigin = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
|
|
100
|
+
|
|
101
|
+
export type InteractionMode = 'select' | 'highlight' | 'hybrid';
|
|
102
|
+
|
|
94
103
|
export interface HighlightStyle {
|
|
95
104
|
backgroundColor: string;
|
|
96
105
|
borderColor?: string;
|
|
97
106
|
borderWidth?: string;
|
|
107
|
+
outline?: string;
|
|
98
108
|
opacity?: number;
|
|
99
109
|
hoverOpacity?: number;
|
|
100
110
|
pulseAnimation?: boolean;
|
|
@@ -102,20 +112,30 @@ export interface HighlightStyle {
|
|
|
102
112
|
|
|
103
113
|
export interface HighlightLabelStyle {
|
|
104
114
|
fontSize?: string | number;
|
|
115
|
+
opacity?: number;
|
|
105
116
|
color?: string;
|
|
106
117
|
backgroundColor?: string;
|
|
107
118
|
padding?: string;
|
|
108
|
-
borderRadius?: string;
|
|
119
|
+
borderRadius?: string; // e.g. '2px' or shorthand '2px 2px 0 0'
|
|
109
120
|
fontFamily?: string;
|
|
110
121
|
fontWeight?: string | number;
|
|
111
122
|
border?: string;
|
|
123
|
+
borderColor?: string;
|
|
124
|
+
borderWidth?: string;
|
|
125
|
+
outline?: string;
|
|
112
126
|
whiteSpace?: string;
|
|
113
127
|
iconSize?: string | number; // size for beforeIcon (e.g. 14 or '14px')
|
|
128
|
+
iconColor?: string;
|
|
129
|
+
offsetLeft?: number;
|
|
130
|
+
offsetTop?: number;
|
|
131
|
+
outlineRight?: string;
|
|
114
132
|
}
|
|
115
133
|
|
|
116
134
|
export interface InputHighlightData {
|
|
117
135
|
id: string;
|
|
118
136
|
bboxes: BBox[];
|
|
137
|
+
bboxOrigin?: BBoxOrigin;
|
|
138
|
+
bboxSourceDimensions?: BBoxDimensions;
|
|
119
139
|
style?: HighlightStyle;
|
|
120
140
|
label?: string;
|
|
121
141
|
beforeIcon?: string; // inline SVG string (trusted content only, e.g. Tabler icons)
|
|
@@ -125,12 +145,29 @@ export interface InputHighlightData {
|
|
|
125
145
|
}
|
|
126
146
|
```
|
|
127
147
|
|
|
148
|
+
If `bboxSourceDimensions` is provided, each bbox coordinate is recalculated against the actual page size:
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
scaledX = (x / bboxSourceDimensions.width) * actualPageWidth;
|
|
152
|
+
scaledY = (y / bboxSourceDimensions.height) * actualPageHeight;
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Priority (highest to lowest):
|
|
156
|
+
|
|
157
|
+
- `highlight.bboxOrigin` / `highlight.bboxSourceDimensions`
|
|
158
|
+
- global viewer options (`bboxOrigin`, `bboxSourceDimensions`)
|
|
159
|
+
|
|
128
160
|
## Configuration Options
|
|
129
161
|
|
|
130
|
-
###
|
|
162
|
+
### ViewerOptions
|
|
131
163
|
|
|
132
164
|
```ts
|
|
133
|
-
interface
|
|
165
|
+
export interface HighlightsConfig {
|
|
166
|
+
enableMultilineHover?: boolean;
|
|
167
|
+
defaultStyle?: HighlightStyle;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export interface ViewerOptions {
|
|
134
171
|
// Enable text selection functionality
|
|
135
172
|
enableTextSelection?: boolean;
|
|
136
173
|
|
|
@@ -144,22 +181,23 @@ interface ViewerConfig {
|
|
|
144
181
|
maxCachedPages?: number;
|
|
145
182
|
|
|
146
183
|
// Interaction mode: 'select' | 'highlight' | 'hybrid'
|
|
147
|
-
interactionMode?:
|
|
184
|
+
interactionMode?: InteractionMode;
|
|
148
185
|
|
|
149
|
-
//
|
|
150
|
-
|
|
186
|
+
// Performance mode (smaller frame budget)
|
|
187
|
+
performanceMode?: boolean;
|
|
151
188
|
|
|
152
|
-
//
|
|
153
|
-
|
|
189
|
+
// Enable accessibility helpers
|
|
190
|
+
accessibility?: boolean;
|
|
154
191
|
|
|
155
192
|
// Highlight UI config (style is per highlight)
|
|
156
|
-
highlightsConfig?:
|
|
157
|
-
enableMultilineHover?: boolean;
|
|
158
|
-
};
|
|
193
|
+
highlightsConfig?: HighlightsConfig;
|
|
159
194
|
|
|
160
195
|
// Coordinate origin for incoming bbox values
|
|
161
196
|
// Default: 'bottom-right'
|
|
162
|
-
bboxOrigin?:
|
|
197
|
+
bboxOrigin?: BBoxOrigin;
|
|
198
|
+
|
|
199
|
+
// Page dimensions for which bbox coordinates were calculated
|
|
200
|
+
bboxSourceDimensions?: BBoxDimensions;
|
|
163
201
|
}
|
|
164
202
|
```
|
|
165
203
|
|
|
@@ -167,11 +205,11 @@ interface ViewerConfig {
|
|
|
167
205
|
|
|
168
206
|
### Main Methods
|
|
169
207
|
|
|
170
|
-
#### `init(container: HTMLElement, config?:
|
|
208
|
+
#### `init(container: HTMLElement, config?: ViewerOptions): Promise<void>`
|
|
171
209
|
|
|
172
210
|
Initialize the viewer with a container element and optional configuration.
|
|
173
211
|
|
|
174
|
-
#### `loadPDF(source: string | ArrayBuffer): Promise<void>`
|
|
212
|
+
#### `loadPDF(source: string | ArrayBuffer | Blob): Promise<void>`
|
|
175
213
|
|
|
176
214
|
Load a PDF document from URL or ArrayBuffer.
|
|
177
215
|
|
|
@@ -187,22 +225,19 @@ Add a single highlight (incremental update).
|
|
|
187
225
|
|
|
188
226
|
Remove highlight by its `id`.
|
|
189
227
|
|
|
190
|
-
#### `updateHighlightStyle(termId: string, stylePatch: Partial<HighlightStyle>): void`
|
|
191
|
-
|
|
192
|
-
Update highlight style by id (patch merge).
|
|
228
|
+
#### `updateHighlightStyle(termId: string, stylePatch: Partial<HighlightStyle>, labelStylePatch?: Partial<HighlightLabelStyle>): void`
|
|
193
229
|
|
|
194
|
-
|
|
230
|
+
Update highlight style by id (patch merge). Optionally pass `labelStylePatch` to update label styles as well.
|
|
195
231
|
|
|
196
|
-
|
|
232
|
+
#### `goToHighlight(termId: string, occurrenceIndex?: number): void`
|
|
197
233
|
|
|
198
|
-
|
|
234
|
+
Navigate to a specific highlight occurrence. `occurrenceIndex` defaults to `0`.
|
|
199
235
|
|
|
200
|
-
|
|
236
|
+
#### `nextHighlight(): void` / `previousHighlight(): void`
|
|
201
237
|
|
|
202
|
-
|
|
203
|
-
- with `termId` → only within that highlight’s occurrences
|
|
238
|
+
Navigate across all highlight occurrences in document order.
|
|
204
239
|
|
|
205
|
-
#### `
|
|
240
|
+
#### `setPage(pageNumber: number): void`
|
|
206
241
|
|
|
207
242
|
Navigate to a specific page (1-based).
|
|
208
243
|
|
|
@@ -210,7 +245,7 @@ Navigate to a specific page (1-based).
|
|
|
210
245
|
|
|
211
246
|
Zoom in or out of the PDF.
|
|
212
247
|
|
|
213
|
-
#### `setZoom(
|
|
248
|
+
#### `setZoom(value: ZoomMode | number): void`
|
|
214
249
|
|
|
215
250
|
Set a specific zoom level (e.g., 1.0 for 100%, 1.5 for 150%).
|
|
216
251
|
|
|
@@ -240,7 +275,7 @@ viewer.addEventListener('pdfLoaded', (e) => {
|
|
|
240
275
|
});
|
|
241
276
|
|
|
242
277
|
viewer.addEventListener('pageChanged', (e) => {
|
|
243
|
-
console.log('Current page:', e.
|
|
278
|
+
console.log('Current page:', e.currentPage);
|
|
244
279
|
});
|
|
245
280
|
|
|
246
281
|
viewer.addEventListener('zoomChanged', (e) => {
|
|
@@ -256,7 +291,7 @@ viewer.addEventListener('renderError', (e) => {
|
|
|
256
291
|
});
|
|
257
292
|
|
|
258
293
|
viewer.addEventListener('highlightsLoaded', (e) => {
|
|
259
|
-
console.log('Highlights loaded:', e.
|
|
294
|
+
console.log('Highlights loaded:', e.count);
|
|
260
295
|
});
|
|
261
296
|
|
|
262
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,
|
|
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));
|
|
@@ -837,7 +843,7 @@ export class PDFHighlightViewer {
|
|
|
837
843
|
const normalizedHighlights = this.highlightsIndex.highlights.map((highlight) => ({
|
|
838
844
|
...highlight,
|
|
839
845
|
bboxes: highlight.bboxes.map((bbox) => {
|
|
840
|
-
const normalized = this.normalizeBBoxForPage(bbox, bbox.page);
|
|
846
|
+
const normalized = this.normalizeBBoxForPage(bbox, bbox.page, highlight.bboxSourceDimensions, highlight.bboxOrigin);
|
|
841
847
|
return {
|
|
842
848
|
...bbox,
|
|
843
849
|
x1: normalized.x1,
|
|
@@ -925,7 +931,7 @@ export class PDFHighlightViewer {
|
|
|
925
931
|
for (const h of this.highlightsIndex.highlights) {
|
|
926
932
|
for (let i = 0; i < h.bboxes.length; i++) {
|
|
927
933
|
const b = h.bboxes[i];
|
|
928
|
-
const normalized = this.normalizeBBoxForPage(b, b.page);
|
|
934
|
+
const normalized = this.normalizeBBoxForPage(b, b.page, h.bboxSourceDimensions, h.bboxOrigin);
|
|
929
935
|
list.push({
|
|
930
936
|
termId: h.id,
|
|
931
937
|
pageNumber: b.page,
|
|
@@ -946,7 +952,7 @@ export class PDFHighlightViewer {
|
|
|
946
952
|
if (!bbox)
|
|
947
953
|
return;
|
|
948
954
|
const page = bbox.page;
|
|
949
|
-
const normalizedBBox = this.normalizeBBoxForPage(bbox, page);
|
|
955
|
+
const normalizedBBox = this.normalizeBBoxForPage(bbox, page, highlight.bboxSourceDimensions, highlight.bboxOrigin);
|
|
950
956
|
this.highlightSelectedTerm(termId);
|
|
951
957
|
this.setPage(page);
|
|
952
958
|
void this.renderPage(page)
|
|
@@ -1174,14 +1180,12 @@ 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)
|
|
1183
1187
|
continue;
|
|
1184
|
-
const normalizedBBox = this.normalizeBBoxForPage(bbox, pageNumber);
|
|
1188
|
+
const normalizedBBox = this.normalizeBBoxForPage(bbox, pageNumber, highlight.bboxSourceDimensions, highlight.bboxOrigin);
|
|
1185
1189
|
const highlightDiv = document.createElement('div');
|
|
1186
1190
|
highlightDiv.className = 'highlight';
|
|
1187
1191
|
highlightDiv.setAttribute('data-term-id', highlight.id);
|
|
@@ -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
|
});
|
|
@@ -1358,10 +1440,16 @@ export class PDFHighlightViewer {
|
|
|
1358
1440
|
*/
|
|
1359
1441
|
buildSpatialIndexForPage(pageNumber) {
|
|
1360
1442
|
const refs = this.highlightsIndex.pages[String(pageNumber)] ?? [];
|
|
1361
|
-
const normalizedRefs = refs.map((ref) =>
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1443
|
+
const normalizedRefs = refs.map((ref) => {
|
|
1444
|
+
const highlight = this.highlightsIndex.byId.get(ref.id);
|
|
1445
|
+
return {
|
|
1446
|
+
...ref,
|
|
1447
|
+
bbox: this.normalizeBBoxForPage(highlight?.bboxes[ref.bboxIndex] ?? {
|
|
1448
|
+
...ref.bbox,
|
|
1449
|
+
page: ref.page,
|
|
1450
|
+
}, ref.page, highlight?.bboxSourceDimensions, highlight?.bboxOrigin),
|
|
1451
|
+
};
|
|
1452
|
+
});
|
|
1365
1453
|
this.performanceOptimizer.buildSpatialIndex(normalizedRefs, pageNumber);
|
|
1366
1454
|
}
|
|
1367
1455
|
toPageCoordinateDimensions(pixelDimensions) {
|
|
@@ -1371,8 +1459,9 @@ export class PDFHighlightViewer {
|
|
|
1371
1459
|
height: pixelDimensions.height / scale,
|
|
1372
1460
|
};
|
|
1373
1461
|
}
|
|
1374
|
-
normalizeBBoxForPage(bbox, pageNumber) {
|
|
1375
|
-
const origin = this.options.bboxOrigin ?? 'bottom-right';
|
|
1462
|
+
normalizeBBoxForPage(bbox, pageNumber, bboxSourceDimensions, bboxOrigin) {
|
|
1463
|
+
const origin = bboxOrigin ?? this.options.bboxOrigin ?? 'bottom-right';
|
|
1464
|
+
const computedSourceDimensions = bboxSourceDimensions ?? this.options.bboxSourceDimensions;
|
|
1376
1465
|
const pixelDimensions = this.pageDimensions.get(pageNumber);
|
|
1377
1466
|
if (!pixelDimensions) {
|
|
1378
1467
|
throw new Error(`Page dimensions for page ${pageNumber} are not available`);
|
|
@@ -1382,13 +1471,23 @@ export class PDFHighlightViewer {
|
|
|
1382
1471
|
let x2 = bbox.x2;
|
|
1383
1472
|
let y1 = bbox.y1;
|
|
1384
1473
|
let y2 = bbox.y2;
|
|
1474
|
+
if (computedSourceDimensions &&
|
|
1475
|
+
computedSourceDimensions.width &&
|
|
1476
|
+
computedSourceDimensions.height) {
|
|
1477
|
+
const xScale = pageWidth / computedSourceDimensions.width;
|
|
1478
|
+
const yScale = pageHeight / computedSourceDimensions.height;
|
|
1479
|
+
x1 *= xScale;
|
|
1480
|
+
x2 *= xScale;
|
|
1481
|
+
y1 *= yScale;
|
|
1482
|
+
y2 *= yScale;
|
|
1483
|
+
}
|
|
1385
1484
|
if (origin.endsWith('right')) {
|
|
1386
|
-
x1 = pageWidth -
|
|
1387
|
-
x2 = pageWidth -
|
|
1485
|
+
x1 = pageWidth - x1;
|
|
1486
|
+
x2 = pageWidth - x2;
|
|
1388
1487
|
}
|
|
1389
1488
|
if (origin.startsWith('bottom')) {
|
|
1390
|
-
y1 = pageHeight -
|
|
1391
|
-
y2 = pageHeight -
|
|
1489
|
+
y1 = pageHeight - y1;
|
|
1490
|
+
y2 = pageHeight - y2;
|
|
1392
1491
|
}
|
|
1393
1492
|
return {
|
|
1394
1493
|
x1: Math.min(x1, x2),
|
|
@@ -1549,6 +1648,9 @@ export class PDFHighlightViewer {
|
|
|
1549
1648
|
// textLayer.style.opacity = '0.1'; // Uncomment for debugging text boundaries
|
|
1550
1649
|
// Process text items with proper PDF coordinate transformation
|
|
1551
1650
|
textContent.items.forEach((item) => {
|
|
1651
|
+
if (!('str' in item) || !('transform' in item) || !('fontName' in item)) {
|
|
1652
|
+
return;
|
|
1653
|
+
}
|
|
1552
1654
|
if (!item.str || !item.str.trim())
|
|
1553
1655
|
return; // Skip empty text
|
|
1554
1656
|
const textSpan = document.createElement('span');
|