Add support for opening PDFs at the correct page for their results. (#507)
This relies on the `#Page N^page=N` form coming back from Text Extractor per https://github.com/scambier/obsidian-text-extractor/issues/75 This commit is an attempt to implement https://github.com/scambier/obsidian-omnisearch/issues/100
This commit is contained in:
@@ -1,6 +1,30 @@
|
|||||||
import { type App, type CachedMetadata, MarkdownView, TFile } from 'obsidian'
|
import { type App, type CachedMetadata, MarkdownView, TFile } from 'obsidian'
|
||||||
import type { ResultNote } from '../globals'
|
import type { ResultNote } from '../globals'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the PDF page number from content based on the offset, looking for page markers in format: `^# Page N^page=N$`
|
||||||
|
*/
|
||||||
|
function getPdfPageFromOffset(content: string, offset: number): number | null {
|
||||||
|
// return early if the inputs do not look valid (e.g. if text extractor does not support Page markers)
|
||||||
|
if (!content.includes('# Page ') || offset > content.length) return null
|
||||||
|
|
||||||
|
// We're looking for the last # Page marker, set search space to all text prior to our match
|
||||||
|
const textBeforeOffset = content.substring(0, offset)
|
||||||
|
|
||||||
|
// Iterate through all # Page heading matches, collecting the last one
|
||||||
|
const regex = /^# Page ([0-9]+)\^page=\1$/gm
|
||||||
|
let lastMatch: RegExpExecArray | null = null
|
||||||
|
let match: RegExpExecArray | null
|
||||||
|
while (
|
||||||
|
(match = regex.exec(textBeforeOffset)) !== null
|
||||||
|
) {
|
||||||
|
lastMatch = match
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we found a match, that's the page the result appears on
|
||||||
|
return lastMatch ? parseInt(lastMatch[1], 10) : null
|
||||||
|
}
|
||||||
|
|
||||||
export async function openNote(
|
export async function openNote(
|
||||||
app: App,
|
app: App,
|
||||||
item: ResultNote,
|
item: ResultNote,
|
||||||
@@ -8,6 +32,25 @@ export async function openNote(
|
|||||||
newPane = false,
|
newPane = false,
|
||||||
newLeaf = false
|
newLeaf = false
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
// We don't have a way to switch pages on a PDF view, so we must open a new pane for PDF results to trigger page navigation
|
||||||
|
// We should only trigger this behaviour if we know the page number for the result
|
||||||
|
// This code runs before the normal implementation because we don't want to trigger activation of an existing pane for this PDF and then open a new one on top
|
||||||
|
const isPdf = item.path.toLowerCase().endsWith('.pdf')
|
||||||
|
if (isPdf) {
|
||||||
|
const pdfPage = isPdf ? getPdfPageFromOffset(item.content, offset) : null
|
||||||
|
if (pdfPage !== null) {
|
||||||
|
// Obsidian also supports &selection= but this takes page content id references
|
||||||
|
const linkPath = `${item.path}#page=${pdfPage}`
|
||||||
|
|
||||||
|
await app.workspace.openLinkText(
|
||||||
|
linkPath,
|
||||||
|
'',
|
||||||
|
newLeaf ? 'split' : newPane
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check if the note is already open,
|
// Check if the note is already open,
|
||||||
// to avoid opening it twice if the first one is pinned
|
// to avoid opening it twice if the first one is pinned
|
||||||
let alreadyOpenAndPinned = false
|
let alreadyOpenAndPinned = false
|
||||||
@@ -25,7 +68,19 @@ export async function openNote(
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (!alreadyOpenAndPinned) {
|
if (!alreadyOpenAndPinned) {
|
||||||
// Open the note normally
|
// For PDFs, extract page number and append to path
|
||||||
|
// TODO if we knew the view type for PDF could we reuse an existing view?
|
||||||
|
let linkPath = item.path
|
||||||
|
|
||||||
|
if (isPdf && offset > 0) {
|
||||||
|
// If this PDF extract has page headings, use them
|
||||||
|
const pageNum = getPdfPageFromOffset(item.content, offset)
|
||||||
|
|
||||||
|
if (pageNum !== null) {
|
||||||
|
linkPath = `${item.path}#page=${pageNum}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await app.workspace.openLinkText(item.path, '', newLeaf ? 'split' : newPane)
|
await app.workspace.openLinkText(item.path, '', newLeaf ? 'split' : newPane)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user