epub.js icon indicating copy to clipboard operation
epub.js copied to clipboard

Getting current page and total number of pages

Open vblazenka opened this issue 7 years ago • 42 comments

Hi, I would like to show something like this (in my reader footer): Page 5 - 323. This is basically this: "Page " + currentPage + " - " + totalPages

But I didn't find a way to get currentPage and totalPages number.

For currentPage I tried:

this.rendition.on('relocated', (location) => {
...
this.book.locations.locationFromCfi(location.start.cfi);
...
});

But I get strange numbers like (in order): 0, 1, 2, 4, 6, 8, 11...

And I'm not even sure how to get totalPages number.

Can anyone help? I would like to find solution for both cases and add them to epub documentation.

EDIT: I use v0.3, I found that v0.2 has generatePagination method but I want to use v0.3

vblazenka avatar May 05 '18 21:05 vblazenka

Hey, @fchasen. Thank you for this cool library you and other people created.

I was wondering if you could give me some tips for regarding my question? I would love to help back.

Thank you!

vblazenka avatar May 08 '18 17:05 vblazenka

@wedranb if you look at the wiki under Updating to v0.3 from v0.2 you find that they have a new method:

generatePagination() was far too resource intensive, as it rendered every page. It has been replaced by book.locations.generate(600) which will create a CFI for every X characters in the book.

From my understanding depending on the argument you pass (150 being the default) it goes through the book and marks where it is every X characters. You get back a big array of pages. I don't find this very helpful though because it's not actual pages, just a rough estimate. Using this method you could skip a page if your number is too high or count a page twice if too low. It seems that when you use rendition.currentLocation() it has an end and start. In those is the list of pages in that chapter but that can be wrong based off of your screen size. All in all, there seems to be no easy way to do it.

jlag34 avatar May 08 '18 22:05 jlag34

Users want total page number. Support for it would be great. After the current section is rendered, rest of book could be initialised to calculate pages. book.locations.generate currently doesn't help.

mikkelvp avatar May 15 '18 19:05 mikkelvp

Getting the page for a currently rendered chapter is simple enough, but rendering the entire book out in each users browser to get page numbers just wasn't practical.

Would be amazing to render out the entire book server side and save the pages as an epub page list. With Puppeteer it should not be too hard to setup.

fchasen avatar May 17 '18 21:05 fchasen

How about showing the user a page number in the format chapter.page (e.g. 1.1, 1.1, 1.2, 1.3, 1.4, 1.5, 2.1, 2.3, etc)? It would still increment predictably and be easy to read. It would also be a lot faster and easier to generate than full pagination, but still better fit your use case than locations.

pgaskin avatar May 18 '18 16:05 pgaskin

@geek1011 You can do that with numbers from rendition.currentLocation()

I've made an extra offscreen rendering of the book. I use that to load one section at a time and get page counts. Takes a while but it's no problem since it's all in the background after the book has loaded. Will show a spinner or similar where page counts are shown while loading them.

mikkelvp avatar May 18 '18 19:05 mikkelvp

@geek1011 I think the basic user (who is just reading with the epub, never coded anything) will be confused by that page numbering system. Wouldn't want to have to tell every user why their page numbers are weird.

@fchasen thanks for that. I'll give puppeteer a shot and let you know!

jlag34 avatar May 19 '18 15:05 jlag34

book.locations.generate returns a promise containing an array of all the CFI locations. Correct me if I am wrong but if these locations are all the pages then we can just get the length of the array.

        this.book.ready.then((book) => {
            return this.book.locations.generate();
        }).then(locations => {
            console.log("Total Pages?: ", locations.length);
        });

askilondz avatar May 31 '18 18:05 askilondz

@askilondz You are wrong. It generates cfi ranges for every x (default 150) characters which has nothing to do with pages.

mikkelvp avatar Jun 01 '18 10:06 mikkelvp

@mikkelvp :-) thanks for the clarification.

askilondz avatar Jun 06 '18 15:06 askilondz

Hi guys! Has anyone found a practical, and hopefully accurate, solution to this?

So far the approaches I'm considering are: A) @fchasen solution sounds nice, but doesn't the number of pages depend also on screen and font size? B) Use book.locations.generate, though it has nothing to do with pages

Also, what about fixed-layout epubs?

fgilio avatar Jun 27 '18 16:06 fgilio

@fgilio I've described my solution earlier in this issue. A) Yes, if you have applied styling you will need to wait for browser to render with that styling before getting page count for a section.

mikkelvp avatar Jun 29 '18 09:06 mikkelvp

Hi @mikkelvp That's what I was afraid of. There seems like there is no way of preprocessing this server side only once. It would need to be generated either client side, or with a, probably overly complex, server side solution that takes into account this variables (screen size and font size, and maybe others). The server side solution could also do it on demand and store the results for the next time the epub and variables match. Or maybe I'm just overly complicating this, but this is when a pdf sounds like something super simple 😅

fgilio avatar Jun 29 '18 18:06 fgilio

@mikkelvp Could you show your code for how you're rendering each section for the offscreen rendering you are doing? I like this approach for getting the page count. I'm looping through each spine item and attempting to render each item/section item.render() but having some issues would be curious to see your solution. Thanks!

askilondz avatar Jul 16 '18 18:07 askilondz

@fchasen I want to show more sections together, e.g. if the page is having an image, breaking it into sections shows part of face then neck then chest and so on, and which does not make any sense. Who can help?

ghost avatar Jul 19 '18 20:07 ghost

Here's the best solution that I have come up with for getting page number and total pages based on setting the number of characters per page:

