Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extracting the text per rendered section / block using epub js? #1376

Open
BrendanCook44 opened this issue Apr 1, 2024 · 29 comments
Open

Extracting the text per rendered section / block using epub js? #1376

BrendanCook44 opened this issue Apr 1, 2024 · 29 comments

Comments

@BrendanCook44
Copy link

Hello fellow epub js programmers! My current goal is to be able to extract the text that is being displayed from the render. Using the default render method, there is typically 2 different "blocks" being rendered --- one on the left and one on the right. This changes depending on the device/scaling. Is there a way I can separate the extracted text out into 2 separate variables for each rendered block of text? And if it is the case that the device screen/scaling is small enough to only be displaying one rendered "block", then the 2nd variable would not contain any text. Any ideas on how this could be implemented? Thanks!

I have attached an image with 2 highlighted boxes in red, showing my desired functionality. Thanks!

Per Page Section Block Text Extraction

@johnfactotum
Copy link
Contributor

@BrendanCook44
Copy link
Author

Thank you for providing that documentation. After looking it over, it looks like I need the current render instance to hook in to some of these items (like the layout) --- Would I be able to utilize my current epub rendition object to "hook in" and obtain all of the info necessary to form that mapping construction and extract the text sections? I'm just trying to find the "tie-in" piece between my current rendition object and making the mapping to extract from.

Thank you for your time.

@johnfactotum
Copy link
Contributor

The mapping object is available from rendition.manager.mapping. I believe it's used by the manager to get the current location in paginatedLocation(). You'd need to something similar to what the manager does here, except you'd need to calculate the position of the center of the view and use that as the start/end offset.

Also worth noting that Epub.js calculates the ranges by splitting the text by the space character which could be problematic when the columns ("pages") start or end on hyphens or other none-white-space characters.

@BrendanCook44
Copy link
Author

BrendanCook44 commented Apr 6, 2024

Ok, I think I have a function up and running --- If I wanted to extend the DefaultViewManager class to include this new function in my component, do you know what the import path would be for class extension?

This is a rough implementation:

centeredLocation() {
    let visible = this.visible();
    let container = this.container.getBoundingClientRect();

    let left = 0;
    let used = 0;

    if(this.settings.fullsize) {
        left = window.scrollX;
    }

    let sections = visible.map((view) => {
        let {index, href} = view.section;
        let offset;
        let position = view.position();
        let width = view.width();

        // Find mapping
        let start;
        let end;
        let pageWidth;

        // Calculate the center of the view
        let center = position.left + width / 2;

        if (this.settings.direction === "rtl") {
            offset = container.right - left;
            pageWidth = Math.min(Math.abs(offset - center), this.layout.width) - used;
            end = position.width - (position.right - offset) - used;
            start = end - pageWidth;
        } else {
            offset = container.left + left;
            pageWidth = Math.min(center - offset, this.layout.width) - used;
            start = offset - position.left + used;
            end = start + pageWidth;
        }

        used += pageWidth;

        let mapping = this.mapping.page(view.contents, view.section.cfiBase, start, end);

        let totalPages = this.layout.count(width).pages;
        let startPage = Math.floor(start / this.layout.pageWidth);
        let pages = [];
        let endPage = Math.floor(end / this.layout.pageWidth);
        
        // start page should not be negative
        if (startPage < 0) {
            startPage = 0;
            endPage = endPage + 1;
        }

        // Reverse page counts for rtl
        if (this.settings.direction === "rtl") {
            let tempStartPage = startPage;
            startPage = totalPages - endPage;
            endPage = totalPages - tempStartPage;
        }

        for (var i = startPage + 1; i <= endPage; i++) {
            let pg = i;
            pages.push(pg);
        }

        return {
            index,
            href,
            pages,
            totalPages,
            mapping
        };
    });

    return sections;
}

It's also worth noting while digging through the manager that I saw isVisible() / visible() functions --- I'm wondering if the center could also be calculated by using these functions to divide the visible page length by 2.

You've been very helpful, thanks!

@johnfactotum
Copy link
Contributor

If I wanted to extend the DefaultViewManager class to include this new function in my component, do you know what the import path would be for class extension?

Don't know. Extending it could be problematic. I would just do it in the application code.

It's also worth noting while digging through the manager that I saw isVisible() / visible() functions --- I'm wondering if the center could also be calculated by using these functions to divide the visible page length by 2.

