Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions src/format.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
/** Supernote internal link (created via the Supernote link feature). */
export interface ILink {
/** Link type. 1 = internal note link. */
/** Link type. Meaning unconfirmed: doesn't reliably distinguish real
* internal-note links from other kinds, e.g. real device-created notes
* have had genuine links with `LINKTYPE: '0'`. Every entry present in
* `SupernoteX.links` has already been resolved as a real link regardless
* of this value, so it isn't a "is this a real link" filter. See
* https://github.com/philips/supernote-typescript/issues/32. */
LINKTYPE: string;
LINKINOUT: string;
/** Address of the link bitmap. */
Expand All @@ -16,7 +21,15 @@ export interface ILink {
LINKFILE: string;
LINKFILEID: string;
PAGEID: string;
/** 0-indexed page number this link appears on. */
/** NOT reliably the page this link is drawn on, despite the name --
* verified against a real device-created note where it didn't match the
* link's actual source page under any indexing convention. Best guess is
* something closer to the source page's own template/display label,
* which can drift from that page's current position in `pages` (e.g.
* after reordering), but that's unconfirmed. To find which page a link
* actually appears on, use the first 4 characters of its key in the
* `SupernoteX.links` Record (see `_parseLinks` in parsing.ts) instead.
* See https://github.com/philips/supernote-typescript/issues/32. */
OBJPAGE: string;
/** Decoded filename (without path or .note extension) ready for use as [[link]]. */
text: string;
Expand Down
14 changes: 13 additions & 1 deletion src/parsing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -654,7 +654,19 @@ export class SupernoteX implements ISupernote {
return this.titles;
}

/** Parse links from Supernote file's buffer contents. */
/** Parse links from Supernote file's buffer contents.
* The returned Record's keys (taken as-is from the file's own `LINKO_*`
* footer keys) aren't just unique identifiers: their first 4 characters,
* read as an integer, are the 1-indexed page the link is actually drawn
* on -- verified against real device-created notes, and the only
* reliable way to find a link's source page (`ILink.OBJPAGE` isn't; see
* its doc comment). The remaining characters are the link's own
* `LINKRECT` `y,x,h,w` digits, zero-padded to 4 each and concatenated --
* cosmetic, just enough to keep keys with links on the same page
* distinct. E.g. key `"00021253011801320743"` for a link on page 2 with
* `LINKRECT: "118,1253,743,132"` (`x,y,w,h`): `0002` + `1253` (y) +
* `0118` (x) + `0132` (h) + `0743` (w).
* See https://github.com/philips/supernote-typescript/issues/32. */
_parseLinks(buffer: Uint8Array): Record<string, ILink[]> {
this.links = {};
Object.entries(this.footer.LINKO).forEach(([key, value]) => {
Expand Down
19 changes: 19 additions & 0 deletions tests/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,25 @@ describe("links", () => {
expect(crossFileNoPage).toBeDefined()
expect(crossFileNoPage!.text).toBe("nomad-3.26.40-blank-2p")
})

test("the links Record key's first 4 characters are the 1-indexed source page, not OBJPAGE", async () => {
let sn = new SupernoteX(await readFileToUint8Array("nomad-3.26.40-link-tag-3p.note"))
// All 3 links in this fixture are physically drawn on the same page
// (source page array-index 1, i.e. page 2), so every key shares that
// page's 1-indexed prefix, "0002" -- see _parseLinks()'s doc comment.
const keys = Object.keys(sn.links)
expect(keys.length).toBeGreaterThan(0)
for (const key of keys) {
expect(key.slice(0, 4)).toBe("0002")
}
// OBJPAGE is NOT a reliable stand-in for the key prefix: these 3 links
// share one source page yet have 3 different OBJPAGE values, which is
// exactly what makes it unreliable (see ILink.OBJPAGE's doc comment and
// https://github.com/philips/supernote-typescript/issues/32).
const allLinks = Object.values(sn.links).flat()
const objPages = new Set(allLinks.map(l => l.OBJPAGE))
expect(objPages.size).toBeGreaterThan(1)
})
})

describe("digest_image", () => {
Expand Down