HTML-подобная разбивка на страницы

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

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

Edit

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

Ответ 1

Основываясь на Дэн, ответ на этот вопрос - это мое решение для этой проблемы, с которой я боролся до сих пор. (этот 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;

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

Ответ 2

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

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

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

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

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

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

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

Ответ 3

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

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

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

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

Ответ 4

Недавно я попытался что-то похожее на это и добавил стили 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 для изменения стиля элемента тела после загрузки главы (очень важно). Единственное падение к этому подходу - когда у вас есть изображения в контенте, подсчитанное количество столбцов идет косо. Это не должно быть слишком сложно исправить. Моя попытка заключалась в том, чтобы вводить некоторый JavaScript для добавления атрибутов ширины и высоты ко всем изображениям в DOM, которые не имеют.

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

Дан

Ответ 5

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

Основная идея:

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

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

Ответ 6

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

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

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

Некоторые сведения об этом здесь:   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

Ответ 7

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

Ответ 8

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

Ответ 10

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

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

В моем WebChromeClient onJsAlert получите количество горизонтальных страниц, которые я передаю пользовательскому 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;
    }
}