HTML书籍式的分页

如何将HTML文件的内容以屏幕大小的块分割为WebKit浏览器中的“分页”?

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

编辑

这个问题最初被标记为“Android”,因为我的意图是build立一个Android ePub阅读器。 但是,这个解决scheme似乎只能通过JavaScript和CSS来实现,所以我扩大了问题的范围,使其与平台无关。

丹在这里回答的是我解决这个问题的方法,直到现在,我一直在努力挣扎。 (这个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标准肯定是相当复杂的。

您可以在这里find最新版的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

它根本不是充实的; 我已经玩了好几个月了,但是如果我没有记错的话,只要在debugging目录中粘贴一个ePub,在运行程序时只需input部分名称(例如在圆顶下,只需键入“dome”)它会显示这本书的细节。

我已经准备好了几本书,但Google Books的任何电子书都完全破解了它。 与其他来源的书相比,他们对ePub(对我来说至less)有一个完全奇怪的实现。

无论如何,希望有一些在那里的结构代码可以帮助你!

我必须编写这样的代码,我的(工作)解决scheme是这样的:

你必须将这些行应用到webview …

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

另外,你必须注入一些JavaScript。 我的活动范围和webview中呈现的内容有很多问题,所以我的解决scheme不会从“外部”获得任何价值。

  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元素的样式(非常重要)。 这种方法唯一的缺点是当内容中的图像计算出的列数歪斜时。 尽pipe如此,修复应该不会太难。 我的尝试是注入一些JavaScript来添加宽度和高度属性的DOM中的所有图像没有任何。

希望能帮助到你。

-担

也许它会使用XSL-FO 。 这对移动设备来说似乎很沉重,也许是过度的,但它应该工作,而且你不必执行好分页的复杂性(例如,你如何确保每个屏幕不会把文本切成两半) 。

基本的想法是:

  • 使用XSLT将XHTML(和其他EPUB的东西)转换为XSL-FO。
  • 使用XSL-FO处理器将XSL-FO渲染为您可以在移动设备上显示的分页格式,例如PDF(可以显示该格式?)

我不知道是否有适用于Android的XSL-FO处理器。 你可以试试Apache FOP。 RenderX(XSL-FO处理器)具有分页HTML输出选项的优点,但是我不知道它是否可以在Android上运行。

有几种方法可以完成。 如果每一行都在自己的元素中,所有你需要做的就是检查它的一个边是否在视图之外(浏览器或“书页”)。

如果您想知道将会有多less“页面”,只需暂时将它们移动到视图中,然后获取页面结束的内容。 这可能会很慢,因为浏览器需要页面重排来知道什么地方。

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

最近有这个相同的问题,并从答案中得到灵感,发现了一个使用CSS3的column *属性的纯CSS解决scheme:

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

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

我能够改进纳乔的解决scheme,以获得与WebView的水平滑动分页效果。 你可以在这里find解决scheme和示例代码 。

编辑:

解决scheme代码

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