Not sure what you mean, but those only check the visibility of "views", which are essentially iframes, each of which would be a chapter. Looking at the code, it seems to me the "visible page length" you want is the pageWidth in paginatedLocation() and not width, which would be the width of the whole iframe.

@BrendanCook44
Copy link
Author

BrendanCook44 commented Apr 9, 2024

Could you clarify what you mean by "do it in the application code?" Just trying to see how I would be able to add that function.

Right now, I'm importing epub js via npm install node module and interacting with the epub object via the import line.
If I wanted access to the manager and mapping like you mentioned, I can get access to it via epub.rendition.manager.mapping

and if I wanted to call paginatedLocation(), I would get there through rendition.manager.paginatedLocation()

If I wanted to change this statement to rendition.manager.centeredLocation(), how would that be achieved in the style you described without extending the module?

@johnfactotum
Copy link
Contributor

I mean doing it outside Epub.js. If all the properties are public and accessible from the rendition object, you don't need to have manager.centeredLocation(); instead it would be easier to have something like MyApp.getCenteredLocation().

Indeed you don't have to make use of the manager at all. You can get the start and end CFIs from rendition.reportLocation(), create a Range object for the whole visible spread, and then process the nodes in that Range yourself.

If you really need to extend the Manager, well, currently only the following classes are exported: Book, EpubCFI, Rendition, Contents, Layout. So doesn't seem possible with the current API. Though you can perhaps use rendition.manager.constructor if you want.

@BrendanCook44
Copy link
Author

BrendanCook44 commented Apr 12, 2024

Looks like some of the rendition properties are not set to public, I may have to go with trying the second option.
image

Code for reference:

import { Rendition, Location} from "epubjs";

class CustomRender {
    public rendition: Rendition

    constructor(rendition: Rendition) {
        this.rendition = rendition
    }

    getCenteredLocation(): Location[] {
        let visible = this.rendition.manager.visible();
        let container = this.rendition.manager.container.getBoundingClientRect();
        let left = 0;
        let used = 0;

        if(this.rendition.settings.fullsize) {
            left = window.scrollX;
        }

        let sections = visible.map((view) => {
            let {index, href} = view.section;
            let offset;
            let position = view.position();
            let width = view.width();

            // Find mapping
            let start;
            let end;
            let pageWidth;

            // Calculate the center of the view
            let center = position.left + width / 2;

            if (this.rendition.settings.direction === "rtl") {
                offset = container.right - left;
                pageWidth = Math.min(Math.abs(offset - center), this.rendition.layout.width) - used;
                end = position.width - (position.right - offset) - used;
                start = end - pageWidth;
            } else {
                offset = container.left + left;
                pageWidth = Math.min(center - offset, this.rendition.layout.width) - used;
                start = offset - position.left + used;
                end = start + pageWidth;
            }

            used += pageWidth;

            let mapping = this.rendition.manager.mapping.page(view.contents, view.section.cfiBase, start, end);

            let totalPages = this.rendition.layout.count(width).pages;
            let startPage = Math.floor(start / this.rendition.layout.pageWidth);
            let pages = [];
            let endPage = Math.floor(end / this.rendition.layout.pageWidth);
            
            // start page should not be negative
            if (startPage < 0) {
                startPage = 0;
                endPage = endPage + 1;
            }

            // Reverse page counts for rtl
            if (this.rendition.settings.direction === "rtl") {
                let tempStartPage = startPage;
                startPage = totalPages - endPage;
                endPage = totalPages - tempStartPage;
            }

            for (var i = startPage + 1; i <= endPage; i++) {
                let pg = i;
                pages.push(pg);
            }

            return {
                index,
                href,
                pages,
                totalPages,
                mapping
            };
        });

        return sections;
    }
}

@johnfactotum
Copy link
Contributor

I just noticed that there's actually rendition.requireManager(), which is public, and would allow you to access the manager class.

@BrendanCook44
Copy link
Author

BrendanCook44 commented Apr 12, 2024

Interesting find, it looks like it accepts a multitude of different arguments as inputs, I saw options for strings / functions / objects --- I'm wondering if something like "default" or "DefaultViewManager" would be the appropriate option to use here.
image

