victorsoares96 / epubjs-react-native

ePub.js Reader for React Native
MIT License
147 stars 49 forks source link

highlight text dynamically with span Id #222

Closed rashidwiizb closed 5 months ago

rashidwiizb commented 5 months ago

My EPUB CFI range looks like this: epubcfi(/6/4!/4,/4/2/2[_idTextSpan0001]/1:0 for each element. So, if I have dynamically assigned span IDs like _idTextSpan0001 or _idTextSpan0002, or whatever, I want the CFI range to contain that span ID, and I want to highlight that element. or curresponding elemt with cfirange contains that spanid

victorsoares96 commented 5 months ago

Could you provide me with a use case for what you want to do? Are you trying to start a book with notes already added to it?

rashidwiizb commented 5 months ago

I've been working on a feature to highlight text in a book reading app. Initially, I used a search function to find text dynamically from a JSON file and then highlight it. Now, I want to streamline this process by directly highlighting text that contains specific IDs provided in the JSON.

[ { "id": "_idTextSpan0001", "lines": "هَذِهِ\n\n...يَدي اليَمينُ" }, { "id": "_idTextSpan0002", "lines": "!وَهَذِهِ اليَسارُ" }, ... ]

Instead of searching, I want to use these IDs to highlight text directly. This approach will simplify the process and make it more efficient.

victorsoares96 commented 5 months ago

Got it, you want to highlight a text based on an id... I have been working on improvements in version 1.4.0 beta related to annotations, as soon as possible I will check the feasibility of your order!

rashidwiizb commented 5 months ago

Okay, so currently, there is no way to retrieve the contents or text of each page and highlight the text using span IDs or reference IDs.

victorsoares96 commented 5 months ago

Once I was trying to get the cfi of each element

of the book, I will try to recover this script to pass it to you. I believe he will help you.

rashidwiizb commented 5 months ago

I hope as soon as possible i will get the script because currently i am working on it.

victorsoares96 commented 5 months ago

I believe this will help you, use the latest available beta version of the library:

import * as React from 'react';
import { SafeAreaView, useWindowDimensions } from 'react-native';
import { Reader, ReaderProvider } from '@epubjs-react-native/core';
import { useFileSystem } from '@epubjs-react-native/expo-file-system';

export function JavascriptInjection() {
  const { width, height } = useWindowDimensions();
  return (
    <SafeAreaView>
      <ReaderProvider>
        <Reader
          src="https://s3.amazonaws.com/moby-dick/OPS/package.opf"
          width={width}
          height={height * 0.85}
          fileSystem={useFileSystem}
          injectedJavascript={`
            function highlightTextByTagId(tagId) {
              return Promise.all(book.spine.spineItems.map((item) => {
                return item.load(book.load.bind(book)).then(() => {
                  const element = item.document.getElementById('c001p0005');

                  if (!element) return null;

                  const cfi = item.cfiFromElement(element).split(')')[0].concat(',/1:0,/1:').concat(element.textContent.length).concat(')') ;
                  rendition.annotations.add('highlight', cfi);

                  console.log({
                    cfi,
                    text: element.textContent,
                    elements,
                  });

                  item.unload();
                  return Promise.resolve();
                });
              }));
            }

            highlightTextByTagId('c001p0005');
          `}
        />
      </ReaderProvider>
    </SafeAreaView>
  );
}

This script will look for an element within the book that has a certain tag and will mark the text using cfi

the text associated with the id is the one in the image below: image

rashidwiizb commented 5 months ago

The above script works and highlights the text element according to the corresponding span id, but some text elements with span ids don't work. injectedJavascript={ function highlightTextByTagId(tagId) { return Promise.all(book.spine.spineItems.map((item) => { return item.load(book.load.bind(book)).then(() => { const element = item.document.getElementById(tagId); if (!element) return null; const cfi = item.cfiFromElement(element).split(')')[0].concat(',/1:0,/1:').concat(element.textContent.length).concat(')'); rendition.annotations.add('highlight', cfi); console.log({ text: element.textContent, element, }); item.unload(); return Promise.resolve(); }); })); } highlightTextByTagId('_idTextSpan0001'); }

Here, I am using span id _idTextSpan0001, and this doesn't highlight the corresponding element, but span id _idTextSpan0002 works.

