@embedpdf/plugin-selection 1.0.5 → 1.0.7

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/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { BasePluginConfig, Action, BasePlugin, PluginRegistry, PluginManifest, PluginPackage } from '@embedpdf/core';
2
- import { PdfPageGeometry, Rect, PdfEngine } from '@embedpdf/models';
1
+ import { BasePluginConfig, EventHook, Action, BasePlugin, PluginRegistry, PluginManifest, PluginPackage } from '@embedpdf/core';
2
+ import { PdfTask, PdfPageGeometry, Rect, PdfEngine } from '@embedpdf/models';
3
3
 
4
4
  interface SelectionPluginConfig extends BasePluginConfig {
5
5
  }
@@ -17,22 +17,48 @@ interface SelectionState {
17
17
  /** current selection or null */
18
18
  rects: Record<number, Rect[]>;
19
19
  selection: SelectionRangeX | null;
20
+ slices: Record<number, {
21
+ start: number;
22
+ count: number;
23
+ }>;
20
24
  active: boolean;
21
25
  selecting: boolean;
22
26
  }
27
+ interface FormattedSelection {
28
+ pageIndex: number;
29
+ rect: Rect;
30
+ segmentRects: Rect[];
31
+ }
23
32
  interface SelectionCapability {
24
- getGeometry(page: number): Promise<PdfPageGeometry>;
25
- getHighlightRects(page: number): Rect[];
26
- getBoundingRect(page: number): Rect | null;
33
+ getGeometry(page: number): PdfTask<PdfPageGeometry>;
34
+ getFormattedSelection(): FormattedSelection[];
35
+ getFormattedSelectionForPage(page: number): FormattedSelection | null;
36
+ getHighlightRectsForPage(page: number): Rect[];
37
+ getHighlightRects(): Record<number, Rect[]>;
38
+ getBoundingRectForPage(page: number): Rect | null;
27
39
  getBoundingRects(): {
28
40
  page: number;
29
41
  rect: Rect;
30
42
  }[];
43
+ getSelectedText(): PdfTask<string[]>;
44
+ copyToClipboard(): void;
31
45
  begin(page: number, glyphIdx: number): void;
32
46
  update(page: number, glyphIdx: number): void;
33
47
  end(): void;
34
48
  clear(): void;
35
- onSelectionChange(cb: (r: SelectionRangeX | null) => void): () => void;
49
+ onSelectionChange: EventHook<SelectionRangeX | null>;
50
+ onTextRetrieved: EventHook<string[]>;
51
+ onCopyToClipboard: EventHook<string>;
52
+ onBeginSelection: EventHook<{
53
+ page: number;
54
+ index: number;
55
+ }>;
56
+ onEndSelection: EventHook<void>;
57
+ /** Tell the selection plugin that text selection should stay
58
+ enabled while <modeId> is active. */
59
+ enableForMode(modeId: string): void;
60
+ /** Quick check used by SelectionLayer during pointer events. */
61
+ isEnabledForMode(modeId: string): boolean;
36
62
  }
37
63
 
38
64
  declare const CACHE_PAGE_GEOMETRY = "CACHE_PAGE_GEOMETRY";
@@ -41,6 +67,7 @@ declare const START_SELECTION = "START_SELECTION";
41
67
  declare const END_SELECTION = "END_SELECTION";
42
68
  declare const CLEAR_SELECTION = "CLEAR_SELECTION";
43
69
  declare const SET_RECTS = "SET_RECTS";
70
+ declare const SET_SLICES = "SET_SLICES";
44
71
  declare const RESET = "RESET";