EDIT: I did find the code for this function within the import module by digging through it and screenshotted it here. I'm wondering if I can accomplish having access to the manager with lines such as these:

        let manager = this.rendition.requireManager("default");
        let visible = manager.visible();

@BrendanCook44
Copy link
Author

From the previous edit, if that function does what I think it does, I have edited the function to be able to access the properties. That just leaves some of the layout properties that are inaccessible from the rendition property. Would I be able to use something similar with the manager to be able to access these properties?

Something like manager.layout.width

Instead of this.rendition.layout.width

image

@johnfactotum
Copy link
Contributor

let manager = this.rendition.requireManager("default");
let visible = manager.visible();

No, here manager is a function, the class, not the instance, and you can extend it if you want:

class MyManager extends rendition.requireManager('default') {
    getCenteredLocation() {
    }
}

To actually use this, you need to pass this class as the manager option when rendering. That is

let rendition = book.renderTo('viewer', { manager: MyManager })

Or you might be able to use rendition.setManager() to do the same.

@BrendanCook44
Copy link
Author

BrendanCook44 commented Apr 17, 2024

Thank you for that suggestion. Here is a new version, not creating a CustomRender but creating a CustomManager:

import { Rendition } from "epubjs";

export class CustomManager extends Rendition.requireManager("default"){

    getCenteredLocation() {
        let visible = this.rendition.manager.visible();
        let container = this.rendition.manager.container.getBoundingClientRect();
        let left = 0;
        let used = 0;

        if(this.rendition.settings.fullsize) {
            left = window.scrollX;
        }

        let sections = visible.map((view) => {
            let {index, href} = view.section;
            let offset;
            let position = view.position();
            let width = view.width();

            // Find mapping
            let start;
            let end;
            let pageWidth;

            // Calculate the center of the view
            let center = position.left + width / 2;

            if (this.rendition.settings.direction === "rtl") {
                offset = container.right - left;
                pageWidth = Math.min(Math.abs(offset - center), this.rendition.layout.width) - used;
                end = position.width - (position.right - offset) - used;
                start = end - pageWidth;
            } else {
                offset = container.left + left;
                pageWidth = Math.min(center - offset, this.rendition.layout.width) - used;
                start = offset - position.left + used;
                end = start + pageWidth;
            }

            used += pageWidth;

            let mapping = this.rendition.manager.mapping.page(view.contents, view.section.cfiBase, start, end);

            let totalPages = this.rendition.layout.count(width).pages;
            let startPage = Math.floor(start / this.rendition.layout.pageWidth);
            let pages = [];
            let endPage = Math.floor(end / this.rendition.layout.pageWidth);
            
            // start page should not be negative
            if (startPage < 0) {
                startPage = 0;
                endPage = endPage + 1;
            }

            // Reverse page counts for rtl
            if (this.rendition.settings.direction === "rtl") {
                let tempStartPage = startPage;
                startPage = totalPages - endPage;
                endPage = totalPages - tempStartPage;
            }

            for (var i = startPage + 1; i <= endPage; i++) {
                let pg = i;
                pages.push(pg);
            }

            return {
                index,
                href,
                pages,
                totalPages,
                mapping
            };
        });

        return sections;
    }
}

Here is my implementation:

