pdfjs-viewer-highlight-echo 5.4.456 → 5.4.460

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pdfjs-viewer-highlight-echo",
3
- "version": "5.4.456",
3
+ "version": "5.4.460",
4
4
  "main": "build/pdf.mjs",
5
5
  "types": "types/src/pdf.d.ts",
6
6
  "description": "Dafu custom PDF.js distribution",
@@ -292,6 +292,9 @@ export class AnnotationEditor {
292
292
  addStandaloneCommentButton(): void;
293
293
  removeStandaloneCommentButton(): void;
294
294
  hideStandaloneCommentButton(): void;
295
+ addSaveSelectionButton(): HTMLButtonElement;
296
+ addSaveButtonInToolbar(): void;
297
+ removeSaveButtonFromToolbar(): void;
295
298
  set comment(text: {
296
299
  text: any;
297
300
  richText: any;
@@ -7,6 +7,8 @@ export class EditorToolbar {
7
7
  hide(): void;
8
8
  show(): void;
9
9
  addDeleteButton(): void;
10
+ addSaveButton(saveButton: any): void;
11
+ removeSaveButton(): void;
10
12
  addAltText(altText: any): Promise<void>;
11
13
  addComment(comment: any, beforeElement?: null): void;
12
14
  addColorPicker(colorPicker: any): void;
@@ -76,7 +76,7 @@ export class AnnotationEditorUIManager {
76
76
  highlightSelection(methodOfCreation?: string, comment?: boolean): void;
77
77
  commentSelection(methodOfCreation?: string): void;
78
78
  copySelection(methodOfCreation?: string): void;
79
- saveSelectionAsJson(methodOfCreation?: string): void;
79
+ saveSelectionAsJson(methodOfCreation?: string, editorInfo?: null): void;
80
80
  /**
81
81
  * Some annotations may have been modified in the annotation layer
82
82
  * (e.g. comments added or modified).
@@ -1,35 +1,61 @@
1
1
  [
2
2
  {
3
+ "id": "highlight-1",
3
4
  "location": [
4
- 32,
5
- 456,
6
- 258,
7
- 428
5
+ 25,
6
+ 505,
7
+ 256,
8
+ 407
8
9
  ],
9
- "page": 10,
10
+ "page": 1,
11
+ "text": "Enviro"
12
+ },
13
+ {
14
+ "id": "highlight-2",
15
+ "location": [
16
+ 25,
17
+ 359,
18
+ 276,
19
+ 261
20
+ ],
21
+ "page": 1,
22
+ "text": "Report"
23
+ },
24
+ {
25
+ "id": "highlight-99",
26
+ "location": [
27
+ 22,
28
+ 500,
29
+ 563,
30
+ 267
31
+ ],
32
+ "page": 1,
10
33
  "text": "Environmental Progress Report"
11
34
  },
12
35
  {
36
+ "id": "highlight-98",
13
37
  "location": [
14
- 315,
15
- 488,
16
- 239,
17
- -33
38
+ 584,
39
+ 298,
40
+ 669,
41
+ 286
18
42
  ],
19
- "page": 13,
20
- "text": "run dynamic languages efficiently by\nrecording hot …hnique focuses on aggressively inlined loops, and"
43
+ "page": 4,
44
+ "text": "Read more on page 15."
21
45
  },
22
46
  {
47
+ "id": "highlight-4",
23
48
  "location": [
24
- 315,
25
- 186,
26
- 239,
27
- -102
49
+ 46,
50
+ 441,
51
+ 265,
52
+ 411
28
53
  ],
29
54
  "page": 4,
30
- "text": "For example, the search might finds the value of\no.x in the prototype of o, which uses a shared hash-table represen-\ntation that places x in slot 2 of a property vector. Then the recorded\ncan generate LIR that reads o.x with just two or three loads: one to\nget the prototype, possibly one to get the property value vector, and\none more to get slot 2 from the vector. This is a vast simplification\nand speedup compared to the original interpreter code. Inheritance\nrelationships and object representations can change during execu-\ntion, so the simplified code requires guard instructions that ensure\nthe object representation is the same. In TraceMonkey, objects’ rep-"
55
+ "text": "Reduced our overall GHG emissions by more than 60 percent"
31
56
  },
32
57
  {
58
+ "id": "highlight-5",
33
59
  "location": [
34
60
  315,
35
61
  572,
@@ -40,6 +66,7 @@
40
66
  "text": "igure 5. A tree with two traces, a trunk trace and one branch\ntrace. The trunk trace contains a guard to which a branch trace was\nattached. The branch trace contain a guard that may fail and trigger\na side exit. Both the trunk and the branch trace loop back to the tree\nanchor, which is the beginning of the trace tree."
41
67
  },
42
68
  {
69
+ "id": "highlight-6",
43
70
  "location": [
44
71
  54,
45
72
  432,
@@ -50,6 +77,7 @@
50
77
  "text": "Google’s V8 JS compiler. Our system generates particularly efficient code for programs that benefit most from\ntype specialization, which includes SunSpider Benchmark programs that perform bit manipulation. We type-specialize the code in question\nto use integer arithmetic, which substantially improves performance. For one of the benchmark programs we execute 25 times faster than\nthe SpiderMonkey interpreter, and almost 5 times faster than V8 and SFX. For a large number of benchmarks all three VMs produce similar\nresults. We perform worst on benchmark programs that we do not trace and instead fall back onto the interpreter. This includes the recursive\nbenchmarks access-binary-trees and control-flow-recursive, for which we currently don’t generate any native code.\nIn particular, the bitops benchmarks are short programs that per-\nform many bitwise operations, so TraceMonkey can cover the en-\ntire program with 1 or 2 traces that operate on integers. TraceMon-\nkey runs all the other programs in this set almost entirely as na"
51
78
  },
52
79
  {
80
+ "id": "highlight-7",
53
81
  "location": [
54
82
  54,
55
83
  370,
@@ -60,6 +88,7 @@
60
88
  "text": "he total execution time in processor\nclock cycles by the number of bytecodes executed in the base\ninterpreter shows that on average, a bytecode executes in about\n35 cycles. Native traces take about 9 cycles per bytecode, a 3.9"
61
89
  },
62
90
  {
91
+ "id": "highlight-8",
63
92
  "location": [
64
93
  315,
65
94
  504,
@@ -70,6 +99,7 @@
70
99
  "text": "onclusions\nThis paper described how to run dynamic languages efficiently by\nrecording hot traces and generating type-specialized native code.\nOur techniq"
71
100
  },
72
101
  {
102
+ "id": "highlight-9",
73
103
  "location": [
74
104
  315,
75
105
  652,
@@ -21,8 +21,8 @@
21
21
  */
22
22
 
23
23
  /**
24
- * pdfjsVersion = 5.4.456
25
- * pdfjsBuild = 01b61ef1d
24
+ * pdfjsVersion = 5.4.460
25
+ * pdfjsBuild = 8489d7097
26
26
  */
27
27
  /******/ // The require scope
28
28
  /******/ var __webpack_require__ = {};
@@ -7655,7 +7655,7 @@ class PDFViewer {
7655
7655
  #textLayerMode = TextLayerMode.ENABLE;
7656
7656
  #viewerAlert = null;
7657
7657
  constructor(options) {
7658
- const viewerVersion = "5.4.456";
7658
+ const viewerVersion = "5.4.460";
7659
7659
  if (version !== viewerVersion) {
7660
7660
  throw new Error(`The API version "${version}" does not match the Viewer version "${viewerVersion}".`);
7661
7661
  }
package/web/viewer.mjs CHANGED
@@ -21,8 +21,8 @@
21
21
  */
22
22
 
23
23
  /**
24
- * pdfjsVersion = 5.4.456
25
- * pdfjsBuild = 01b61ef1d
24
+ * pdfjsVersion = 5.4.460
25
+ * pdfjsBuild = 8489d7097
26
26
  */
27
27
  /******/ // The require scope
28
28
  /******/ var __webpack_require__ = {};
@@ -7440,6 +7440,7 @@ class PDFFilterView {
7440
7440
  this.eventBus.dispatch("filterhighlightselected", {
7441
7441
  source: this,
7442
7442
  pageNumber: page,
7443
+ id: this.highlightData.id,
7443
7444
  location: {
7444
7445
  x,
7445
7446
  y,
@@ -7589,13 +7590,15 @@ class FilterHighlightOverlay {
7589
7590
  #eventBus = null;
7590
7591
  #currentOverlay = null;
7591
7592
  #uiManager = null;
7593
+ #lastPageNumber = null;
7594
+ #highlightEditorsByPage = new Map();
7592
7595
  constructor(eventBus) {
7593
7596
  this.#eventBus = eventBus;
7594
7597
  this.#addEventListeners();
7595
7598
  }
7596
7599
  #addEventListeners() {
7597
7600
  this.#eventBus._on("filterhighlightselected", evt => {
7598
- this.showHighlight(evt.pageNumber, evt.location);
7601
+ this.showHighlight(evt.pageNumber, evt.location, evt.id);
7599
7602
  });
7600
7603
  this.#eventBus._on("annotationeditoruimanager", ({
7601
7604
  uiManager
@@ -7603,8 +7606,85 @@ class FilterHighlightOverlay {
7603
7606
  this.#uiManager = uiManager;
7604
7607
  });
7605
7608
  }
7606
- showHighlight(pageNumber, location) {
7607
- this.clearHighlight();
7609
+ storeHighlightEditorsForCurPageNum(id, pageNumber, uiManager) {
7610
+ const editorList = Array.from(uiManager.getEditors(pageNumber - 1));
7611
+ console.log(`DEBUG - Found ${editorList.length} editor(s) for page ${pageNumber}`);
7612
+ let highlightDivIds = [];
7613
+ highlightDivIds = this.#highlightEditorsByPage.get(pageNumber) || [];
7614
+ for (const editor of editorList) {
7615
+ if (editor && editor.constructor.name === 'HighlightEditor') {
7616
+ const divId = editor.div?.id;
7617
+ if (divId) {
7618
+ const exists = highlightDivIds.some(item => item.highlightDivId === divId);
7619
+ if (!exists) {
7620
+ highlightDivIds.push({
7621
+ textId: id,
7622
+ highlightDivId: divId
7623
+ });
7624
+ }
7625
+ console.log(`DEBUG - Found HighlightEditor with div ID:`, {
7626
+ textId: id,
7627
+ highlightDivId: divId
7628
+ });
7629
+ }
7630
+ }
7631
+ }
7632
+ if (highlightDivIds.length > 0) {
7633
+ this.#highlightEditorsByPage.set(pageNumber, highlightDivIds);
7634
+ console.log(`DEBUG - Stored ${highlightDivIds.length} highlight editor(s) for page ${pageNumber}:`, highlightDivIds);
7635
+ }
7636
+ }
7637
+ findHighlightByDivId(divId) {
7638
+ for (const [pageNumber, highlights] of this.#highlightEditorsByPage.entries()) {
7639
+ const found = highlights.find(item => item.highlightDivId === divId);
7640
+ if (found) {
7641
+ return {
7642
+ ...found,
7643
+ pageNumber
7644
+ };
7645
+ }
7646
+ }
7647
+ return null;
7648
+ }
7649
+ findHighlightsByTextId(textId) {
7650
+ const results = [];
7651
+ for (const [pageNumber, highlights] of this.#highlightEditorsByPage.entries()) {
7652
+ const matches = highlights.filter(item => item.textId === textId);
7653
+ matches.forEach(match => results.push({
7654
+ ...match,
7655
+ pageNumber
7656
+ }));
7657
+ }
7658
+ return results;
7659
+ }
7660
+ async showHighlight(pageNumber, location, id) {
7661
+ let highlightDivIds = [];
7662
+ highlightDivIds = this.#highlightEditorsByPage.get(pageNumber) || [];
7663
+ const filteredHighlights = highlightDivIds.filter(item => item.textId === id);
7664
+ if (filteredHighlights.length > 0) {
7665
+ console.log(`DEBUG - Highlights already exist for page ${pageNumber}, textId ${id}, skipping creation.`);
7666
+ filteredHighlights.forEach(item => {
7667
+ const uiManager = this.#uiManager || window.PDFViewerApplication?.pdfViewer?.annotationEditorUIManager;
7668
+ if (uiManager) {
7669
+ const editorList = Array.from(uiManager.getEditors(pageNumber - 1));
7670
+ const editor = editorList.find(e => e.div?.id === item.highlightDivId);
7671
+ if (editor) {
7672
+ console.log("DEBUG - Reusing existing highlight editor:", editor);
7673
+ if (editor.div) {
7674
+ editor.div.style.display = '';
7675
+ editor.div.classList.add('selectedEditor');
7676
+ if (typeof editor.select === 'function') {
7677
+ editor.select();
7678
+ }
7679
+ uiManager.setSelected(editor);
7680
+ }
7681
+ }
7682
+ }
7683
+ });
7684
+ return;
7685
+ }
7686
+ console.log(`DEBUG - No existing highlights for page ${pageNumber}, proceeding to create.`);
7687
+ this.#lastPageNumber = pageNumber;
7608
7688
  const uiManager = this.#uiManager || window.PDFViewerApplication?.pdfViewer?.annotationEditorUIManager;
7609
7689
  if (!uiManager) {
7610
7690
  console.error("UI Manager not initialized. Please enable annotation editing mode.");
@@ -7615,9 +7695,10 @@ class FilterHighlightOverlay {
7615
7695
  console.error("Page view not found for page", pageNumber);
7616
7696
  return;
7617
7697
  }
7618
- if (!pageView.textLayer) {
7619
- console.warn("Text layer not ready, waiting...");
7620
- setTimeout(() => this.showHighlight(pageNumber, location), 100);
7698
+ try {
7699
+ await this.#waitForTextLayer(pageView);
7700
+ } catch (error) {
7701
+ console.error("Failed to load text layer:", error);
7621
7702
  return;
7622
7703
  }
7623
7704
  const textLayer = pageView.textLayer.div;
@@ -7625,12 +7706,14 @@ class FilterHighlightOverlay {
7625
7706
  console.error("Text layer div not found");
7626
7707
  return;
7627
7708
  }
7709
+ console.log("DEBUG - Text layer before extraction, total spans:", textLayer.querySelectorAll("span[role='presentation']").length);
7710
+ console.log("Extracting text from location:", location);
7628
7711
  const textInfo = this.#extractTextFromLocation(textLayer, pageView, location);
7712
+ console.log("Extracting text Info:", textInfo);
7629
7713
  if (!textInfo) {
7630
7714
  console.error("Could not extract text from location", location);
7631
7715
  return;
7632
7716
  }
7633
- console.log("Extracted text info:", textInfo);
7634
7717
  const selection = document.getSelection();
7635
7718
  selection.removeAllRanges();
7636
7719
  const range = document.createRange();
@@ -7640,14 +7723,51 @@ class FilterHighlightOverlay {
7640
7723
  selection.addRange(range);
7641
7724
  console.log("Selection created:", selection.toString());
7642
7725
  setTimeout(() => {
7643
- uiManager.highlightSelection("filter_view");
7726
+ const editor = uiManager.highlightSelection("filter_view");
7644
7727
  selection.removeAllRanges();
7728
+ setTimeout(() => {
7729
+ this.storeHighlightEditorsForCurPageNum(id, pageNumber, uiManager);
7730
+ }, 50);
7645
7731
  }, 100);
7646
7732
  } catch (error) {
7647
7733
  console.error("Error creating selection:", error);
7648
7734
  selection.removeAllRanges();
7649
7735
  }
7650
7736
  }
7737
+ async #waitForTextLayer(pageView) {
7738
+ if (pageView.textLayer?.div) {
7739
+ return;
7740
+ }
7741
+ if (pageView.renderingState < 3) {
7742
+ await new Promise(resolve => {
7743
+ const checkRendering = () => {
7744
+ if (pageView.renderingState >= 3) {
7745
+ resolve();
7746
+ } else {
7747
+ setTimeout(checkRendering, 50);
7748
+ }
7749
+ };
7750
+ checkRendering();
7751
+ });
7752
+ }
7753
+ if (pageView.textLayer?.renderingDone) {
7754
+ await pageView.textLayer.renderingDone;
7755
+ return;
7756
+ }
7757
+ return new Promise((resolve, reject) => {
7758
+ const timeout = setTimeout(() => {
7759
+ reject(new Error("Text layer rendering timeout"));
7760
+ }, 5000);
7761
+ const handler = evt => {
7762
+ if (evt.pageNumber === pageView.id) {
7763
+ clearTimeout(timeout);
7764
+ this.#eventBus._off("textlayerrendered", handler);
7765
+ resolve();
7766
+ }
7767
+ };
7768
+ this.#eventBus._on("textlayerrendered", handler);
7769
+ });
7770
+ }
7651
7771
  #convertLocationToBoxes(pageView, location) {
7652
7772
  if (!pageView.div) {
7653
7773
  return null;
@@ -7661,15 +7781,20 @@ class FilterHighlightOverlay {
7661
7781
  const viewport = pageView.viewport;
7662
7782
  const [x1, y1] = viewport.convertToViewportPoint(x, y + height);
7663
7783
  const [x2, y2] = viewport.convertToViewportPoint(x + width, y);
7664
- const pageWidth = viewport.width;
7665
- const pageHeight = viewport.height;
7784
+ const pageRect = pageView.div.getBoundingClientRect();
7785
+ const scaleX = pageRect.width / viewport.width || 1;
7786
+ const scaleY = pageRect.height / viewport.height || 1;
7787
+ const pageWidth = pageRect.width;
7788
+ const pageHeight = pageRect.height;
7666
7789
  const minX = Math.min(x1, x2);
7667
7790
  const minY = Math.min(y1, y2);
7791
+ const boxWidth = Math.abs(x2 - x1);
7792
+ const boxHeight = Math.abs(y2 - y1);
7668
7793
  return [{
7669
- x: minX / pageWidth,
7670
- y: minY / pageHeight,
7671
- width: Math.abs(x2 - x1) / pageWidth,
7672
- height: Math.abs(y2 - y1) / pageHeight
7794
+ x: minX * scaleX / pageWidth,
7795
+ y: minY * scaleY / pageHeight,
7796
+ width: boxWidth * scaleX / pageWidth,
7797
+ height: boxHeight * scaleY / pageHeight
7673
7798
  }];
7674
7799
  }
7675
7800
  #extractTextFromLocation(textLayer, pageView, location) {
@@ -7688,38 +7813,130 @@ class FilterHighlightOverlay {
7688
7813
  const overlayHeight = Math.abs(y2 - y1);
7689
7814
  const pageRect = pageView.div.getBoundingClientRect();
7690
7815
  const textLayerRect = textLayer.getBoundingClientRect();
7691
- const textElements = textLayer.querySelectorAll("span[role='presentation']");
7692
- let firstNode = null;
7693
- let firstOffset = 0;
7694
- let lastNode = null;
7695
- let lastOffset = 0;
7696
- let collectedText = "";
7816
+ console.log("DEBUG - pageRect:", {
7817
+ left: pageRect.left,
7818
+ top: pageRect.top,
7819
+ width: pageRect.width,
7820
+ height: pageRect.height
7821
+ });
7822
+ console.log("DEBUG - viewport:", {
7823
+ width: viewport.width,
7824
+ height: viewport.height
7825
+ });
7826
+ const scaleX = pageRect.width / viewport.width || 1;
7827
+ const scaleY = pageRect.height / viewport.height || 1;
7828
+ const overlayPageLeft = overlayLeft * scaleX;
7829
+ const overlayPageTop = overlayTop * scaleY;
7830
+ const overlayPageRight = overlayPageLeft + overlayWidth * scaleX;
7831
+ const overlayPageBottom = overlayPageTop + overlayHeight * scaleY;
7832
+ console.log("DEBUG - overlay bounds (page-relative):", {
7833
+ left: overlayPageLeft,
7834
+ top: overlayPageTop,
7835
+ right: overlayPageRight,
7836
+ bottom: overlayPageBottom
7837
+ });
7838
+ const textElements = Array.from(textLayer.querySelectorAll("span[role='presentation']"));
7839
+ console.log("DEBUG - total text spans:", textElements.length);
7840
+ const candidates = [];
7841
+ let debugSkipped = 0;
7697
7842
  for (const span of textElements) {
7698
7843
  const rect = span.getBoundingClientRect();
7699
- const spanLeft = rect.left - pageRect.left;
7700
- const spanTop = rect.top - pageRect.top;
7701
- const spanRight = rect.right - pageRect.left;
7702
- const spanBottom = rect.bottom - pageRect.top;
7703
- if (spanRight > overlayLeft && spanLeft < overlayLeft + overlayWidth && spanBottom > overlayTop && spanTop < overlayTop + overlayHeight) {
7704
- const textNode = span.firstChild;
7705
- if (textNode && textNode.nodeType === Node.TEXT_NODE) {
7706
- if (!firstNode) {
7707
- firstNode = textNode;
7708
- firstOffset = 0;
7709
- }
7710
- lastNode = textNode;
7711
- lastOffset = textNode.textContent.length;
7712
- collectedText += textNode.textContent;
7713
- }
7844
+ const spanPageLeft = rect.left - pageRect.left;
7845
+ const spanPageTop = rect.top - pageRect.top;
7846
+ const spanPageRight = rect.right - pageRect.left;
7847
+ const spanPageBottom = rect.bottom - pageRect.top;
7848
+ if (spanPageRight <= overlayPageLeft || spanPageLeft >= overlayPageRight || spanPageBottom <= overlayPageTop || spanPageTop >= overlayPageBottom) {
7849
+ debugSkipped++;
7850
+ continue;
7851
+ }
7852
+ const textNode = span.firstChild;
7853
+ if (!textNode || textNode.nodeType !== Node.TEXT_NODE) {
7854
+ continue;
7714
7855
  }
7856
+ candidates.push({
7857
+ span,
7858
+ rect: {
7859
+ left: spanPageLeft,
7860
+ top: spanPageTop,
7861
+ right: spanPageRight,
7862
+ bottom: spanPageBottom,
7863
+ width: rect.width,
7864
+ height: rect.height
7865
+ },
7866
+ textNode
7867
+ });
7715
7868
  }
7716
- if (!firstNode || !lastNode) {
7869
+ console.log("DEBUG - candidates found:", candidates.length, "skipped:", debugSkipped);
7870
+ if (candidates.length > 0) {
7871
+ console.log("DEBUG - first candidate:", {
7872
+ text: candidates[0].textNode.textContent.substring(0, 20),
7873
+ rect: candidates[0].rect
7874
+ });
7875
+ console.log("DEBUG - last candidate:", {
7876
+ text: candidates[candidates.length - 1].textNode.textContent.substring(0, 20),
7877
+ rect: candidates[candidates.length - 1].rect
7878
+ });
7879
+ }
7880
+ if (candidates.length === 0) {
7717
7881
  return null;
7718
7882
  }
7883
+ candidates.sort((a, b) => {
7884
+ const topDiff = a.rect.top - b.rect.top;
7885
+ if (Math.abs(topDiff) > 1) return topDiff;
7886
+ return a.rect.left - b.rect.left;
7887
+ });
7888
+ const first = candidates[0];
7889
+ const last = candidates[candidates.length - 1];
7890
+ const makeOffset = (candidate, overlayL, overlayR, isStart) => {
7891
+ const textLen = candidate.textNode.textContent.length || 0;
7892
+ const rectWidth = candidate.rect.width || 0;
7893
+ const rectLeft = candidate.rect.left;
7894
+ const rectRight = candidate.rect.right;
7895
+ if (textLen === 0 || rectWidth === 0) {
7896
+ return isStart ? 0 : textLen;
7897
+ }
7898
+ const coveredLeft = Math.max(rectLeft, overlayL);
7899
+ const coveredRight = Math.min(rectRight, overlayR);
7900
+ const coveredWidth = Math.max(0, coveredRight - coveredLeft);
7901
+ const coverageRatio = coveredWidth / rectWidth;
7902
+ if (isStart) {
7903
+ if (overlayL <= rectLeft + rectWidth * 0.1) {
7904
+ return 0;
7905
+ }
7906
+ const relStart = Math.max(0, overlayL - rectLeft);
7907
+ const startRatio = Math.max(0, Math.min(1, relStart / rectWidth));
7908
+ return Math.floor(startRatio * textLen);
7909
+ } else {
7910
+ if (overlayR >= rectRight - rectWidth * 0.1) {
7911
+ return textLen;
7912
+ }
7913
+ const relEnd = Math.min(rectRight, overlayR) - rectLeft;
7914
+ const endRatio = Math.max(0, Math.min(1, relEnd / rectWidth));
7915
+ return Math.ceil(endRatio * textLen);
7916
+ }
7917
+ };
7918
+ const firstOffset = makeOffset(first, overlayPageLeft, overlayPageRight, true);
7919
+ const lastOffset = makeOffset(last, overlayPageLeft, overlayPageRight, false);
7920
+ let collectedText = "";
7921
+ console.log("Candidates found:", candidates.length);
7922
+ for (let i = 0; i < candidates.length; i++) {
7923
+ const {
7924
+ textNode
7925
+ } = candidates[i];
7926
+ if (i === 0 && i === candidates.length - 1) {
7927
+ collectedText += textNode.textContent.substring(firstOffset, lastOffset);
7928
+ } else if (i === 0) {
7929
+ collectedText += textNode.textContent.substring(firstOffset);
7930
+ } else if (i === candidates.length - 1) {
7931
+ collectedText += textNode.textContent.substring(0, lastOffset);
7932
+ } else {
7933
+ collectedText += textNode.textContent;
7934
+ }
7935
+ }
7719
7936
  return {
7720
- anchorNode: firstNode,
7937
+ anchorNode: first.textNode,
7721
7938
  anchorOffset: firstOffset,
7722
- focusNode: lastNode,
7939
+ focusNode: last.textNode,
7723
7940
  focusOffset: lastOffset,
7724
7941
  text: collectedText.trim()
7725
7942
  };
@@ -7731,12 +7948,29 @@ class FilterHighlightOverlay {
7731
7948
  }
7732
7949
  return pdfViewer._pages[pageNumber - 1];
7733
7950
  }
7734
- clearHighlight() {
7951
+ async clearHighlight() {
7735
7952
  if (this.#currentOverlay) {
7736
- if (typeof this.#currentOverlay.remove === 'function') {
7737
- this.#currentOverlay.remove();
7953
+ console.log("DEBUG - Clearing previous highlights");
7954
+ const uiManager = this.#uiManager || window.PDFViewerApplication?.pdfViewer?.annotationEditorUIManager;
7955
+ if (uiManager) {
7956
+ const editors = uiManager.getEditors();
7957
+ console.log("DEBUG - Editors:", editors, "Type:", typeof editors);
7958
+ if (editors) {
7959
+ const editorList = editors instanceof Map ? Array.from(editors.values()) : Array.isArray(editors) ? editors : Object.values(editors);
7960
+ for (const editor of editorList) {
7961
+ if (editor && typeof editor.remove === 'function') {
7962
+ try {
7963
+ console.log("DEBUG - Removing editor:", editor);
7964
+ editor.remove();
7965
+ } catch (e) {
7966
+ console.warn("Error removing editor:", e);
7967
+ }
7968
+ }
7969
+ }
7970
+ }
7738
7971
  }
7739
7972
  this.#currentOverlay = null;
7973
+ await new Promise(resolve => setTimeout(resolve, 100));
7740
7974
  }
7741
7975
  }
7742
7976
  }
@@ -13288,7 +13522,7 @@ class PDFViewer {
13288
13522
  #textLayerMode = TextLayerMode.ENABLE;
13289
13523
  #viewerAlert = null;
13290
13524
  constructor(options) {
13291
- const viewerVersion = "5.4.456";
13525
+ const viewerVersion = "5.4.460";
13292
13526
  if (version !== viewerVersion) {
13293
13527
  throw new Error(`The API version "${version}" does not match the Viewer version "${viewerVersion}".`);
13294
13528
  }
@@ -17295,7 +17529,76 @@ const PDFViewerApplication = {
17295
17529
  await (this.pdfDocument?.annotationStorage.size > 0 ? this.save() : this.download());
17296
17530
  classList.remove("wait");
17297
17531
  },
17298
- async saveSelectionAsJson() {
17532
+ async saveSelectionAsJson(params = null) {
17533
+ console.log("params:", params);
17534
+ if (params?.methodOfCreation === "editor_save_button" && params?.editorInfo) {
17535
+ const {
17536
+ editorInfo
17537
+ } = params;
17538
+ const pageView = this.pdfViewer.getPageView(editorInfo.pageIndex);
17539
+ if (!pageView) {
17540
+ console.warn("Could not find page view for editor");
17541
+ return;
17542
+ }
17543
+ const pageElement = this.pdfViewer.viewer.querySelector(`.page[data-page-number="${editorInfo.pageNumber}"]`);
17544
+ if (!pageElement) {
17545
+ console.warn("Could not find page element for editor");
17546
+ return;
17547
+ }
17548
+ const textLayer = pageElement.querySelector(".textLayer");
17549
+ if (!textLayer) {
17550
+ console.warn("Could not find text layer for editor");
17551
+ return;
17552
+ }
17553
+ const textLayerRect = textLayer.getBoundingClientRect();
17554
+ const x = editorInfo.screenRect.left - textLayerRect.left;
17555
+ const y = editorInfo.screenRect.top - textLayerRect.top;
17556
+ const width = editorInfo.screenRect.width;
17557
+ const height = editorInfo.screenRect.height;
17558
+ const pdfCoords = pageView.getPagePoint(x, y);
17559
+ const pdfCoordsEnd = pageView.getPagePoint(x + width, y + height);
17560
+ const location = [Math.round(pdfCoords[0]), Math.round(pdfCoords[1]), Math.round(pdfCoordsEnd[0]), Math.round(pdfCoordsEnd[1])];
17561
+ const data = {
17562
+ page: editorInfo.pageNumber,
17563
+ text: editorInfo.textContent,
17564
+ location,
17565
+ timestamp: Date.now(),
17566
+ documentUrl: this.url || window.location.href
17567
+ };
17568
+ console.log("Selection data sent to parent window within Editor:", data);
17569
+ if (this.isViewerEmbedded && window.parent !== window) {
17570
+ try {
17571
+ window.parent.postMessage({
17572
+ type: "pdfjs-selection",
17573
+ source: "pdf.js",
17574
+ data
17575
+ }, "*");
17576
+ console.log("Selection data sent to parent window:", data);
17577
+ } catch (postError) {
17578
+ console.error("Error posting message to parent:", postError);
17579
+ }
17580
+ }
17581
+ try {
17582
+ const customEvent = new CustomEvent("pdfselectioncaptured", {
17583
+ bubbles: true,
17584
+ cancelable: false,
17585
+ detail: data
17586
+ });
17587
+ if (this.isViewerEmbedded) {
17588
+ try {
17589
+ parent.document.dispatchEvent(customEvent);
17590
+ } catch {
17591
+ document.dispatchEvent(customEvent);
17592
+ }
17593
+ } else {
17594
+ document.dispatchEvent(customEvent);
17595
+ }
17596
+ } catch (eventError) {
17597
+ console.error("Error dispatching custom event:", eventError);
17598
+ }
17599
+ console.log("Selection saved and transferred (from editor):", data);
17600
+ return;
17601
+ }
17299
17602
  try {
17300
17603
  const selection = window.getSelection();
17301
17604
  if (!selection || selection.rangeCount === 0) {