wake-up-neo.net

HTML-Buch-artige Paginierung

Wie kann ich den Inhalt einer HTML-Datei in Bildschirmblöcke aufteilen, um sie in einem WebKit-Browser zu "paginieren"? 

Jede "Seite" sollte eine vollständige Textmenge enthalten. Das bedeutet, dass eine Textzeile am oberen oder unteren Rand des Bildschirms nicht halbiert werden darf.

Bearbeiten

Diese Frage wurde ursprünglich mit "Android" getaggt, da ich beabsichtige, einen Android ePub-Reader zu erstellen. Es scheint jedoch, dass die Lösung nur mit JavaScript und CSS implementiert werden kann. Daher habe ich den Umfang der Frage erweitert, um sie plattformunabhängig zu machen.

59
hpique

Aufbauend auf Dans Antwort ist hier meine Lösung für dieses Problem, mit dem ich mich bis jetzt selbst gekämpft habe. (Diese JS funktioniert mit iOS Webkit, keine Garantien für Android, aber bitte teilen Sie mir die Ergebnisse mit.)

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;

Hoffe das hilft...

30
Engin Kurutepe

Wenn Sie aus Erfahrung sprechen, erwarten Sie viel Zeit, auch für einen Barebones-Zuschauer. Ein ePub-Reader war eigentlich das erste große Projekt, mit dem ich angefangen habe, C # zu lernen, aber der ePub-Standard ist auf jeden Fall ziemlich komplex.

Die neueste Version der Spezifikation für ePub finden Sie hier: http://www.idpf.org/specs.htm Diese enthält die OPS (Open Publication Structure), OPF (Open Packaging) Format) und OCF (OEBPS Container Format).

Wenn es Ihnen wirklich hilft, ist hier ein Link zum C # -Quellcode des Projekts, mit dem ich angefangen habe:

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

Es ist überhaupt nicht konkretisiert; Ich habe schon seit Monaten nicht mehr damit gespielt, aber wenn ich mich recht erinnere, stecke einfach einen ePub in das Debug-Verzeichnis und wenn du das Programm ausführst, gib einfach einen Teil des Namens ein (zB unter dem Dome einfach "Dome") und es werden die Details des Buches angezeigt. 

Ich hatte es für ein paar Bücher richtig funktioniert, aber alle eBooks von Google Books haben es völlig kaputt gemacht. Sie haben eine völlig bizarre Implementierung von ePub (zumindest für mich) im Vergleich zu Büchern aus anderen Quellen. 

Wie dem auch sei, hoffentlich kann dir der strukturelle Code da draußen helfen!

21
kcoppock

Ich musste auch so etwas codieren, und meine (funktionierende) Lösung lautet:

Sie müssen diese Zeilen auf den Webview anwenden ...

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

Außerdem müssen Sie Javascript einführen. Ich hatte Tonnen von Problemen mit den unterschiedlichen Skalen meiner Aktivitäten und den in der Webansicht gerenderten Inhalten, so dass meine Lösung keinen Wert von "außen" hat.

    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()");
    }

Genießen :)

18
Nacho L.

Ich habe kürzlich etwas Ähnliches versucht und ein paar CSS-Stile hinzugefügt, um das Layout horizontal und nicht vertikal zu ändern. Dies gab mir die gewünschte Wirkung, ohne den Inhalt des Epub in irgendeiner Weise ändern zu müssen.

Dieser Code sollte funktionieren.

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");

Grundsätzlich fügen Sie JavaScript ein, um die Gestaltung des body-Elements zu ändern, nachdem das Kapitel geladen wurde (sehr wichtig). Der einzige Nachteil dieses Ansatzes besteht darin, dass die berechnete Spaltenanzahl schief ist, wenn sich Bilder im Inhalt befinden. Es sollte jedoch nicht zu schwer zu beheben sein. Bei meinem Versuch sollte JavaScript eingefügt werden, um Breiten- und Höhenattribute zu allen Bildern im DOM hinzuzufügen, die noch keine haben.

