diff --git a/epubcfi.js b/epubcfi.js index a046bab..77ea1b9 100644 --- a/epubcfi.js +++ b/epubcfi.js @@ -320,3 +320,16 @@ export const fake = { fromIndex: index => `/6/${(index + 1) * 2}`, toIndex: parts => parts?.at(-1).index / 2 - 1, } + +// get CFI from Calibre bookmarks +// see https://github.com/johnfactotum/foliate/issues/849 +export const fromCalibrePos = pos => { + const [parts] = parse(pos) + const item = parts.shift() + parts.shift() + return toString([[{ index: 6 }, item], parts]) +} +export const fromCalibreHighlight = ({ spine_index, start_cfi, end_cfi }) => { + const pre = fake.fromIndex(spine_index) + '!' + return buildRange(pre + start_cfi.slice(2), pre + end_cfi.slice(2)) +} diff --git a/reader.js b/reader.js index 18bc892..ad3cf9b 100644 --- a/reader.js +++ b/reader.js @@ -1,6 +1,7 @@ import './view.js' import { createTOCView } from './ui/tree.js' import { createMenu } from './ui/menu.js' +import { Overlayer } from '../foliate-js/overlayer.js' const isZip = async file => { const arr = new Uint8Array(await file.slice(0, 4).arrayBuffer()) @@ -152,6 +153,8 @@ class Reader { maxColumns: 2, maxColumnWidth: 720, } + annotations = new Map() + annotationsByValue = new Map() closeSideBar() { $('#dimming-overlay').classList.remove('show') $('#side-bar').classList.remove('show') @@ -237,6 +240,39 @@ class Reader { }) $('#toc-view').append(this.#tocView.element) } + + // load and show highlights embedded in the file by Calibre + const bookmarks = await book.getCalibreBookmarks?.() + if (bookmarks) { + const { fromCalibreHighlight } = await import('./epubcfi.js') + for (const obj of bookmarks) { + if (obj.type === 'highlight') { + const value = fromCalibreHighlight(obj) + const color = obj.style.which + const note = obj.notes + const annotation = { value, color, note } + const list = this.annotations.get(obj.spine_index) + if (list) list.push(annotation) + else this.annotations.set(obj.spine_index, [annotation]) + this.annotationsByValue.set(value, annotation) + } + } + this.view.addEventListener('create-overlay', e => { + const { index } = e.detail + const list = this.annotations.get(index) + if (list) for (const annotation of list) + this.view.addAnnotation(annotation) + }) + this.view.addEventListener('draw-annotation', e => { + const { draw, annotation } = e.detail + const { color } = annotation + draw(Overlayer.highlight, { color }) + }) + this.view.addEventListener('show-annotation', e => { + const annotation = this.annotationsByValue.get(e.detail.value) + if (annotation.note) alert(annotation.note) + }) + } } setAppearance = () => { this.view?.setAppearance({ css: getCSS(this.style), layout: this.layout })