HTML 书本式分页

Posted

技术标签:

【中文标题】HTML 书本式分页【英文标题】:HTML book-like pagination 【发布时间】:2011-04-07 20:31:13 【问题描述】:

如何将 html 文件的内容拆分为屏幕大小的块,以便在 WebKit 浏览器中对其进行“分页”?

每个“页面”都应显示完整的文本量。这意味着一行文本不得在屏幕的顶部或底部边框中被切成两半。

编辑

这个问题最初被标记为“android”,因为我的目的是构建一个 Android ePub 阅读器。但是,该解决方案似乎可以仅使用 javascript 和 CSS 来实现,因此我扩大了问题的范围以使其与平台无关。

【问题讨论】:

"我如何将 XHTML 文件的内容分成屏幕大小的块来“分页”这本书。" -- 为什么要这样做? 因为它是一种类似于书本且被广泛使用的方式(即:Aldiko)来呈现文本,而且它也是一个有趣的问题。 :) 嗨,以下哪种解决方案适合你。现在我面临和你一样的问题 -:( 兄弟...你能得到解决方案吗..我非常需要它...请 EPUB 相同:***.com/questions/2808652/… 【参考方案1】:

您可以将页面拆分为单独的 XHTML 文件并将它们存储在一个文件夹中。例如:page01、page02。然后,您可以将这些页面一张一张地呈现在彼此的下方。

【讨论】:

我怎么知道根据屏幕和字体大小在哪里分割?【参考方案2】:

根据经验,即使对于准系统观众来说,也需要投入大量时间。 ePub 阅读器实际上是我开始学习 C# 时接手的第一个大项目,但 ePub 标准肯定相当复杂。

您可以在此处找到最新版本的 ePub 规范: http://www.idpf.org/specs.htm 其中包括 OPS(Open Publication Structure)、OPF(Open Packaging Format)和 OCF(OEBPS Container Format)。

另外,如果它对你有帮助,这里是我开始的项目的 C# 源代码的链接:

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

它根本没有充实;我已经好几个月没玩这个了,但如果我没记错的话,只需在调试目录中粘贴一个 ePub,然后在运行程序时输入名称的一部分(例如在 Dome 下,只需输入“dome”)它将显示该书的详细信息。

我让它在几本书上都能正常工作,但谷歌图书中的任何电子书都完全破坏了它。与其他来源的书籍相比,他们对 ePub 的实现完全奇怪(至少对我而言)。

无论如何,希望其中的一些结构代码可以帮助您!

【讨论】:

【参考方案3】:

我最近尝试了类似的方法并添加了一些 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 中没有任何图像的所有图像添加宽度和高度属性。

希望对你有帮助。

-丹

【讨论】:

+1 无法让它发挥作用,但我认为它为我指明了正确的方向。 @Dan 你能分享一下你的css风格吗?分页对我有用,但同样需要 css。请发布您的CSS。提前致谢。【参考方案4】:

也许使用XSL-FO 会起作用。这对于移动设备来说似乎很重,也许有点矫枉过正,但它应该可以工作,而且您不必自己实现良好分页的复杂性(例如,您如何确保每个屏幕不会将文本切成两半) .

基本思路是:

使用 XSLT 将 XHTML(和其他 EPUB 内容)转换为 XSL-FO。 使用 XSL-FO 处理器将 XSL-FO 呈现为可以在移动设备上显示的分页格式,例如 PDF(可以显示吗?)

我不知道是否有适用于 Android 的 XSL-FO 处理器。你可以试试 Apache FOP。 RenderX(XSL-FO 处理器)的优势在于拥有paged-HTML output option,但我也不知道它是否可以在 Android 上运行。

【讨论】:

【参考方案5】:

以 Dan 的回答为基础,这是我对这个问题的解决方案,直到现在我一直在努力解决这个问题。 (此 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;

希望这会有所帮助...

【讨论】:

你能指导我如何在 webview 中实现这个吗?或者你可以为此发布整个代码吗?提前致谢。【参考方案6】:

您可以查看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。

如果链接不起作用,请发表评论 - 会给你修复。

【讨论】:

【参考方案7】:

有几种方法可以做到这一点。如果每一行都在它自己的元素中,你所要做的就是检查它的一个边缘是否超出了视图(浏览器或“书页”)。

如果您想提前知道会有多少“页面”,只需将它们临时移动到视图中并获取页面结束的行。这可能会很慢,因为浏览器需要页面重排才能知道任何东西在哪里。

否则我认为您可以使用 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

【讨论】:

【参考方案8】:

我也不得不编写类似的代码,我的(工作)解决方案是这样的:

您必须将这些行应用到 webview...

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

此外,您必须注入一些 javascript。我的活动的不同规模和 web 视图中呈现的内容有很多问题,所以我的解决方案没有从“外部”获取任何价值。

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

享受:)

【讨论】:

这行得通。如何在单击按钮时滚动到下一列? @Midhun, $("body").animate(scrollLeft: window.innerWidth*<your col index>); 如何使用此代码进行滚动页面分页? 这很好用,但有一个问题。我可以向下滚动,底部有很多额外的空间。如何删除这个多余的空间? js字符串中的currentPage变量有什么用?【参考方案9】:

最近遇到了同样的问题,并受到答案的启发,找到了一个使用 CSS3 的 column-* 属性的普通 CSS 解决方案:

/* 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 上给出了很好的结果。使用不同的属性会产生不同的页面间距等。

如果您需要支持多个章节,例如需要从新页面开始,github 上有一个 XCode 5 示例:https://github.com/dmrschmidt/ios_uiwebview_pagination

【讨论】:

【参考方案10】:

我能够改进Nacho's 解决方案以使用WebView 获得水平滑动分页效果。您可以找到解决方案here 和示例code。

编辑:

解决方案代码。

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 中获取我传递给自定义 Horizo​​ntalWebView 以实现分页效果的水平页面数

Horizo​​ntalWebView.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;
    

【讨论】:

以上是关于HTML 书本式分页的主要内容,如果未能解决你的问题,请参考以下文章

单页分页问题中的多个角度材料表

AI-响应式分页器

操作系统——存储管理:分区分页分段请求式分页和虚拟内存

Laravel 多页分页

react-slick 自定义分页分页道具使用

梦内容页分页标题提取