{ "page": "2", "startDelay": 3000, "endDelay": 6000, "data": [ { "begin": "0.000", "end": "1.960", "language": "arb", "lines": "هَذِهِ\n\n...يَدي اليَمينُ", "id": "_idTextSpan0001", "page": "2" }, { "begin": "1.960", "end": "3.800", "language": "arb", "lines": "!وَهَذِهِ اليَسارُ", "id": "_idTextSpan0002", "page": "2" }, { "begin": "3.800", "end": "4.280", "language": "arb", "lines": "..لا..لا", "id": "_idTextSpan0003", "page": "2" }, { "begin": "4.280", "end": "4.720", "language": "arb", "lines": "؟؟", "id": "_idTextSpan0004", "page": "2" }, { "begin": "4.720", "end": "6.160", "language": "arb", "lines": "..هَذِهِ اليَسارُ", "id": "_idTextSpan0005", "page": "2" }, { "begin": "6.160", "end": "6.520", "language": "arb", "lines": "..وَهَذِهِ اليَمينُ", "id": "_idTextSpan0006", "page": "2" } ], "isAudio": true }

Actually, this issue does not appear to be related to the book element; all text is highlighted using the above JSON span ids in the web application using the react-reader library.

Screenshot_1712561597

Also instaed of doing annotation can i do custome style for each span? I attach the book page html below

image

victorsoares96 commented 5 months ago

Did any of the ids containing the
tag work?

rashidwiizb commented 5 months ago

Yes some span with spanid containe br tag

victorsoares96 commented 5 months ago

At the moment it is not possible to change the style of a section or a specific element dynamically, annotations such as underline or highlight are svg elements attached to the book

victorsoares96 commented 5 months ago

I don't know how react-reader works very well but I think it allows you to manipulate the rendition directly... you can try to do the same thing here either using the injectJavascript method from the example above

debug the highlightTextBySpanId function and check if it can find the id _idTextSpan0001 in the html document

also try adding:

rendition.views().forEach(view => view.pane ? view.pane.render() : null);

this method will render the active view, maybe this will solve your problem with this id

rashidwiizb commented 5 months ago

So there is no way to inject a script to change color of text on corresponding element with spanid?

rashidwiizb commented 5 months ago

My opf file is : https://kutubiapp-blobdb.s3.ap-south-1.amazonaws.com/unzip/65d75f3e8a403d7e14b4fd74/OEBPS/content.opf

Actually the above script got the cfi range of span which contain br tag. But highlight is not work

Screenshot_1712647010

and the element outerhtml is:

Screenshot_1712647286

victorsoares96 commented 5 months ago

okay, i'll take a look

victorsoares96 commented 5 months ago

try this:

import * as React from 'react';
import { SafeAreaView, useWindowDimensions } from 'react-native';
import { Reader, ReaderProvider } from '@epubjs-react-native/core';
import { useFileSystem } from '@epubjs-react-native/expo-file-system';
import { Header } from './Header';

export function JavascriptInjection() {
  const { width, height } = useWindowDimensions();
  return (
    <SafeAreaView>
      <ReaderProvider>
        <Header />

        <Reader
          src="https://kutubiapp-blobdb.s3.ap-south-1.amazonaws.com/unzip/65d75f3e8a403d7e14b4fd74/OEBPS/content.opf"
          width={width}
          height={height * 0.85}
          fileSystem={useFileSystem}
          initialLocation="epubcfi(/6/4!/4/4/2/2[_idTextSpan0001]/1:0)"
          injectedJavascript={`
            function highlightTextByTagId(tagId) {
              return Promise.all(book.spine.spineItems.map((item) => {
                return item.load(book.load.bind(book)).then(() => {
                  const element = item.document.getElementById(tagId);

                  if (!element) return null;

                  const range = item.document.createRange();
                  range.selectNodeContents(element);

                  let textOffset = element.textContent.length;
                  if (element.childNodes.length > 1) {
                    const lastChildNode = element.childNodes[element.childNodes.length - 1];
                    textOffset = lastChildNode.textContent.length;
                  }

                  const cfi = item.cfiFromElement(element).split(')')[0].concat(',/1:0,/').concat(range.endOffset).concat(':').concat(textOffset).concat(')');
                  rendition.annotations.add('highlight', cfi);

                  item.unload();
                  return Promise.resolve();
                });
              }));
            }

            highlightTextByTagId('_idTextSpan0001');
          `}
          onWebViewMessage={(message) => console.log(message)}
        />
      </ReaderProvider>
    </SafeAreaView>
  );
}

image

rashidwiizb commented 5 months ago

Thank you, this works fine. However, I want to confirm whether I can move my application to production with this beta version "@epubjs-react-native/core": "1.4.0-beta.45". Will the injectJavaScript or any other property be deprecated or removed in future versions?

victorsoares96 commented 5 months ago

This props not will be removed

rashidwiizb commented 5 months ago

Do you know why i got this error while opening book?

domain undefineed error code: 1 WARN Encountered an error loading page {"canGoBack": false, "canGoForward": false, "code": -1, "description": "net::ERR_ACCESS_DENIED", "loading": false, "target": 5927, "title": "", "url": "file:///data/user/0/com.alrajhieducation.kutubiapp/filesindex.html"}

Screenshot_1712921643