45
72
  interface CachePageGeometryAction extends Action {
46
73
  type: typeof CACHE_PAGE_GEOMETRY;
@@ -64,23 +91,33 @@ interface ClearSelectionAction extends Action {
64
91
  }
65
92
  interface SetRectsAction extends Action {
66
93
  type: typeof SET_RECTS;
67
- payload: {
68
- page: number;
69
- rects: Rect[];
70
- };
94
+ payload: Record<number, Rect[]>;
95
+ }
96
+ interface SetSlicesAction extends Action {
97
+ type: typeof SET_SLICES;
98
+ payload: Record<number, {
99
+ start: number;
100
+ count: number;
101
+ }>;
71
102
  }
72
103
  interface ResetAction extends Action {
73
104
  type: typeof RESET;
74
105
  }
75
- type SelectionAction = CachePageGeometryAction | SetSelectionAction | StartSelectionAction | EndSelectionAction | ClearSelectionAction | SetRectsAction | ResetAction;
106
+ type SelectionAction = CachePageGeometryAction | SetSelectionAction | StartSelectionAction | EndSelectionAction | ClearSelectionAction | SetRectsAction | SetSlicesAction | ResetAction;
76
107
 
77
108
  declare class SelectionPlugin extends BasePlugin<SelectionPluginConfig, SelectionCapability, SelectionState, SelectionAction> {
78
109
  private engine;
79
110
  static readonly id: "selection";
80
111
  private doc?;
112
+ /** Modes that should trigger text-selection logic */
113
+ private enabledModes;
81
114
  private selecting;
82
115
  private anchor?;
83
116
  private readonly selChange$;
117
+ private readonly textRetrieved$;
118
+ private readonly copyToClipboard$;
119
+ private readonly beginSelection$;
120
+ private readonly endSelection$;
84
121
  constructor(id: string, registry: PluginRegistry, engine: PdfEngine);
85
122
  initialize(): Promise<void>;
86
123
  destroy(): Promise<void>;
@@ -89,9 +126,10 @@ declare class SelectionPlugin extends BasePlugin<SelectionPluginConfig, Selectio
89
126
  private beginSelection;
90
127
  private endSelection;
91
128
  private clearSelection;
92
- private updateRectsForRange;
93
129
  private updateSelection;
94
- private buildRectsForPage;
130
+ private updateRectsAndSlices;
131
+ private getSelectedText;
132
+ private copyToClipboard;
95
133
  }
96
134
 
97
135
  declare const SELECTION_PLUGIN_ID = "selection";
@@ -107,7 +145,68 @@ declare function glyphAt(geo: PdfPageGeometry, pt: {
107
145
  x: number;
108
146
  y: number;
109
147
  }): number;
148
+ /**
149
+ * Helper: min/max glyph indices on `page` for current sel
150
+ * @param sel - selection range
151
+ * @param geo - page geometry
152
+ * @param page - page index
153
+ * @returns { from: number; to: number } | null
154
+ */
155
+ declare function sliceBounds(sel: SelectionRangeX | null, geo: PdfPageGeometry | undefined, page: number): {
156
+ from: number;
157
+ to: number;
158
+ } | null;
159
+ /**
160
+ * Helper: build rects for a slice of the page
161
+ * @param geo - page geometry
162
+ * @param from - from index
163
+ * @param to - to index
164
+ * @param merge - whether to merge adjacent rects (default: true)
165
+ * @returns rects
166
+ */
167
+ declare function rectsWithinSlice(geo: PdfPageGeometry, from: number, to: number, merge?: boolean): Rect[];
168
+ /**
169
+ * ============================================================================
170
+ * Rectangle Merging Algorithm
171
+ * ============================================================================
172
+ *
173
+ * The following code is adapted from Chromium's PDF text selection implementation.
174
+ *
175
+ * Copyright 2010 The Chromium Authors
176
+ * Use of this source code is governed by a BSD-style license that can be
177
+ * found in the LICENSE file: https://source.chromium.org/chromium/chromium/src/+/main:LICENSE
178
+ *
179
+ * Original source:
180
+ * https://source.chromium.org/chromium/chromium/src/+/main:pdf/pdfium/pdfium_range.cc
181
+ *
182
+ * Adapted for TypeScript and this project's Rect/geometry types.
183
+ */
184
+ /**
185
+ * Text run info for rect merging (similar to Chromium's ScreenRectTextRunInfo)
186
+ */
187
+ interface TextRunInfo {
188
+ rect: Rect;
189
+ charCount: number;
190
+ }
191
+ /**
192
+ * Helper functions for Rect operations
193
+ */
194
+ declare function rectUnion(rect1: Rect, rect2: Rect): Rect;
195
+ declare function rectIntersect(rect1: Rect, rect2: Rect): Rect;
196
+ declare function rectIsEmpty(rect: Rect): boolean;
197
+ /**
198
+ * Returns a ratio between [0, 1] representing vertical overlap
199
+ */
200
+ declare function getVerticalOverlap(rect1: Rect, rect2: Rect): number;
201
+ /**
202
+ * Returns true if there is sufficient horizontal and vertical overlap
203
+ */
204
+ declare function shouldMergeHorizontalRects(textRun1: TextRunInfo, textRun2: TextRunInfo): boolean;
205
+ /**
206
+ * Merge adjacent rectangles based on proximity and overlap (similar to Chromium's algorithm)
207
+ */
208
+ declare function mergeAdjacentRects(textRuns: TextRunInfo[]): Rect[];
110
209
 
111
210
  declare const SelectionPluginPackage: PluginPackage<SelectionPlugin, SelectionPluginConfig, SelectionState, SelectionAction>;
112
211
 
113
- export { type GlyphPointer, SELECTION_PLUGIN_ID, type SelectionCapability, SelectionPlugin, type SelectionPluginConfig, SelectionPluginPackage, type SelectionRangeX, type SelectionState, glyphAt, manifest };
212
+ export { type FormattedSelection, type GlyphPointer, SELECTION_PLUGIN_ID, type SelectionCapability, SelectionPlugin, type SelectionPluginConfig, SelectionPluginPackage, type SelectionRangeX, type SelectionState, type TextRunInfo, getVerticalOverlap, glyphAt, manifest, mergeAdjacentRects, rectIntersect, rectIsEmpty, rectUnion, rectsWithinSlice, shouldMergeHorizontalRects, sliceBounds };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { BasePluginConfig, Action, BasePlugin, PluginRegistry, PluginManifest, PluginPackage } from '@embedpdf/core';
2
- import { PdfPageGeometry, Rect, PdfEngine } from '@embedpdf/models';
1
+ import { BasePluginConfig, EventHook, Action, BasePlugin, PluginRegistry, PluginManifest, PluginPackage } from '@embedpdf/core';
2
+ import { PdfTask, PdfPageGeometry, Rect, PdfEngine } from '@embedpdf/models';
3
3
 
4
4
  interface SelectionPluginConfig extends BasePluginConfig {
5
5
  }
@@ -17,22 +17,48 @@ interface SelectionState {
17
17
  /** current selection or null */
18
18
  rects: Record<number, Rect[]>;
19
19
  selection: SelectionRangeX | null;
20
+ slices: Record<number, {
21
+ start: number;
22
+ count: number;
23
+ }>;
20
24
  active: boolean;
21
25
  selecting: boolean;
22
26
  }
27
+ interface FormattedSelection {
28
+ pageIndex: number;
29
+ rect: Rect;
30
+ segmentRects: Rect[];
31
+ }
23
32
  interface SelectionCapability {
24
- getGeometry(page: number): Promise<PdfPageGeometry>;
25
- getHighlightRects(page: number): Rect[];
26
- getBoundingRect(page: number): Rect | null;
33
+ getGeometry(page: number): PdfTask<PdfPageGeometry>;
34
+ getFormattedSelection(): FormattedSelection[];
35
+ getFormattedSelectionForPage(page: number): FormattedSelection | null;
36
+ getHighlightRectsForPage(page: number): Rect[];
37
+ getHighlightRects(): Record<number, Rect[]>;
38
+ getBoundingRectForPage(page: number): Rect | null;
27
39
  getBoundingRects(): {
28
40
  page: number;
29
41
  rect: Rect;
30
42
  }[];
43
+ getSelectedText(): PdfTask<string[]>;
44
+ copyToClipboard(): void;
31
45
  begin(page: number, glyphIdx: number): void;
32
46
  update(page: number, glyphIdx: number): void;
33
47
  end(): void;
34
48
  clear(): void;
35
- onSelectionChange(cb: (r: SelectionRangeX | null) => void): () => void;
49
+ onSelectionChange: EventHook<SelectionRangeX | null>;
50
+ onTextRetrieved: EventHook<string[]>;
51
+ onCopyToClipboard: EventHook<string>;
52
+ onBeginSelection: EventHook<{
53
+ page: number;
54
+ index: number;
55
+ }>;
56
+ onEndSelection: EventHook<void>;
57
+ /** Tell the selection plugin that text selection should stay
58
+ enabled while <modeId> is active. */
59
+ enableForMode(modeId: string): void;
60
+ /** Quick check used by SelectionLayer during pointer events. */
61
+ isEnabledForMode(modeId: string): boolean;
36
62
  }
37
63
 
38
64
  declare const CACHE_PAGE_GEOMETRY = "CACHE_PAGE_GEOMETRY";
@@ -41,6 +67,7 @@ declare const START_SELECTION = "START_SELECTION";
41
67
  declare const END_SELECTION = "END_SELECTION";
42
68
  declare const CLEAR_SELECTION = "CLEAR_SELECTION";
43
69
  declare const SET_RECTS = "SET_RECTS";
70
+ declare const SET_SLICES = "SET_SLICES";
44
71
  declare const RESET = "RESET";
45
72
  interface CachePageGeometryAction extends Action {
46
73
  type: typeof CACHE_PAGE_GEOMETRY;
@@ -64,23 +91,33 @@ interface ClearSelectionAction extends Action {
64
91
  }
65
92
  interface SetRectsAction extends Action {
66
93
  type: typeof SET_RECTS;
67
- payload: {
68
- page: number;
69
- rects: Rect[];
70
- };
94
+ payload: Record<number, Rect[]>;
95
+ }
96
+ interface SetSlicesAction extends Action {
97
+ type: typeof SET_SLICES;
98
+ payload: Record<number, {
99
+ start: number;
100
+ count: number;
101
+ }>;
71
102
  }
72
103
  interface ResetAction extends Action {
73
104
  type: typeof RESET;
74
105
  }
75
- type SelectionAction = CachePageGeometryAction | SetSelectionAction | StartSelectionAction | EndSelectionAction | ClearSelectionAction | SetRectsAction | ResetAction;
106
+ type SelectionAction = CachePageGeometryAction | SetSelectionAction | StartSelectionAction | EndSelectionAction | ClearSelectionAction | SetRectsAction | SetSlicesAction | ResetAction;
76
107
 
77
108
  declare class SelectionPlugin extends BasePlugin<SelectionPluginConfig, SelectionCapability, SelectionState, SelectionAction> {
78
109
  private engine;
79
110
  static readonly id: "selection";
80
111
  private doc?;
112
+ /** Modes that should trigger text-selection logic */
113
+ private enabledModes;
81
114
  private selecting;
82
115
  private anchor?;
83
116
  private readonly selChange$;
117
+ private readonly textRetrieved$;
118
+ private readonly copyToClipboard$;
119
+ private readonly beginSelection$;
120
+ private readonly endSelection$;
84
121
  constructor(id: string, registry: PluginRegistry, engine: PdfEngine);
85
122
  initialize(): Promise<void>;
86
123
  destroy(): Promise<void>;
@@ -89,9 +126,10 @@ declare class SelectionPlugin extends BasePlugin<SelectionPluginConfig, Selectio
89
126
  private beginSelection;
90
127
  private endSelection;
91
128
  private clearSelection;
92
- private updateRectsForRange;
93
129
  private updateSelection;
94
- private buildRectsForPage;
130
+ private updateRectsAndSlices;
131
+ private getSelectedText;
132
+ private copyToClipboard;
95
133
  }
96
134
 
97
135
  declare const SELECTION_PLUGIN_ID = "selection";
@@ -107,7 +145,68 @@ declare function glyphAt(geo: PdfPageGeometry, pt: {
107
145
  x: number;
108
146
  y: number;
109
147
  }): number;
148
+ /**
149
+ * Helper: min/max glyph indices on `page` for current sel
150
+ * @param sel - selection range
151
+ * @param geo - page geometry
152
+ * @param page - page index
153
+ * @returns { from: number; to: number } | null
154
+ */
155
+ declare function sliceBounds(sel: SelectionRangeX | null, geo: PdfPageGeometry | undefined, page: number): {
156
+ from: number;
157
+ to: number;
158
+ } | null;
159
+ /**
160
+ * Helper: build rects for a slice of the page
161
+ * @param geo - page geometry
162
+ * @param from - from index
163
+ * @param to - to index
164
+ * @param merge - whether to merge adjacent rects (default: true)
165
+ * @returns rects
166
+ */
167
+ declare function rectsWithinSlice(geo: PdfPageGeometry, from: number, to: number, merge?: boolean): Rect[];
168
+ /**
169
+ * ============================================================================
170
+ * Rectangle Merging Algorithm
171
+ * ============================================================================
172
+ *
173
+ * The following code is adapted from Chromium's PDF text selection implementation.
174
+ *
175
+ * Copyright 2010 The Chromium Authors
176
+ * Use of this source code is governed by a BSD-style license that can be
177
+ * found in the LICENSE file: https://source.chromium.org/chromium/chromium/src/+/main:LICENSE
178
+ *
179
+ * Original source:
180
+ * https://source.chromium.org/chromium/chromium/src/+/main:pdf/pdfium/pdfium_range.cc
181
+ *
182
+ * Adapted for TypeScript and this project's Rect/geometry types.
183
+ */
184
+ /**
185
+ * Text run info for rect merging (similar to Chromium's ScreenRectTextRunInfo)
186
+ */
187
+ interface TextRunInfo {
188
+ rect: Rect;
189
+ charCount: number;
190
+ }
191
+ /**
192
+ * Helper functions for Rect operations
193
+ */
194
+ declare function rectUnion(rect1: Rect, rect2: Rect): Rect;
195
+ declare function rectIntersect(rect1: Rect, rect2: Rect): Rect;
196
+ declare function rectIsEmpty(rect: Rect): boolean;
197
+ /**
198
+ * Returns a ratio between [0, 1] representing vertical overlap
199
+ */
200
+ declare function getVerticalOverlap(rect1: Rect, rect2: Rect): number;
201
+ /**
202
+ * Returns true if there is sufficient horizontal and vertical overlap
203
+ */
204
+ declare function shouldMergeHorizontalRects(textRun1: TextRunInfo, textRun2: TextRunInfo): boolean;
205
+ /**
206
+ * Merge adjacent rectangles based on proximity and overlap (similar to Chromium's algorithm)
207
+ */
208
+ declare function mergeAdjacentRects(textRuns: TextRunInfo[]): Rect[];
110
209
 
111
210
  declare const SelectionPluginPackage: PluginPackage<SelectionPlugin, SelectionPluginConfig, SelectionState, SelectionAction>;
112
211
 
113
- export { type GlyphPointer, SELECTION_PLUGIN_ID, type SelectionCapability, SelectionPlugin, type SelectionPluginConfig, SelectionPluginPackage, type SelectionRangeX, type SelectionState, glyphAt, manifest };
212
+ export { type FormattedSelection, type GlyphPointer, SELECTION_PLUGIN_ID, type SelectionCapability, SelectionPlugin, type SelectionPluginConfig, SelectionPluginPackage, type SelectionRangeX, type SelectionState, type TextRunInfo, getVerticalOverlap, glyphAt, manifest, mergeAdjacentRects, rectIntersect, rectIsEmpty, rectUnion, rectsWithinSlice, shouldMergeHorizontalRects, sliceBounds };