web-dev-qa-db-fra.com

Pagination de type livre HTML

Comment diviser le contenu d'un fichier HTML en morceaux de la taille d'un écran pour le "paginer" dans un navigateur WebKit? 

Chaque "page" devrait montrer une quantité complète de texte. Cela signifie qu’une ligne de texte ne doit pas être coupée en deux dans la bordure supérieure ou inférieure de l’écran.

Modifier

Cette question était à l'origine étiquetée "Android" car mon intention est de créer un lecteur ePub Android. Cependant, il semble que la solution puisse être implémentée uniquement avec JavaScript et CSS. J'ai donc élargi le champ de la question pour la rendre indépendante de la plate-forme.

59
hpique

S'appuyant sur la réponse de Dan, voici ma solution à ce problème, avec lequel je me débattais jusqu'à présent. (Ce JS fonctionne sur iOS Webkit, aucune garantie pour Android, mais s'il vous plaît laissez-moi savoir les résultats)

var desiredHeight;
var desiredWidth;
var bodyID = document.getElementsByTagName('body')[0];
totalHeight = bodyID.offsetHeight;
pageCount = Math.floor(totalHeight/desiredHeight) + 1;
bodyID.style.padding = 10; //(optional) prevents clipped letters around the edges
bodyID.style.width = desiredWidth * pageCount;
bodyID.style.height = desiredHeight;
bodyID.style.WebkitColumnCount = pageCount;

J'espère que cela t'aides...

30
Engin Kurutepe

Parlant d’expérience, attendez-vous à y consacrer beaucoup de temps, même pour un spectateur dépourvu. Un lecteur ePub était en fait le premier grand projet auquel j'ai participé lorsque j'ai commencé à apprendre le C #, mais le standard ePub est vraiment assez complexe.

Vous pouvez trouver la dernière version de la spécification pour ePub ici: http://www.idpf.org/specs.htm Qui inclut la structure OPS (Open Publication Structure), OPF (Open Packaging) Format) et OCF (format de conteneur OEBPS).

De plus, si cela vous aide du tout, voici un lien vers le code source C # du projet sur lequel j'ai commencé:

https://www.dropbox.com/sh/50kxcr29831t854/MDITIklW3I/ePub%20Test.Zip

Ce n'est pas étoffé du tout; Cela fait des mois que je n’ai pas joué à cela, mais si je me souviens bien, collez simplement un ePub dans le répertoire de débogage, et lorsque vous exécuterez le programme, tapez une partie du nom (par exemple, Sous le dôme, tapez "dome") et il affichera les détails du livre. 

Je l'ai fait fonctionner correctement pour quelques livres, mais tous les livres électroniques de Google Livres l'ont complètement cassé. Ils ont une implémentation complètement bizarre de ePub (du moins pour moi) par rapport aux livres d’autres sources. 

Quoi qu'il en soit, j'espère qu'une partie du code structurel pourrait vous aider!

21
kcoppock

J'ai eu à coder quelque chose comme ça aussi, et ma solution (de travail) est la suivante:

Vous devez appliquer ces lignes à la vue Web ...

    webView_.getSettings().setUseWideViewPort(true);
    webView_.getSettings().setLayoutAlgorithm(LayoutAlgorithm.NARROW_COLUMNS);

En outre, vous devez injecter du javascript. J'ai eu des tonnes de problèmes avec les différentes échelles de mon activité et le contenu rendu dans la vue Web, donc ma solution ne prend aucune sorte de valeur de "l'extérieur".

    webView_.setWebViewClient(new WebViewClient(){

            public void onPageFinished(WebView view, String url) {
                injectJavascript();
            }

        });

[...]

    public void injectJavascript() {
        String js = "javascript:function initialize() { " +
                "var d = document.getElementsByTagName('body')[0];" +
                "var ourH = window.innerHeight; " +
                "var ourW = window.innerWidth; " + 
                "var fullH = d.offsetHeight; " +
                "var pageCount = Math.floor(fullH/ourH)+1;" +
                "var currentPage = 0; " +
                "var newW = pageCount*ourW; " +
                "d.style.height = ourH+'px';" +
                "d.style.width = newW+'px';" +
                "d.style.webkitColumnGap = '2px'; " +
                "d.style.margin = 0; " +
                "d.style.webkitColumnCount = pageCount;" +
                "}";
        webView_.loadUrl(js);
        webView_.loadUrl("javascript:initialize()");
    }

Prendre plaisir :)