// Initialize the book
let bookUri = "https://s3.amazonaws.com/moby-dick/moby-dick.epub";
let book = ePub(bookUri, {});
let rendition = book.renderTo('epubContainer', {
    flow: 'paginated',
    manager: 'continuous'
    spread: 'always'
    width: "100% - 106px",
    height: this.calculateReaderHeight()
});

// Display the book
let displayed = rendition.display(window.location.hash.substr(1) || undefined);
displayed.then(function() {
    console.log('rendition.currentLocation():', rendition.currentLocation());
});

// Generate location and pagination
book.ready.then(function() {
    const stored = localStorage.getItem(book.key() + '-locations');
    console.log('metadata:', book.package.metadata);
    if (stored) {
        return book.locations.load(stored);
    } else {
        return book.locations.generate(1024); // Generates CFI for every X characters (Characters per/page)
    }
}).then(function(location) { // This promise will take a little while to return (About 20 seconds or so for Moby Dick)
    localStorage.setItem(book.key() + '-locations', book.locations.save());
});

// When navigating to the next/previous page
rendition.on('relocated', function(locations) {
    progress = book.locations.percentageFromCfi(locations.start.cfi);
    console.log('Progress:', progress); // The % of how far along in the book you are
    console.log('Current Page:', book.locations.locationFromCfi(locations.start.cfi);
    console.log('Total Pages:', book.locations.total);
});

EPubJS will use these generated epubCFI's for a "start" and an "end" for each page. The bigger the number you set in the book.locations.generate() method, the less number of pages you have. This is also why I'm logging the book.package.metadata so that I can determine if the publisher has set a "characters per page" so I can use that value. I'm still early on in my epub.js development but have learned quite a bit over the past week.

I hope this can help someone.

scott-engemann avatar May 14 '19 15:05 scott-engemann

@sengemann how accurate is this? What if the user changes font size or screen size?

vblazenka avatar May 15 '19 07:05 vblazenka

It updates accordingly when the relocated event is triggered. I will have to see how to manually trigger that event for the actual values to change.

scott-engemann avatar May 15 '19 13:05 scott-engemann

It updates accordingly when the relocated event is triggered. I will have to see how to manually trigger that event for the actual values to change.

As far as your understanding, if I call rendition.currentLocation() when on a given page, will the returned start and end CFIs be the actual start and end CFIs of the page 100% of the time? Or are these just estimates, so that if I go to one of these start CFIs, it may not bring me to the correct page...

If anyone else has an answer to this it will likewise be appreciated.

yh54321 avatar Sep 02 '19 09:09 yh54321

The documentation of this library is really bad. Seems to be very good project but I'm losing a lot of time guessing how it works :(((((

ezequiel9 avatar Sep 17 '19 04:09 ezequiel9

Hi,How to get current page and total number of pages? Does anybody have a solution? PLease 🥺

MrXCQ avatar Oct 14 '19 07:10 MrXCQ

@MrXCQ You can't. :) In this thread you can find possible solutions but nothing is accurate.

vblazenka avatar Oct 14 '19 10:10 vblazenka

It updates accordingly when the relocated event is triggered. I will have to see how to manually trigger that event for the actual values to change.

As far as your understanding, if I call rendition.currentLocation() when on a given page, will the returned start and end CFIs be the actual start and end CFIs of the page 100% of the time? Or are these just estimates, so that if I go to one of these start CFIs, it may not bring me to the correct page...

If anyone else has an answer to this it will likewise be appreciated.

The only time that the method rendition.currentLocation() has not been returning the correct location was immediately after adjusting the font-size via the themes() method. I have a bug created for that here: https://github.com/futurepress/epub.js/issues/982

scott-engemann avatar Oct 14 '19 13:10 scott-engemann

@sengemann would you please give us the calculateReaderHeight() function?

hesampour avatar Mar 24 '20 13:03 hesampour

@hesampour, it's pretty simple once you know what your variables are:

calculateReaderHeight() {
  let h = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
  // On the next line you need to factor in the height of any of your UI elements.
  // You could just take the  values if they are static or use jQuery to get the heights dynamically (that would be the preferred method)
  h = h - 50 - 68 - 44; // 50: Header Height, 68: Footer Height, 44: 22px * 2 top/bottom margin
  return h;
},

scott-engemann avatar Mar 24 '20 14:03 scott-engemann

The documentation of this library is really bad. Seems to be very good project but I'm losing a lot of time guessing how it works :(((((

thats right

jiangxiaopeng avatar May 21 '20 05:05 jiangxiaopeng

does anyone have a solution for pagination?

hrkazemi avatar Jun 30 '20 09:06 hrkazemi

I'm messing around with the react native implementation, and adjusting it to my needs here https://github.com/sbrighiu/epubjs-rn. @hrkazemi you can look in the repo for pagingEnabled={true}.

I've also found the documentation for epub.js http://epubjs.org/documentation/0.3/, if newcomers don't find it immediately

Would be really nice to be able to get number of pages and current page, but as my content varies from device size to device size and from epub to epub, I don't see a reliable way of doing it :(

sbrighiu avatar Sep 24 '20 14:09 sbrighiu

What about the completion? Like a percentage? 61%

sbrighiu avatar Sep 24 '20 14:09 sbrighiu

@sbrighiu and @MrXCQ This is the best solution that I have came up with to get the percentage, current page, page range and total pages: https://github.com/futurepress/epub.js/issues/744#issuecomment-492300092

There are a few requirements for this to work:

  • You need to set your height and width of the area to render the book (I have the set by some function returns in my example code in my comment)
  • You need to set a "page size" with book.locations.generate(X) where X is the number of characters you want to have on a page
  • The entire book needs to be loaded (not one chapter at a time)

scott-engemann avatar Sep 24 '20 14:09 scott-engemann