Ich hoffe es hilft.

-Dan

12
Dan

Vielleicht würde es funktionieren, XSL-FO zu verwenden. Dies scheint schwer für ein mobiles Gerät zu sein, und vielleicht ist es übertrieben, aber es sollte funktionieren, und Sie müssen nicht die Komplexität einer guten Paginierung (z. B. wie stellen Sie sicher, dass jeder Bildschirm den Text nicht halbiert) selbst vor sich .

Die Grundidee wäre:

  • wandeln Sie XHTML (und andere EPUB-Dateien) mithilfe von XSLT in XSL-FO um.
  • verwenden Sie einen XSL-FO-Prozessor, um den XSL-FO in ein Seitenformat zu rendern, das Sie auf dem mobilen Gerät anzeigen können, z. B. PDF (können Sie das anzeigen?)

Ich weiß nicht, ob es einen XSL-FO-Prozessor für Android gibt. Sie könnten Apache FOP ausprobieren. RenderX (XSL-FO-Prozessor) hat den Vorteil einer paged-HTML-Ausgabeoption , aber ich weiß auch nicht, ob es auf Android laufen könnte.

4
LarsH

Ich konnte die Nacho'sLösung verbessern, um mit WebView einen horizontalen Swipe-Paging-Effekt zu erzielen. Lösung/- hier und Beispiel Code .

Bearbeiten:

Lösungscode.

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())");
}

In onJsAlert meines WebChromeClient erhalten Sie die Anzahl der horizontalen Seiten, die ich an das benutzerdefinierte HorizontalWebView übergeben, um den Paging-Effekt zu implementieren 

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

Hatte das gleiche Problem vor kurzem und inspiriert von den Antworten, fand eine einfache CSS-Lösung mit CSS3-Spalten- * Attributen:

/* 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>

Das obige Beispiel liefert großartige Ergebnisse auf einem Retina-iPhone. Das Herumspielen mit den verschiedenen Attributen führt zu unterschiedlichen Abständen zwischen den Seiten und so.

Wenn Sie beispielsweise mehrere Kapitel unterstützen müssen, die auf neuen Seiten beginnen müssen, finden Sie unter github ein XCode 5-Beispiel: https://github.com/dmrschmidt/ios_uiwebview_pagination

1
Dennis

Es gibt mehrere Möglichkeiten, dies zu erreichen. Wenn sich jede Zeile in einem eigenen Element befindet, müssen Sie nur überprüfen, ob eine der Kanten außerhalb der Ansicht (entweder der Browser oder die "Buchseite") liegt.

Wenn Sie wissen möchten, wie viele "Seiten" es im Voraus geben wird, verschieben Sie sie einfach vorübergehend in die Ansicht, um zu sehen, welche Zeile eine Seite endet. Dies kann möglicherweise langsam sein, da der Seitenumfluss erforderlich ist, damit der Browser wissen kann, wo sich etwas befindet.

Ansonsten denke ich, dass Sie das HTML5-Canvas-Element zum Messen von Text und/oder zum Zeichnen von Text verwenden könnten.

Einige Infos dazu hier: https://developer.mozilla.org/de/Drawing_text_using_a_canvashttp://uupaa-js-spinoff.googlecode.com/svn/trunk/uupaa-excanvas.js/demo/8_2_canvas_measureText.html

1
Frank

Sie können nach http://www.litres.ru/static/OR/or.html?data=/static/trials/00/42/47/00424722.gur.html&art=424722&user=0&trial=1 suchen Der Code kann jedoch stark verschleiert sein. Verwenden Sie Firebug daher, um DOM zu überprüfen.

Wenn der Link nicht funktioniert, erhalten Sie einen Kommentar.

0
kagali-san

Sie können die Seiten in separate XHTML-Dateien aufteilen und in einem Ordner speichern. ZB: Seite01, Seite02. Sie können diese Seiten dann einzeln nacheinander rendern.

0
Charles