18
Nacho L.

J'ai récemment tenté quelque chose de similaire et ajouté des styles CSS pour changer la disposition en horizontale plutôt qu'en verticale. Cela m'a donné l'effet souhaité sans avoir à modifier le contenu de l'Epub de quelque manière que ce soit.

Ce code devrait fonctionne.

mWebView.setWebViewClient(new WebViewClient() {
    public void onPageFinished(WebView view, String url) {

        // Column Count is just the number of 'screens' of text. Add one for partial 'screens'
        int columnCount = Math.floor(view.getHeight() / view.getWidth())+1;

        // Must be expressed as a percentage. If not set then the WebView will not stretch to give the desired effect.
        int columnWidth = columnCount * 100;

        String js = "var d = document.getElementsByTagName('body')[0];" + 
            "d.style.WebkitColumnCount=" + columnCount + ";" + 
            "d.style.WebkitColumnWidth='" + columnWidth + "%';";
        mWebView.loadUrl("javascript:(function(){" + js + "})()");
    }
});

mWebView.loadUrl("file:///Android_asset/chapter.xml");

Donc, en gros, vous injectez du JavaScript pour modifier le style de l'élément body après le chargement du chapitre (très important). Le seul inconvénient de cette approche est que lorsque vous avez des images dans le contenu, le nombre de colonnes calculées va de travers. Cela ne devrait pas être trop difficile à résoudre. J'essayais d'injecter du code JavaScript pour ajouter des attributs de largeur et de hauteur à toutes les images du DOM qui n'en contiennent pas.

J'espère que ça aide.

-Dan

12
Dan

Peut-être que cela fonctionnerait d'utiliser XSL-FO . Cela semble lourd pour un appareil mobile, et peut-être excessif, mais cela devrait fonctionner, et vous ne devriez pas mettre en œuvre les complexités d'une bonne pagination (par exemple, comment vous assurer que chaque écran ne coupe pas le texte en deux) .

L'idée de base serait:

  • transformer le XHTML (et d’autres éléments EPUB) en XSL-FO en utilisant XSLT.
  • utiliser un processeur XSL-FO pour rendre le XSL-FO dans un format paginable que vous pouvez afficher sur le périphérique mobile, tel que PDF (pouvez-vous l'afficher?)

Je ne sais pas s'il existe un processeur XSL-FO disponible pour Android. Vous pouvez essayer Apache FOP. RenderX (processeur XSL-FO) a l’avantage d’avoir une option de sortie paginée-HTML , mais là encore, je ne sais pas s’il peut s’exécuter sur Android.

4
LarsH

J'ai pu améliorer la solution Nacho / pour obtenir un effet de pagination par balayage horizontal avec WebView. Vous pouvez trouver la solution ici et exemple code .

Modifier:

Code de solution.

MainActivity.Java

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KitKat) {
        WebView.setWebContentsDebuggingEnabled(true);
    }
    wv = (HorizontalWebView) findViewById(R.id.web_view);
    wv.getSettings().setJavaScriptEnabled(true);
    wv.setWebViewClient(new WebViewClient() {

        public void onPageFinished(WebView view, String url) {
            injectJavascript();
        }
    });

    wv.setWebChromeClient(new WebChromeClient() {
        @Override
        public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
            int pageCount = Integer.parseInt(message);
            wv.setPageCount(pageCount);
            result.confirm();
            return true;
        }
    });
    wv.loadUrl("file:///Android_asset/ch03.html");   // now it will not fail here
}

private void injectJavascript() {
    String js = "function initialize(){\n" +
            "    var d = document.getElementsByTagName('body')[0];\n" +
            "    var ourH = window.innerHeight;\n" +
            "    var ourW = window.innerWidth;\n" +
            "    var fullH = d.offsetHeight;\n" +
            "    var pageCount = Math.floor(fullH/ourH)+1;\n" +
            "    var currentPage = 0;\n" +
            "    var newW = pageCount*ourW;\n" +
            "    d.style.height = ourH+'px';\n" +
            "    d.style.width = newW+'px';\n" +
            "    d.style.margin = 0;\n" +
            "    d.style.webkitColumnCount = pageCount;\n" +
            "    return pageCount;\n" +
            "}";
    wv.loadUrl("javascript:" + js);
    wv.loadUrl("javascript:alert(initialize())");
}

