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. Затем вы можете отображать эти страницы один за другим под друг другом.
Ответ 9
Вы можете посмотреть 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.
Если ссылка не работает, комментарий - даст вам исправление.
Ответ 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;
}
}