HTML книжное разбиение на страницы

Как я могу разделить содержимое HTML-файла на куски размером с экран, чтобы" разбить " его на страницы в браузере WebKit?

каждая "страница"должна показывать полный текст. Это означает, что строка текста не должна быть разрезана пополам в верхней или нижней границе экрана.

редактировать

этот вопрос был первоначально помечен как "Android", поскольку я намерен создать Android ePub reader. Однако, похоже, что решение может быть реализовано только с помощью JavaScript и CSS я расширил область вопроса, чтобы сделать его независимым от платформы.

10 ответов


основываясь на ответе Дэна, вот мое решение этой проблемы, с которой я боролся до сих пор. (этот JS работает на iOS Webkit, никаких гарантий для android, но, пожалуйста, дайте мне знать результаты)

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;

надеюсь, что это помогает...


говоря по опыту, ожидайте, что вложите в это много времени, даже для зрителя barebones. Читатель ePub на самом деле был первым большим проектом, который я взял, когда начал изучать C#, но стандарт ePub определенно довольно сложный.

вы можете найти последнюю версию спецификации для ePub здесь: http://www.idpf.org/specs.htm который включает OPS (открытая структура публикации), OPF (открытый формат упаковки) и OCF (контейнер OEBPS Формат.)

кроме того, если это вам поможет, вот ссылка на исходный код C# проекта, который я начал:

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

Это вообще не плоть; я не играл с этим в течение нескольких месяцев, но если я правильно помню, просто вставьте ePub в каталог отладки, и когда вы запустите программу, просто введите некоторую часть имени (например, под куполом, просто введите "купол"), и он будет отображение деталей книги.

У меня он работал правильно для нескольких книг, но любые электронные книги из Google Books сломали его полностью. У них совершенно странная реализация ePub (по крайней мере, для меня) по сравнению с книгами из других источников.

в любом случае, надеюсь, некоторые из структурных кодов там могут помочь вам!


мне тоже пришлось закодировать что-то вроде этого, и мое (рабочее) решение таково:

вы должны применить эти строки к webview...

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

кроме того, вы должны ввести некоторый javascript. У меня было множество проблем с различными масштабами моей деятельности и контентом, отображаемым в webview, поэтому мое решение не имеет никакой ценности "извне".

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

наслаждайтесь :)


недавно я попытался что-то подобное этому и добавил стиль CSS, чтобы изменить макет на горизонтальный, а не вертикальный. Это дало мне желаемый эффект без необходимости каким-либо образом изменять содержимое Epub.

код должны работа.

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

Итак, в основном вы вводите JavaScript для изменения стиля элемента body после загрузки главы (очень важно). Единственный недостаток этого подхода - когда вы у изображений в содержимом вычисляемый счетчик столбцов идет косо. Это не должно быть слишком сложно исправить. Моя попытка заключалась в том, чтобы ввести JavaScript для добавления атрибутов width и height ко всем изображениям в DOM, у которых их нет.

надеюсь, что это помогает.

-Дэн


может быть, это сработает, чтобы использовать XSL-FO. Это кажется тяжелым для мобильного устройства, и, возможно, это перебор, но он должен работать, и вам не придется реализовывать сложности хорошей разбиения на страницы (например, как вы убедитесь, что каждый экран не разрезает текст пополам).

основная идея была бы:

  • преобразуйте XHTML (и другие материалы EPUB) в XSL-FO с помощью XSLT.
  • используйте процессор XSL-FO для отображения XSL-FO в формат подкачки, который можно отобразить на мобильном устройстве, например PDF (можно ли это отобразить?)

Я не знаю, есть ли процессор XSL-FO, доступный для Android. Вы можете попробовать Apache FOP. RenderX (процессор XSL-FO) имеет преимущество наличия параметр вывода paged-HTML, но опять же я не знаю, Может ли он работать на Android.


существует несколько способов сделать это. Если каждая строка находится в своем собственном элементе, все, что вам нужно сделать, это проверить, выходит ли один из ее краев за пределы представления (либо браузеры, либо "страница книги").

Если вы хотите знать, сколько "страниц" будет заранее, просто временно переместите их в представление и получите, какая строка заканчивается на странице. Это может быть медленным из-за того, что страница оплавления требуется для браузера, чтобы знать, где что есть.

в противном случае я думаю, что вы можете использовать элемент HTML5 canvas для измерения текста и / или рисования текста.

некоторая информация об этом здесь: https://developer.mozilla.org/en/Drawing_text_using_a_canvas http://uupaa-js-spinoff.googlecode.com/svn/trunk/uupaa-excanvas.js/demo/8_2_canvas_measureText.html


недавно эта же проблема и вдохновленные ответами нашли простое решение CSS, используя столбец 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>

приведенный выше пример дает отличные результаты на retina iPhone. Игра с различными атрибутами дает разные интервалы между страницами и тому подобное.

Если вам нужно поддерживать несколько глав, например, которые должны начинаться на новых страницах, есть пример XCode 5 на github: https://github.com/dmrschmidt/ios_uiwebview_pagination


я смог улучшить Начо решение для получения горизонтального эффекта подкачки с помощью WebView. Вы можете найти решение здесь пример код.

Edit:

код решения.

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

в onJsAlert моего WebChromeClient получите количество горизонтальных страниц, которые я передаю пользовательскому HorizontalWebView для реализации эффекта подкачки

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;
    }
}

вы можете разделить страницы на отдельные файлы XHTML и сохранить их в папке. Например: page01, page02. Затем вы можете отобразить эти страницы одну за другой под другой.


вы можете посмотреть http://www.litres.ru/static/OR/or.html?data=/static/trials/00/42/47/00424722.gur.html&art=424722&user=0&trial=1 но код может быть сильно запутан, поэтому просто используйте Firebug для проверки DOM.

Если ссылка не работает, комментарий-даст вам исправлено.