readium / readium-js-viewer

👁 ReadiumJS viewer: default web app for Readium.js library
BSD 3-Clause "New" or "Revised" License
551 stars 186 forks source link

Unable to highlight text fragments inside the "epubContentIframe" #278

Closed rkwright closed 9 years ago

rkwright commented 9 years ago

Submitted by Lars Voigt: lars.voigt@dzb.de

Platform/Browser: CloudReader, All platforms.

I try to highlight text fragments (single words) inside the "epubContentIframe".

$("#epubContentIframe").contents().find('body').highlight("text");

I use the jQuery highlight plugin and I think it works fine. Because I have debug the iframe dom manipulation and it looks good. But at the viewport I can see that the text should be highlighted is lost.

Should I do more to update the viewport? Or there a cleaner way to do that? Readium-JS API?

danielweck commented 9 years ago

@larsvoigt the jQuery highlight plugin inserts spans in the markup in order to "select" character ranges. These spans are not blacklisted by the CFI backend, so this is a strongly discouraged practice (e.g. it potentially breaks bookmarks, etc,). So, ideally you would be able to customise the CFI blacklists with your own "highlight" class, but as it turns out there is no easy way to do this (plus, there are blacklist API "holes" in the current Readium codebase). More information here: https://github.com/readium/readium-cfi-js/issues/30

What you could do is use Readium's existing highlighting mechanism, which does not rely on additional / interspersed span markup. Instead, semi-transparent divs are overlaid on top of the document text (so styling options are limited to an overlay colour), and the positions/dimensions are updated automatically when the page is reflowed (window resized, font enlarged, etc.). See addHighlight(CFI, id, type, styles): https://github.com/readium/readium-shared-js/blob/develop/lib/annotations_module.js#L565 Accessible via the Reader API: https://github.com/readium/readium-shared-js/blob/develop/js/views/reader_view.js#L1197 ...this requires building a CFI expression that represents a single search result:

var cfi = EPUBcfi.Generator.generateCharOffsetRangeComponent(
startTextNode, 
charPosInStartTextNode, 
endTextNode, 
charPosInEndTextNode
blacklistClass,
blacklistElement,
blacklistId,
);

_reader.addHighlight(
SPINE_ID,
cfi,
HIGHLIGHT_ID,
"highlight", //"underline"
undefined // styles
);

Alternatively, you could highlight the browser active text selection for each search result: addSelectionHighlight(id, type) https://github.com/readium/readium-shared-js/blob/develop/js/views/reader_view.js#L1210

larsvoigt commented 9 years ago

Thank you @danielweck for your fast response and help. The facts sound good. I will give you feedback as far as possible.

danielweck commented 9 years ago

Using the new plugins architecture:

https://github.com/readium/readium-js/blob/develop/dev/index.js#L109

            ReadiumSDK.on(ReadiumSDK.Events.PLUGINS_LOADED, function(reader) {

                reader.plugins.annotations.initialize({annotationCSSUrl: readerOptions.annotationCSSUrl});

                reader.plugins.annotations.on("annotationClicked", function(type, idref, cfi, id) {
                    console.log("ANNOTATION CLICK: " + id);
                    reader.plugins.annotations.removeHighlight(id);
                });

                reader.plugins.annotations.on("textSelectionEvent", function() {
                    console.log("ANNOTATION SELECT");
                    reader.plugins.annotations.addSelectionHighlight(Math.floor((Math.random()*1000000)), "highlight");
                });

            });
rkumar2236 commented 8 years ago

I am able to highlight text in reflowable epubs but not for fixed layout, please help.

Bahar1978 commented 8 years ago

Dear Daniel, I can highlight selected text, but not save or store in Readium. I have been working on a way to somehow save my annotations in a separate file (in a right position using CFI ) that can be shown as a layer on top of each visible content of the epub3-based book in Readium. Also what is the best way to pass these annotated data to an existing file in Readium (or and DB in Readium)? Or create a new epub3 file consisting just annotations?

Could you please let me know what is your suggestion?

Thanks in advance, Kind regards, Hajar

danielweck commented 8 years ago

At this stage there is no proper built-in "annotation" feature in Readium. You would need to extend the "highlight" plugin, or even better: create a new plugin that would implement a persistence backend (save to / load from IndexedDB, LocalStorage etc.) and you would also need a front-end to handle user interaction / edits (delete annotation, change text, etc.) This project is very keen to receive open-source contributions, so let us know if you would like to participate.

Bahar1978 commented 8 years ago

Thank you very much Daniel. We are interested in contributing to this project. Please provide me more information. We have been also working on the way to make content safe (disable copy paste or save book content) something like https://read.amazon.com/ Could you please let me know if yo have started this project? I saw somewhere else a comment that Readium is going to make the highlight feature by the end of the year (2016), is this right?

Thanks in advance, Kind regards, Hajar

danielweck commented 8 years ago

