futurepress / epub.js

Enhanced eBooks in the browser.
http://futurepress.org
Other
6.4k stars 1.1k forks source link

Ios cuts off the last few pages of a paragraph after changing the font size #330

Open peeter-tomberg opened 8 years ago

peeter-tomberg commented 8 years ago

We're adding a class to the iframe's body element to define if the user has the default size (no extra class), medium size (opus-font-size-medium) or large (opus-font-size-large).

After adding the class (medium or large), we trigger a reformat:

reader.renderer.reformat();

Any idea what we might be doing wrong? Happens with every book, the end of the paragraphs pages gets cut off (when going from paragraph b back to paragraph a)

.opus-font-size-medium,
.opus-font-size-medium * {
    font-size: 18px !important;
}
.opus-font-size-medium H6 {
    font-size: 18px !important;
}
.opus-font-size-medium H5 {
    font-size: 22px !important;
}
.opus-font-size-medium H4 {
    font-size: 28px !important;
}
.opus-font-size-medium H3 {
    font-size: 36px !important;
}
.opus-font-size-medium H2 {
    font-size: 42px !important;
}
.opus-font-size-medium H1 {
    font-size: 54px !important;
}

.opus-font-size-large,
.opus-font-size-large * {
    font-size: 24px !important;
}
.opus-font-size-large H6 {
    font-size: 24px !important;
}
.opus-font-size-large H5 {
    font-size: 30px !important;
}
.opus-font-size-large H4 {
    font-size: 36px !important;
}
.opus-font-size-large H3 {
    font-size: 42px !important;
}
.opus-font-size-large H2 {
    font-size: 50px !important;
}
.opus-font-size-large H1 {
    font-size: 60px !important;
}
fchasen commented 8 years ago

Not sure what is happening here. Could you post a screenshot of the cut off?

Also, how are you adding the class? I added that css to moby dick and tried reader/index.html with the following function:

function test() {
    reader.book.renderer.render.bodyEl.classList.add('opus-font-size-large');
    reader.book.renderer.reformat();
}

and was able to get to all the pages in the ios simulator.

peeter-tomberg commented 8 years ago

Steps to reproduce on a browser:

My setup:

<!DOCTYPE html>
<html class="no-js">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
        <title>Basic ePubJS Example</title>
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width">
        <meta name="apple-mobile-web-app-capable" content="yes">

        <!-- EPUBJS Renderer -->
        <script src="../build/epub.js"></script>

        <style type="text/css">

          body {
            overflow: hidden;
          }

          #main {
            position: absolute;
            width: 100%;
            height: 100%;
          }

          #area {
              margin: 0px 20px;
          }

          #area iframe {
            border: none;
          }

          #prev {
            left: 40px;
          }

          #next {
            right: 40px;
          }

          .arrow {
            position: absolute;
            top: 50%;
            margin-top: -32px;
            font-size: 64px;
            color: #E2E2E2;
            font-family: arial, sans-serif;
            font-weight: bold;
            cursor: pointer;
            -webkit-user-select: none;
            -moz-user-select: none;
            user-select: none;
          }

          .arrow:hover {
            color: #777;
          }

          .arrow:active {
            color: #000;
          }
        </style>
    </head>
    <body>
        <button onclick="changeClass('small');">Small</button>
        <button onclick="changeClass('medium');">Medium</button>
        <button onclick="changeClass('large');">Large</button>
        <div id="main">
          <div id="prev" onclick="reader.prevPage();" class="arrow">‹</div>
          <div id="area"></div>
          <div id="next" onclick="reader.nextPage();" class="arrow">›</div>
        </div>

        <script>
            "use strict";

            EPUBJS.Hooks.register('beforeChapterDisplay').touchHandlers = function (callback, renderer)  {
                EPUBJS.core.addCss('http://localhost:63342/epub.js/examples/styles/reader.css', function() { }, renderer.render.document.head); //Same file that is added in the ticket, update to the correct path 
                callback();
            };
            var reader = ePub({
                height : window.innerHeight - 40,
                width : window.innerWidth,
                styles : {
                    padding : '0 20px'
                }
            });
            reader.forceSingle(true);
            reader.open("https://s3.amazonaws.com/moby-dick/");
            reader.renderTo("area");

            var changeClass = function (type) {
                var body = reader.renderer.doc.body;
                var map = {
                    'small' : 'opus-font-size-small',
                    'medium': 'opus-font-size-medium',
                    'large' : 'opus-font-size-large'
                };
                Object.keys(map).forEach(function (key) {
                    body.classList.remove(map[key]);
                });
                body.classList.add(map[type]);
                reader.renderer.reformat();
            };

        </script>
    </body>
</html>
peeter-tomberg commented 8 years ago

Video of the bug: https://www.youtube.com/watch?v=oa2ExSpuYbQ Plunkr used in the video: http://plnkr.co/edit/O9LSIFLdfVhmI5YTeYcn?p=preview

Steps to reproduce:

allankikkas commented 8 years ago

A common reason for this issue:

Currently on first load and on every chapter display additional css gets requested (from external site or from filesystem) and reported that it is loaded, as soon as this reporting happens reflow calculation starts for the script. Page calculation starts and repositioning happens.

Too bad this happens in the default css context as when the css file is loaded it's not actually applied to DOM yet. So page calculation get's everything wrong as it doesn't know yet how wide the page really is.

Possible solution:

  1. get rid of the css dynamic loading and inject it only once together with the primary css, not on every chapter display.
  2. actually test if the css is applied. (tricky as markers and watchers are needed to do that).
fchasen commented 8 years ago

I agree that this is probably an issue with the timing of the css being applied.

On that note, the changeClass and the callback should be in the callback of the addCSS function, so that they don't execute until the css has been loaded.

EPUBJS.Hooks.register('beforeChapterDisplay').touchHandlers = function (callback, renderer)  {
    EPUBJS.core.addCss(renderer.element.baseURI + 'reader.css', function() {
      changeClass(fontSize);
      callback();
    }, renderer.render.document.head); //Same file that is added in the ticket, update to the correct path
};

One other thing to try for testing if it is a timing issue is putting book.renderer.reformat(); a setTimeout.

peeter-tomberg commented 8 years ago

After putting reformat in a timeout, it helped a bit but the calculation is still off by a page.

Why do I need to re-load the CSS for every paragraph load? Cant I add the CSS/JS files at a configuration step and they stay there?

peeter-tomberg commented 8 years ago

Alright, after 4h of just trying out different approaches, this is what worked for me:

EPUBJS.Hooks.register('beforeChapterDisplay').touchHandlers = (callback, renderer) => {

            var doc = renderer.render.document;

            var node = doc.createElement('style');
            node.type = 'text/css';
            node.appendChild(document.createTextNode(css));
            doc.head.appendChild(node);

            window.setTimeout(() => {
                this.CurrentBookService.updateContrastClass();
                this.CurrentBookService.updateLineSpacingClass();
                this.CurrentBookService.updateFontSizeClass();
                this.CurrentBookService.updateDeviceClass();
                callback();
            }, 100);

        };

I've in-lined the CSS, instead of adding a remote file. I also deferred the callback by a 100ms. No idea why deferring the callback by a 100ms worked, but it did.

ezequiel9 commented 5 years ago

What I've done is encrypt the book coming from the server in base64, decrypt in Javascript and render the ArrayBuffer.