Dans mon onJsAlert de WebChromeClient, obtenez le nombre de pages horizontales que je passe au HorizontalWebView personnalisé pour implémenter l'effet de pagination 

HorizontalWebView.Java

public class HorizontalWebView extends WebView {
    private float x1 = -1;
    private int pageCount = 0;

    public HorizontalWebView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                x1 = event.getX();
                break;
            case MotionEvent.ACTION_UP:
                float x2 = event.getX();
                float deltaX = x2 - x1;
                if (Math.abs(deltaX) > 100) {
                    // Left to Right swipe action
                    if (x2 > x1) {
                        turnPageLeft();
                        return true;
                    }

                    // Right to left swipe action
                    else {
                        turnPageRight();
                        return true;
                    }

                }
                break;
        }
        return true;
    }

    private int current_x = 0;

    private void turnPageLeft() {
        if (getCurrentPage() > 0) {
            int scrollX = getPrevPagePosition();
            loadAnimation(scrollX);
            current_x = scrollX;
            scrollTo(scrollX, 0);
        }
    }

    private int getPrevPagePosition() {
        int prevPage = getCurrentPage() - 1;
        return (int) Math.ceil(prevPage * this.getMeasuredWidth());
    }

    private void turnPageRight() {
        if (getCurrentPage() < pageCount - 1) {
            int scrollX = getNextPagePosition();
            loadAnimation(scrollX);
            current_x = scrollX;
            scrollTo(scrollX, 0);
        }
    }

    private void loadAnimation(int scrollX) {
        ObjectAnimator anim = ObjectAnimator.ofInt(this, "scrollX",
                current_x, scrollX);
        anim.setDuration(500);
        anim.setInterpolator(new LinearInterpolator());
        anim.start();
    }

    private int getNextPagePosition() {
        int nextPage = getCurrentPage() + 1;
        return (int) Math.ceil(nextPage * this.getMeasuredWidth());
    }

    public int getCurrentPage() {
        return (int) (Math.ceil((double) getScrollX() / this.getMeasuredWidth()));
    }

    public void setPageCount(int pageCount) {
        this.pageCount = pageCount;
    }
}
1
Gautam Chibde

Ce même problème récemment et inspiré par les réponses a trouvé une solution CSS simple utilisant les attributs column- * de CSS3:

/* CSS */
.chapter {
    width: 600px;
    padding: 60px 10px;
    -webkit-column-gap: 40px;
    -webkit-column-width: 150px;
    -webkit-column-count: 2;
    height:400px;
}

/* HTML */
<div class="chapter">
    your long lorem ipsum arbitrary HTML
</div>

L'exemple ci-dessus donne d'excellents résultats sur un iPhone avec rétine. En jouant avec les différents attributs, on obtient un espacement différent entre les pages.

Par exemple, si vous devez prendre en charge plusieurs chapitres qui doivent commencer sur de nouvelles pages, il existe un exemple XCode 5 sur github: https://github.com/dmrschmidt/ios_uiwebview_pagination

1
Dennis

Cela peut être fait de plusieurs manières. Si chaque ligne est dans son propre élément, tout ce que vous avez à faire est de vérifier si l'un de ses bords sort de la vue (les navigateurs ou la "page de livre").

Si vous voulez savoir combien de "pages" il y aura d'avance, déplacez-les temporairement dans la vue et obtenez la ligne à laquelle se termine une page. Cela pourrait potentiellement être lent en raison du fait que le navigateur doit savoir où se trouve quoi que ce soit.

Sinon, je pense que vous pourriez utiliser l'élément de canevas HTML5 pour mesurer du texte et/ou dessiner du texte.

Quelques informations à ce sujet ici: https://developer.mozilla.org/en/Drawing_text_using_a_canvashttp://uupaa-js-spinoff.googlecode.com/svn/trunk/uupaa-excanvas.js/demo/8_2_canvas_measureText.html

1
Frank

Vous pouvez consulter http://www.litres.ru/static/OR/or.html?data=/static/trials/00/42/47/00424722.gur.html&art=424722&user=0&trial=1 mais le code peut être fortement obscurci, utilisez donc simplement Firebug pour inspecter DOM.

Si le lien ne fonctionne pas, le commentaire - vous donnerait résolu.

0
kagali-san

Vous pouvez diviser les pages en fichiers XHTML distincts et les stocker dans un dossier. Par exemple: page01, page02. Vous pouvez ensuite rendre ces pages une à une sous l’autre.

0
Charles