HTML-книжная нумерация страниц

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

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

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

Этот вопрос изначально был помечен как "Android", так как я намереваюсь создать читатель ePub для Android. Однако, похоже, что решение может быть реализовано только с помощью 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;

Надеюсь это поможет...

Исходя из опыта, ожидайте, что потратите на это много времени, даже для зрителей. Читатель 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 Книг сломали его полностью. У них совершенно странная реализация ePub (по крайней мере для меня) по сравнению с книгами из других источников.

В любом случае, надеюсь, что некоторый структурный код там может вам помочь!

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

Вы должны применить эти строки к веб-просмотру...

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

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

    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 для добавления атрибутов ширины и высоты ко всем изображениям в DOM, у которых их нет.

Надеюсь, поможет.

Дан

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

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

Код решения.

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

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

Основная идея будет:

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

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

Если бы эта проблема возникла недавно и была вдохновлена ​​ответами, то нашел простое решение CSS с использованием атрибутов column- * 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>

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

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

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

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

В противном случае я думаю, что вы можете использовать элемент 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

Вы можете просмотреть 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.

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

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

Другие вопросы по тегам