Hello, regarding your potential open source contributions, please ask Ric Wright @rkwright Regarding highlights, the plugin is available in the develop branch of readium-shared-js, but it is still experimental in the sense that it hasn't been tested enough to be considered production-ready. We are not able to commit to a firm deadline because this is an open source project mostly run by volunteers. We are aware that commercial vendors who use Readium as the foundation / engine for their reading systems usually implement their own annotation framework as "value add" / unique product selling point (when that's the case, the code is likely to remain proprietary).

danielweck commented 8 years ago

Regarding copy/paste disabling, this could be a cross-platform generic readium-shared-js plugin, and native apps developers could choose to use their own solution if they wanted to. We are aware that existing Readium-based vendor apps implemented their own protection measures to secure the contents. No open-source code as far as I know, but it probably is pretty trivial JavaScript event handling (preventDefault()).

Bahar1978 commented 7 years ago

Hi @danielweck and @larsvoigt I have been working on highlighting text based on CFI. I could integrate epub-search engin in Readim, as search function. Now I can extract CFI of a search keyword in json format. Now I need to highlight my search keyword in the readium. So it is not based on selected text. The only guidance that I could find is https://docs.google.com/document/d/1O8V0C2XaynobpDApnQvGcfgE4aCDj7BiZ5W-4eOupIk/edit#heading=h.nlvujjtmpl2m. But I could not use it to highlight my searched keyword. For example my extracted information in jsn format is: { "filename" : "accessible_epub_3", "epubTitle" : "Accessible EPUB 3", "href" : "ch01.xhtml", "baseCfi" : "/6/16[id-id2611884]!", "id" : "id-id2611884", "cfis" : ["/6/16[id-id2611884]!/4/2[introduction]/4,/1:22,/1:23"] } and I tried to work on cloud-reader. Could you please let me know how should be the right code in order to highlight my example: I tried to adopt "S.reader.plugins.highlights.addSelectionHighlight(Math.floor(1e6 * Math.random()), "test-highlight")" from " r(".icon-annotations").on("click", function () { S.reader.plugins.highlights.addSelectionHighlight(Math.floor(1e6 * Math.random()), "test-highlight") //S.reader.plugins.highlights.addSelectionHighlight('id-id2611884', "/4,/1:22,/1:23", 123, "highlight"); });"

But my problem is how do I need to make a link between Math.floor(1e6 * Math.random()) and 'id-id2611884', "/4,/1:22,/1:23", 123, Or if you could propose me know other ways!

danielweck commented 7 years ago

@hajarghaem you want to use addHighlight(), not addSelectionHighlight(). See function parameters: https://github.com/readium/readium-shared-js/blob/develop/plugins/highlights/manager.js#L155 ...and for your information, here is the function logic for a given spine item document: https://github.com/readium/readium-shared-js/blob/develop/plugins/highlights/controller.js#L151

PS: the Math.random() call creates some kind of unique identifier for the newly-created highlight (see the id parameter of both "add highlight" functions).

Bahar1978 commented 7 years ago