useEffect(() => {
    if (book) {
      if (!rendition) {
        const rend = book.renderTo("viewer", { width: "95%", height: "95%", manager: CustomManager });
        setRendition(rend);
        rend.display();
  
        rend.on('relocated', async () => {
          const sections = rend.manager.getCenteredLocation();
          const centerSection = sections[Math.floor(sections.length / 2)];
          const centerPosition = centerSection.mapping.start;
  
          const mapping = rend.manager.mapping;
  
          // Use the center position as the start/end offset to get the current page's CFI
          // const pageCfiPair = mapping.page(rend.getContents(), centerPosition, 0, 0);
  
          // // Get the range of the current page
          // const range = await rend.getRange(pageCfiPair.start, pageCfiPair.end);
  
          // // Extract the text from the range
          // const text = range.toString();
  
          // console.log(text);

          //const percentage = parseFloat((book.locations.percentageFromCfi(location.start.cfi) * 100).toFixed(2));
          //setProgression(percentage);
        });
  
        book.loaded.navigation.then((navigation: { toc: React.SetStateAction<any[]> }) => {
          setToc(navigation.toc);
        });
      }
    }

When running, it seems the application is returning the following error code:
epubjs__WEBPACK_IMPORTED_MODULE_0__.Rendition.requireManager is not a function

I wonder if this is because we are required to use this in an instance instead of a static context. That would explain why we couldn't call requireManager("default") on the Rendition class itself. One possible solution would be to try to create an instance of Rendition within CustomManager() and call requireManager() on that instance. The challenge would be having access to the same functions as the default manager in this case.

@BrendanCook44
Copy link
Author

I found another interesting solution here to use Object.setPrototypeOf, which I think means that CustomManager will have all the methods of the default manager, and you can override or add additional methods as needed.

image

@johnfactotum
Copy link
Contributor

I wonder if this is because we are required to use this in an instance instead of a static context.

Yeah, it's not a static method, so you have to create an instance first (then create your custom class, and then create the actual instance that you're going to use). Which makes no sense (see for instance #966), but that's the way it is now.

@BrendanCook44
Copy link
Author

Would you be able to provide me with an example of what that looks like from an initialization standpoint? I dont mind the workaround, as long as it works.

Also side note, do you know if this library is still being actively developed / updated?

@johnfactotum
Copy link
Contributor

Something like:

import { Rendition, Book } from "epubjs";
const DefaultViewManager = new Rendition(new Book()).requireManager("default");
class CustomManager extends DefaultViewManager {
}

Also side note, do you know if this library is still being actively developed / updated?

I believe it's still developed. There were some new commits in 2022-restructure branch as recent as 7 months ago, for example. Just very slowly and there hasn't really been that much activity.

@BrendanCook44
Copy link
Author

Thank you for that clarification and all of the help you have provided so far.

With that new implementation, would I use this line as if it was a static class?
const rend = book.renderTo("viewer", { width: "95%", height: "95%", manager: CustomManager });

or these lines to setup an instance:

const rend = book.renderTo("viewer", { width: "95%", height: "95%"});
const manager = new CustomManager(rend);
rend.setManager(manager);

@johnfactotum
Copy link
Contributor

With that new implementation, would I use this line as if it was a static class?

Yes, I think that's the way.

or these lines to setup an instance:

setManager() probably won't work. Actually I'm confused about this method. All it does is this.manager = manager. So it doesn't actually do anything. It seems to be incomplete and missing what is actually necessary for setting up the manager:

epub.js/src/rendition.js

Lines 229 to 234 in f09089c

this.manager = new this.ViewManager({
view: this.View,
queue: this.q,
request: this.book.load.bind(this.book),
settings: this.settings
});

@BrendanCook44
Copy link
Author

Thanks for that heads up, yeah that's a bit misleading. For testing my application, I'm going to focus on just getting the render to accept the custom manager at the moment w/ all inherited properties from default manager, and not worry so much about the new function for now. Once I can get the render up and running with the new manager, then I should be good to put focus back on the custom function.

@BrendanCook44
Copy link
Author

That did the trick! Big step forward in getting the render to utilize the custom manager instead of the default manager. Now, I can just focus on the implementation of the getCenteredLocation() function to extract the text per section

@BrendanCook44
Copy link
Author

BrendanCook44 commented Apr 27, 2024

Moving on to the implementation of getCenteredLocation(), I think some of the code will need to be changed to reflect being an extension of DefaultViewManager. For instance, anything that says "this.rendition.manager" should be replaced with "this" , or anynthing with "this.rendition" to "this"

export class CustomManager extends DefaultViewManager {

    getCenteredLocation() {
        let visible = this.visible();
        let container = this.container.getBoundingClientRect();
        let left = 0;
        let used = 0;
    
        if(this.settings.fullsize) {
            left = window.scrollX;
        }
    
        let sections = visible.map((view) => {
            let {index, href} = view.section;
            let offset;
            let position = view.position();
            let width = view.width();
    
            // Find mapping
            let start;
            let end;
            let pageWidth;
    
            // Calculate the center of the view
            let center = position.left + width / 2;
    
            if (this.settings.direction === "rtl") {
                offset = container.right - left;
                pageWidth = Math.min(Math.abs(offset - center), this.layout.width) - used;
                end = position.width - (position.right - offset) - used;
                start = end - pageWidth;
            } else {
                offset = container.left + left;
                pageWidth = Math.min(center - offset, this.layout.width) - used;
                start = offset - position.left + used;
                end = start + pageWidth;
            }
    
            used += pageWidth;
    
            let mapping = this.mapping.page(view.contents, view.section.cfiBase, start, end);
    
            let totalPages = this.layout.count(width).pages;
            let startPage = Math.floor(start / this.layout.pageWidth);
            let pages = [];
            let endPage = Math.floor(end / this.layout.pageWidth);
            
            // start page should not be negative
            if (startPage < 0) {
                startPage = 0;
                endPage = endPage + 1;
            }
    
            // Reverse page counts for rtl
            if (this.settings.direction === "rtl") {
                let tempStartPage = startPage;
                startPage = totalPages - endPage;
                endPage = totalPages - tempStartPage;
            }
    
            for (var i = startPage + 1; i <= endPage; i++) {
                let pg = i;
                pages.push(pg);
            }
    
            return {
                index,
                href,
                pages,
                totalPages,
                mapping
            };
        });
    
        return sections;
    }
}

@BrendanCook44
Copy link
Author

BrendanCook44 commented May 1, 2024

Another update:

More good progress. Got it to start the application correctly and call the getCenteredLocation() function using the custom manager.

Added some extra debug code like counting number of sections, console logging of text, etc.

I am getting one bug in the form of my application only ever returning 1 section after running the new function. I am basically rendering the returned sections array size to the main application, and have it call that code every time a location change occurs.

I remember you mentioning some bits before about the page width to correct, but I couldn't find that exact comment upon going back through the thread. I ended up using getPaginatedLocation() as a template for this function, so there might be some other things that need to be changed here, just like I had to change some of the "this.xxx" statements.

Once I have it returning the correct number of displayed sections (which should be 2 in this example, I can move on to text extraction code.

image

@johnfactotum
Copy link
Contributor

Once I have it returning the correct number of displayed sections (which should be 2 in this example, I can move on to text extraction code.

If you mean that the length of this.visible() is 1, then that is expected. A "section" is an XHTML page. For the default manager, there should only ever be one visible section at any time, unless you're rendering fixed layout books. Only the continuous manager is capable of rendering reflowable books two sections at a time (or more when using continuous scrolling).

@BrendanCook44
Copy link
Author

BrendanCook44 commented May 2, 2024

Ok, I might just need to grab the right property of the section then to get the number I'm looking for.

to obtain that value, it was just:
const sections = rend.manager.getCenteredLocation(); which returned 1, which makes sense with your explanation.

Was there perhaps a certain property of sections that would contain that number? I'm trying to figure out how I would utilize sections to find the "splitting point" that would allow me to extract text by.

For instance, if section contains two separate "blocks", which I highlighted in red in the first post, I would want to access the number of "blocks" being rendered and perform functions against them for text extraction. I was under the impression these "blocks" I am referring to are sections.

I was put off by the return "sections" at the bottom of the function, which made me think there was going to be more than 1.

            return {
                index,
                href,
                pages,
                totalPages,
                mapping,
                center
            };
        });
    
        return sections;

@johnfactotum
Copy link
Contributor

I was under the impression these "blocks" I am referring to are sections.

A "section" in Epub.js is an XHTML file in the EPUB. So for fixed layout EPUBs, yes, each "page" in a spread would normally consist of one section.

But for reflowable EPUBs, no. Although there appear to be two text blocks in a spread, they would belong to the same section, rendered in the same iframe. The blocks are just CSS columns. That's why you have to check the rects of ranges in the section to see which columns they are being rendered to.

@BrendanCook44
Copy link
Author

BrendanCook44 commented May 3, 2024

Got it, I think I will need to include a columns array within getCenteredLocation() and add it within the return.
Essentially this columns array would represent the CSS columns and hold startCfi and endCfi properties for each column which would then feed into the range function.

@BrendanCook44
Copy link
Author

BrendanCook44 commented May 7, 2024

it looks like this is what I've been looking for:

let columns = this.mapping.findRanges(view);

function definition from mapping.js:

findRanges(view) {
    var columns = [];
    var scrollWidth = view.contents.scrollWidth();
    var spreads = Math.ceil(scrollWidth / this.layout.spreadWidth);
    var count = spreads * this.layout.divisor;
    var columnWidth = this.layout.columnWidth;
    var gap = this.layout.gap;
    var start, end;

    for (var i = 0; i < count.pages; i++) {
      start = (columnWidth + gap) * i;
      end = columnWidth * (i + 1) + gap * i;
      columns.push({
        start: this.findStart(view.document.body, start, end),
        end: this.findEnd(view.document.body, start, end)
      });
    }

    return columns;
  }

I seem to always return a value of 0 for columns when calling in the function below:

import { Rendition, Book } from "epubjs";
const DefaultViewManager = new Rendition(new Book()).requireManager("default");

export class CustomManager extends DefaultViewManager {

    getCenteredLocation() {
        let visible = this.visible();
        let container = this.container.getBoundingClientRect();
        let left = 0;
        let used = 0;
    
        if(this.settings.fullsize) {
            left = window.scrollX;
        }
    
        let sections = visible.map((view) => {
            let {index, href} = view.section;
            let offset;
            let position = view.position();
            let width = view.width();
    
            // Find mapping
            let start;
            let end;
            let pageWidth;
    
            // Calculate the center of the view
            let center = position.left + width / 2;
    
            if (this.settings.direction === "rtl") {
                offset = container.right - left;
                pageWidth = Math.min(Math.abs(offset - center), this.layout.width) - used;
                end = position.width - (position.right - offset) - used;
                start = end - pageWidth;
            } else {
                offset = container.left + left;
                pageWidth = Math.min(center - offset, this.layout.width) - used;
                start = offset - position.left + used;
                end = start + pageWidth;
            }
    
            used += pageWidth;
    
            let mapping = this.mapping.page(view.contents, view.section.cfiBase, start, end);
    
            let totalPages = this.layout.count(width).pages;
            let startPage = Math.floor(start / this.layout.pageWidth);
            let pages = [];
            let endPage = Math.floor(end / this.layout.pageWidth);
            
            // start page should not be negative
            if (startPage < 0) {
                startPage = 0;
                endPage = endPage + 1;
            }
    
            // Reverse page counts for rtl
            if (this.settings.direction === "rtl") {
                let tempStartPage = startPage;
                startPage = totalPages - endPage;
                endPage = totalPages - tempStartPage;
            }
    
            for (var i = startPage + 1; i <= endPage; i++) {
                let pg = i;
                pages.push(pg);
            }

            let columns = this.mapping.findRanges(view);
            console.log('columns:', columns); // log the columns array
    
            return {
                index,
                href,
                pages,
                totalPages,
                mapping,
                columns
            };
        });
    
        return sections;
    }
}

@BrendanCook44
Copy link
Author

BrendanCook44 commented May 9, 2024

Update: Was able to get accurate column counts using the pages property of sections and counting total.
I changed back to using getPaginatedLocation() code as the template and factoring it in to getCenteredLocation() and I am seeing proper column ("page") counts.

I noticed each page has start and end properties, so attempted to gain Cfi values to create the range function and seem to get undefined values:

  useEffect(() => {
    if (book) {
      if (!rendition) {
        const rend = book.renderTo("viewer", { width: "100%", height: "100%", manager: CustomManager});
        setRendition(rend);
        rend.display("epubcfi(/6/38!/4[9H5K0-963c561ef2154901b72b0f739696423b]/2[calibre_pb_2]/1:0)");

        rend.on('relocated', (location: { start: { cfi: any; }; }) => {
          const sections = rend.manager.getCenteredLocation();

          let total = 0;
          let pageTexts: string[] = [];
        
          for (let section of sections) {
            total += section.pages.length;
        
            for (let page of section.pages) {
              let startCfi = page.start;
              let endCfi = page.end;
              console.log("startCfi: " + startCfi);
              console.log("endCfi: " + endCfi);


              // book.getRange(startCfi, endCfi).then((range: { toString: () => any; }) => {
              //   let text = range.toString();
              //   pageTexts.push(text);
              // });
            }
          }
        
          //setPageTexts(pageTexts);
          setNumColumns(total);

          const percentage = parseFloat((book.locations.percentageFromCfi(location.start.cfi) * 100).toFixed(2));
          setProgression(percentage);
        });

        book.loaded.navigation.then((navigation: { toc: React.SetStateAction<any[]> }) => {
          setToc(navigation.toc);
        });
      }
    }
    return () => {
      if (rendition) {
        rendition.destroy();
      }
    };
  }, [book, rendition]);

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants