diff --git a/manifest-beta.json b/manifest-beta.json index 8ee2210..93b87bb 100644 --- a/manifest-beta.json +++ b/manifest-beta.json @@ -1,7 +1,7 @@ { "id": "pdf-plus", "name": "PDF++", - "version": "0.40.13", + "version": "0.40.14", "minAppVersion": "1.5.8", "description": "The most Obsidian-native PDF annotation tool ever.", "author": "Ryota Ushio", diff --git a/manifest.json b/manifest.json index 8ee2210..93b87bb 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "pdf-plus", "name": "PDF++", - "version": "0.40.13", + "version": "0.40.14", "minAppVersion": "1.5.8", "description": "The most Obsidian-native PDF annotation tool ever.", "author": "Ryota Ushio", diff --git a/package-lock.json b/package-lock.json index 35a1a85..0c713a7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "obsidian-pdf-plus", - "version": "0.40.13", + "version": "0.40.14", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "obsidian-pdf-plus", - "version": "0.40.13", + "version": "0.40.14", "license": "MIT", "devDependencies": { "@cantoo/pdf-lib": "^1.21.1", diff --git a/package.json b/package.json index d10410b..590a021 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "obsidian-pdf-plus", - "version": "0.40.13", + "version": "0.40.14", "description": "The most Obsidian-native PDF annotation tool ever.", "scripts": { "dev": "node esbuild.config.mjs", diff --git a/src/lib/copy-link.ts b/src/lib/copy-link.ts index 4073000..4ad5cfb 100644 --- a/src/lib/copy-link.ts +++ b/src/lib/copy-link.ts @@ -76,9 +76,12 @@ export class copyLinkLib extends PDFPlusLibSubmodule { page = child.pdfViewer.pdfViewer?.currentPageNumber ?? page; } + const selectionStr = child.getTextSelectionRangeStr(pageEl); + if (!selectionStr) return null; + const subpath = paramsToSubpath({ page, - selection: child.getTextSelectionRangeStr(pageEl), + selection: selectionStr, ...subpathParams }); diff --git a/src/lib/highlights/geometry.ts b/src/lib/highlights/geometry.ts index 93f01d9..be338b8 100644 --- a/src/lib/highlights/geometry.ts +++ b/src/lib/highlights/geometry.ts @@ -1,5 +1,5 @@ import { PDFPlusLibSubmodule } from 'lib/submodule'; -import { PropRequired } from 'utils'; +import { getNodeAndOffsetOfTextPos, PropRequired } from 'utils'; import { TextLayerBuilder, Rect, TextContentItem } from 'typings'; @@ -13,7 +13,7 @@ export class HighlightGeometryLib extends PDFPlusLibSubmodule { * Each rectangle is associated with an array of indices of the text content items contained in the rectangle. */ computeMergedHighlightRects(textLayer: TextLayerBuilder, beginIndex: number, beginOffset: number, endIndex: number, endOffset: number): MergedRect[] { - const { textContentItems, textDivs, div } = textLayer; + const { textContentItems, textDivs } = textLayer; const results: MergedRect[] = []; @@ -34,7 +34,7 @@ export class HighlightGeometryLib extends PDFPlusLibSubmodule { if (!item.str) continue; // the minimum rectangle that contains all the chars of this text content item - const rect = this.computeHighlightRectForItem(div, item, textDiv, index, beginIndex, beginOffset, endIndex, endOffset); + const rect = this.computeHighlightRectForItem(item, textDiv, index, beginIndex, beginOffset, endIndex, endOffset); if (!rect) continue; if (!mergedRect) { @@ -59,13 +59,13 @@ export class HighlightGeometryLib extends PDFPlusLibSubmodule { return results; } - computeHighlightRectForItem(textLayerDiv: HTMLElement, item: TextContentItem, textDiv: HTMLElement, index: number, beginIndex: number, beginOffset: number, endIndex: number, endOffset: number): Rect | null { + computeHighlightRectForItem(item: TextContentItem, textDiv: HTMLElement, index: number, beginIndex: number, beginOffset: number, endIndex: number, endOffset: number): Rect | null { // If the item has the `chars` property filled, use it to get the bounding rectangle of each character in the item. if (item.chars && item.chars.length >= item.str.length) { return this.computeHighlightRectForItemFromChars(item as PropRequired, index, beginIndex, beginOffset, endIndex, endOffset); } // Otherwise, use the text layer divs to get the bounding rectangle of the text selection. - return this.computeHighlightRectForItemFromTextLayer(textLayerDiv, item, textDiv, index, beginIndex, beginOffset, endIndex, endOffset); + return this.computeHighlightRectForItemFromTextLayer(item, textDiv, index, beginIndex, beginOffset, endIndex, endOffset); } computeHighlightRectForItemFromChars(item: PropRequired, index: number, beginIndex: number, beginOffset: number, endIndex: number, endOffset: number): Rect | null { @@ -92,40 +92,40 @@ export class HighlightGeometryLib extends PDFPlusLibSubmodule { ]; } - // Inspired by PDFViewerChild.prototype.hightlightText from Obsidian's app.js - computeHighlightRectForItemFromTextLayer(textLayerDiv: HTMLElement, item: TextContentItem, textDiv: HTMLElement, index: number, beginIndex: number, beginOffset: number, endIndex: number, endOffset: number): Rect | null { - const offsetFrom = index === beginIndex ? beginOffset : 0; - // `endOffset` is computed from the `endOffset` property (https://developer.mozilla.org/en-US/docs/Web/API/Range/endOffset) - // of the `Range` contained in the selection, which is the number of characters from the start of the `Range` to its end. - // Therefore, `endOffset` is 1 greater than the index of the last character in the selection. - const offsetTo = index === endIndex ? endOffset : undefined; - + computeHighlightRectForItemFromTextLayer(item: TextContentItem, textDiv: HTMLElement, index: number, beginIndex: number, beginOffset: number, endIndex: number, endOffset: number): Rect | null { // the bounding box of the whole text content item const x1 = item.transform[4]; const y1 = item.transform[5]; const x2 = item.transform[4] + item.width; const y2 = item.transform[5] + item.height; - const textDivCopied = textDiv.cloneNode() as HTMLElement; - textLayerDiv.appendChild(textDivCopied); - - const textBefore = item.str.substring(0, offsetFrom); - textDivCopied.appendText(textBefore); + const range = textDiv.doc.createRange(); - const text = item.str.substring(offsetFrom, offsetTo); - const boundingEl = textDivCopied.createSpan(); - boundingEl.appendText(text); + if (index === beginIndex) { + const posFrom = getNodeAndOffsetOfTextPos(textDiv, beginOffset); + if (posFrom) { + range.setStart(posFrom.node, posFrom.offset); + } else { + range.setStartBefore(textDiv); + } + } else { + range.setStartBefore(textDiv); + } - if (offsetTo !== undefined) { - const textAfter = item.str.substring(offsetTo); - textDivCopied.appendText(textAfter); + if (index === endIndex) { + const posTo = getNodeAndOffsetOfTextPos(textDiv, endOffset); + if (posTo) { + range.setEnd(posTo.node, posTo.offset); + } else { + range.setEndAfter(textDiv); + } + } else { + range.setEndAfter(textDiv); } - const rect = boundingEl.getBoundingClientRect(); + const rect = range.getBoundingClientRect(); const parentRect = textDiv.getBoundingClientRect(); - textDivCopied.remove(); - return [ x1 + (rect.left - parentRect.left) / parentRect.width * item.width, y1 + (rect.bottom - parentRect.bottom) / parentRect.height * item.height, diff --git a/src/toolbar.ts b/src/toolbar.ts index 07c6860..b343bd1 100644 --- a/src/toolbar.ts +++ b/src/toolbar.ts @@ -159,6 +159,18 @@ export class PDFPlusToolbar extends PDFPlusComponent { }); }); }) + .addItem((item) => { + item.setSection('scroll') + .setIcon('lucide-sticky-note') + .setTitle('In-page scroll') + .setChecked(scrollMode === ScrollMode.PAGE) + .onClick(() => { + eventBus.dispatch('switchscrollmode', { + source: toolbar, + mode: ScrollMode.PAGE + }); + }); + }) .addItem((item) => { item.setSection('scroll') .setIcon('lucide-wrap-text')