@danielweck Thank you very much Daniel. When use "S.reader.plugins.highlights.addHighlight("/4/2/1:22,/1:23", 'id-id2611884', 123, "test-highlight");" inside: r(".icon-annotations").on("click", function () { //S.reader.plugins.highlights.addSelectionHighlight(Math.floor(1e6 * Math.random()), "highlight") S.reader.plugins.highlights.addHighlight("/4/2/1:22,/1:23", 'id-id2611884', 123, "highlight"); }); It does not annotate the text.

Could you please let e know if I need to change other parts? Thanks in advance, Hajar

Bahar1978 commented 7 years ago

Hello @danielweck,

I tried to enable or disable addSelectionHighlight function via "manager.js and/or controller.js", but nothing changed. It seems that there is not connection between "controller.js and manager.js" with cloud-reader (readium-js-viewer_all.js). I can controller my annotation when I use "npm run http", but not when I use "node node_modules/http-server/bin/http-server -a 127.0.0.1 -p 8080 -c-1 .."

danielweck commented 7 years ago

hello, did you build readium-js-viewer from source, and from the develop branch? remember, you need to ensure that the configuration in readium-shared-js/plugins/plugins.cson includes the highlighter plugin.

Bahar1978 commented 7 years ago

I followed instruction "https://github.com/readium/readium-js-viewer". as I said I can control when I use "npm run http". But I cannot control highlights in cloud reader.

danielweck commented 7 years ago

what branch? what is your plugins.cson configuration?

Bahar1978 commented 7 years ago

my plugins.cson includes:

You may add the plugins that should be included here.

plugins: [

'highlights'

'example'

]

you may override this configuration by creating a file called plugins-override.cson, e.g.:

plugins:

include: [

'example'

]

exclude: [

'highlights'

]

danielweck commented 7 years ago

# the sharp character indicates a comment in the CSON syntax. you must uncomment the line for the highlights plugin. then use "npm run http" to try the cloud reader from the raw source tree, or "npm run dist+sourcemap" to build the app so you can upload it to your HTTP server.

danielweck commented 7 years ago

PS: the highlighter plugin does just that: it highlights text selections. you must implement your own logic on top of this basic functionality, for example to support annotations.

Bahar1978 commented 7 years ago

Thanks I try your solution and then update you. For the highlight, I extract CFI via epub-search engine ( fetch this information in a json file). Then in the cloud-reader I added an even in order to activate addHighlight function with my CFI information. Something like we have already had for addSelectionHighlight() in: r(".icon-annotations").on("click", function () { S.reader.plugins.highlights.addSelectionHighlight(Math.floor(1e6 * Math.random()), "highlight") });

danielweck commented 7 years ago

When you add your code, do you edit the original (raw) files in the source tree, or do you patch the JavaScript bundles created by RequireJS (build-ouput folder)? Or, do you just add JS code in reader.html?

My recommendation is to customise the original codebase, iteratively test with "npm run http", and compile the final application with "npm run dist+sourcemap" (to deploy / upload to a real HTTP server)

Bahar1978 commented 7 years ago

@danielweck The readium-js-viewer_all.js is created after "nppm run dist". Could you please let me know where can I add my code (instead of readium-js-viewer_all.js) before building dist?

danielweck commented 7 years ago

Obviously, the answer to your question depends on what you are trying to achieve, but assuming you want to add behaviours after a document (spine item) is loaded and paginated / rendered, then a good starting point would be listen to the PAGINATION_CHANGED event, as done here in the "reader" view:

readium.reader.on(ReadiumSDK.Events.PAGINATION_CHANGED, function (pageChangeData) {
...

https://github.com/readium/readium-js-viewer/blob/develop/src/js/EpubReader.js#L451

Bahar1978 commented 7 years ago

I added my codes in different files: src/css/viewer.css , \src\templates\reader-navbar.html, ...

Bahar1978 commented 7 years ago

Hi @danielweck and @larsvoigt , I integrated epub-search in cloud-reader (from the original (raw) files in the source tree ). I also can simply highlight my search keywords. There are still an issue: 1- this is related to PAGINATION_CHANGED. When I am in a page that includes my search word then I can it is highlighted but not other same words that are in other pages (not current page). I have been looking for a way to highlight my search words even in other pages (not current visible page).

Thanks in advance, Hajar

danielweck commented 7 years ago

@hajarghaem "pages" for reflowable documents, or fixed layout? If fixed-layout, then of course only the visible page (or two pages) can display highlights. If reflowable documents, then there may be several "pages" (CSS columns) within the same spine item document, and any number of text highlights can be created, even in pages that are not visible.

larsvoigt commented 7 years ago

@hajarghaem.

// open the "page" with the cfi of your keyword
readium.reader.openSpineItemElementCfi(...); 
// see daniels hint
readium.reader.on(ReadiumSDK.Events.PAGINATION_CHANGED, function (pageChangeData) {
...
hightlight(cfi)
});
danielweck commented 7 years ago

@hajarghaem , as @larsvoigt said, you can navigate to any CFI location using readium.reader.openSpineItemElementCfi(), or at the time the EPUB is loaded. See here for a more complete answer: https://github.com/readium/readium-js/issues/143

Bahar1978 commented 7 years ago

Thanks @danielweck regarding you previous comment "If reflowable documents, then there may be several "pages" (CSS columns) within the same spine item document, and any number of text highlights can be created, even in pages that are not visible." When I use "SCROLL MODE: Continuous" then I cannot highlight anything! Even with the simple highlight button? I can highlight in reflowable document, the only issue is when I change the SCROLL mode to Continuous, then it cannot highlight.

danielweck commented 7 years ago

Yes, unfortunately there is a bug preventing the highlights plugin to function properly in the scroll and fixed layout views. See: https://github.com/readium/readium-shared-js/issues/280 and https://github.com/readium/readium-shared-js/issues/315

Bahar1978 commented 7 years ago

Hi @danielweck, Thanks for your time and responses. (goal: Dynamic table of content based on search result) I am going to create a table of content based on my search results. I tried different files (e.g., package_document.js, manifest.js, content_document_fetcher.js ) and functions (e.g., generateTocListDOM) to change the order of table of content based on my conditions. But I just could see some changes when I applied my code in "EpubReader.js" in "$('#readium-toc-body').on('click', 'a', function(e) { ..." . In addition, adding some codes to (package_document.js) in (resourceFetcher.relativeToPackageFetchFileContents(toc, 'text', function (tocDocumentText) { ... ) lets me update the "tocDocumentText" and thus updating the TOC. But obviousley i I have to find a way to make it specific for my Search-TOC and not the Book-TOC. SO in this sptep I have to find a way to enable (package_document.js) to create a second TOC based on my search result. I have to know how I can control TOC (control package_document.js in epubreader.js), please help on this.

Could you please let me know if there is a better way to change the order of table of content, and linking the TOC and their corresponding html page of the book? Thanks in advance!