mirror of
https://github.com/geometer/FBReaderJ.git
synced 2025-10-03 09:49:19 +02:00
intro ePub auto generation
This commit is contained in:
parent
3d5ab599f4
commit
7fa54dfd9b
20 changed files with 196 additions and 47 deletions
|
@ -1,16 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FictionBook xmlns="http://www.gribuser.ru/xml/fictionbook/2.0" xmlns:l="http://www.w3.org/1999/xlink"><description><title-info><author><last-name>FBReader</last-name></author> <book-title>О программе FBReader</book-title> <lang>ru</lang></title-info></description>
|
||||
<body>
|
||||
<title><p>О программе FBReader &FBReaderVersion;</p></title>
|
||||
<p>Эта программа предназначена для чтения электронных книг в форматах ePub и fb2. Если у вас уже есть подходящие книги, положите их на карту памяти в каталог Books. После этого вы найдете свои книги в <a l:href="fbreader-action:library">библиотеке</a>. В FBReader встроена <a l:href="fbreader-action:networkLibrary">сетевaя библиотека</a>, из которой можно бесплатно скачивать и покупать книги.</p>
|
||||
<p>Для перелистывания страницы коснитесь правой части экрана (или левой, если хотите вернуться на предыдущую страницу). Другие варианты перелистывания – провести по экрану пальцем справа налево, или воспользоваться кнопками регулировки громкости.</p>
|
||||
<p>Вы можете настроить FBReader, чтобы читать так, как удобно именно вам. Цвета, шрифты, каталоги, способы листания, и многое другое можно поменять в <a l:href="fbreader-action:preferences">диалоге настроек</a>.</p>
|
||||
<p>Обычно управление программой происходит с помощью меню. Если на вашем устройстве нет кнопки Menu, просто нажмите на экран в середине его нижней части.</p>
|
||||
<p>Приятного чтения!</p>
|
||||
<empty-line/>
|
||||
<subtitle><p>Ссылки</p></subtitle>
|
||||
<p>Узнать подробнее о программе FBReader, скачать версии для разных устройств и исходные тексты можно на <a l:href="http://www.fbreader.org/FBReaderJ">нашем сайте</a>.</p>
|
||||
<p>Сообщения о вышедших версиях и другие новости публикуются в <a l:href="http://twitter.com/fbreader_ru">twitter'е</a>.</p>
|
||||
<p>Если вы хотите связаться с авторами, <a l:href="mailto:contact@fbreader.org">напишите</a> нам электронное письмо.</p>
|
||||
</body>
|
||||
</FictionBook>
|
Binary file not shown.
11
build.xml
11
build.xml
|
@ -55,7 +55,15 @@
|
|||
<echo message="DONE (Checking if native libraries are up-to-date)"/>
|
||||
</target>
|
||||
|
||||
<target name="-pre-build" depends="init, native">
|
||||
<target name="genhelp">
|
||||
<exec executable="./help/generate.py">
|
||||
<arg value="help/proto"/>
|
||||
<arg value="help/html"/>
|
||||
<arg value="assets/data/intro"/>
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<target name="-pre-build" depends="init, native, genhelp">
|
||||
</target>
|
||||
<!--
|
||||
<target name="-pre-compile">
|
||||
|
@ -69,6 +77,7 @@
|
|||
<delete dir="gen" />
|
||||
<delete dir="bin" />
|
||||
<delete dir="out" />
|
||||
<delete dir="assets/data/intro" />
|
||||
<echo message="DONE (Deleting temporary files)" />
|
||||
</target>
|
||||
|
||||
|
|
63
help/generate.py
Executable file
63
help/generate.py
Executable file
|
@ -0,0 +1,63 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
import os, re, shutil, sys, time, zipfile
|
||||
|
||||
def collect_languages(proto_dir):
|
||||
return [os.path.splitext(name)[0] for name in os.listdir(proto_dir) if name.endswith('.html')]
|
||||
|
||||
def find_title(path):
|
||||
pattern = re.compile(r'<h2>(.*)<\/h2>')
|
||||
with open(path, 'r') as stream:
|
||||
for line in stream:
|
||||
match = pattern.match(line)
|
||||
if match:
|
||||
return match.group(1)
|
||||
return None
|
||||
|
||||
def string_from_template(template_path, data):
|
||||
result = ''
|
||||
pattern = re.compile(r'(.*)\$([^$]*)\$(.*\n)')
|
||||
with open(template_path, 'r') as istream:
|
||||
for line in istream:
|
||||
match = pattern.match(line)
|
||||
if match:
|
||||
result += match.group(1) + data[match.group(2)] + match.group(3)
|
||||
else:
|
||||
result += line
|
||||
return result
|
||||
|
||||
def generate_epub(proto_dir, html_dir, epub, lang):
|
||||
html_file = os.path.join(html_dir, lang + '.html')
|
||||
title = find_title(html_file)
|
||||
if not title:
|
||||
raise Exception('Title not found in %s' % htmlfile)
|
||||
|
||||
data = {
|
||||
'TITLE': title,
|
||||
'LANG': lang,
|
||||
'ID': 'fbreader:intro:%s:%d' % (lang, time.time())
|
||||
}
|
||||
|
||||
with zipfile.ZipFile(epub, 'w', zipfile.ZIP_DEFLATED) as zip_file:
|
||||
zip_file.writestr('mimetype', 'application/epub+zip', zipfile.ZIP_STORED)
|
||||
zip_file.write(os.path.join(proto_dir, 'container.xml'), arcname='META-INF/container.xml')
|
||||
zip_file.write(os.path.join(proto_dir, 'style.css'), arcname='style.css')
|
||||
zip_file.writestr('main.html', string_from_template(html_file, data))
|
||||
zip_file.writestr('content.opf', string_from_template(os.path.join(proto_dir, 'content.opf'), data))
|
||||
zip_file.write(os.path.join(proto_dir, 'fbreader.png'), arcname='fbreader.png')
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) != 4:
|
||||
exit('Usage: %s <proto_dir> <html_dir> <output_dir>' % sys.argv[0])
|
||||
|
||||
proto_dir = sys.argv[1]
|
||||
html_dir = sys.argv[2]
|
||||
output_dir = sys.argv[3]
|
||||
|
||||
os.mkdirs(output_dir)
|
||||
for lang in collect_languages(html_dir):
|
||||
epub = os.path.join(output_dir, 'intro_' + lang + '.epub')
|
||||
if os.path.exists(epub):
|
||||
os.remove(epub)
|
||||
print 'Generating intro for language %s...' % lang
|
||||
generate_epub(proto_dir, html_dir, epub, lang)
|
32
help/html/ru.html
Normal file
32
help/html/ru.html
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<html xmlns:fbreader='http://data.fbreader.org/xhtml-extension/'>
|
||||
<head>
|
||||
<link type='text/css' rel='stylesheet' href='style.css'/>
|
||||
</head>
|
||||
<body>
|
||||
<h2>FBReader: краткое руководство</h2>
|
||||
<h3>Оглавление</h3>
|
||||
<ul>
|
||||
<li><a href='#introduction'>Что такое FBReader</a></li>
|
||||
<li><a href='#first'>Популярные книги</a></li>
|
||||
<li><a href='#network'>Где взять ещё книг</a></li>
|
||||
<li><a href='#sync'>Как копировать книги с компьютера</a></li>
|
||||
<li><a href='#more'>И это всё?</a></li>
|
||||
<li><a href='#help'>У меня проблема, кто мне поможет?</a></li>
|
||||
</ul>
|
||||
<h3 id='introduction'>Что такое FBReader</h3>
|
||||
<p>FBReader – свободная программа для чтения электронных книг. Если у вас есть книги в виде файлов <code>ePub</code>, <code>fb2</code>, <code>mobi</code> или во многих других форматах, вы можете скопировать их на это устройство, открыть в FBReader, и читать прямо здесь. «Не совсем текстовые» форматы книг, <code>PDF</code> и <code>DjVu</code>, FBReader тоже понимает. Но чтобы программа не слишком разрасталась, мы вынесли их поддержку в отдельные плагины <a href='https://play.google.com/store/apps/details?id=org.geometerplus.fbreader.plugin.pdf'>для PDF</a> и <a href='https://play.google.com/store/apps/details?id=org.geometerplus.fbreader.plugin.djvu'>для DjVu</a>.</p>
|
||||
<h3 id='first'>Популярные книги</h3>
|
||||
<p>Хотите начать читать прямо сейчас? Мы предлагаем на выбор несколько книг, может быть, одна из них вас заинтересует? Нажмите на обложку, книга скачается из сети и сразу же откроется в FBReader. Чтобы вернуться потом к этому тексту, выберите в меню пункт «Стартовый экран».</p>
|
||||
<p class='opds'><fbreader:opds size='5' src='https://books.fbreader.org/recommended/lang/$LANG$.xml'/></p>
|
||||
<h3 id='network'>Где взять ещё книг</h3>
|
||||
<p>В FBReader есть раздел <a href='fbreader-action:networkLibrary'>«Сетевая библиотека»</a>. В нескольких каталогах собраны книги на разных языках, некоторые можно полностью скачать бесплатно, в некоторых доступны только первые несколько глав, а продолжение можно купить за деньги. Если вы знаете адреса других каталогов, вы можете добавить их в свой список, и скачивать книги из них прямо в свой FBReader.</p>
|
||||
<h3 id='sync'>Как копировать книги с компьютера</h3>
|
||||
<p>У вас есть книги на компьютере, и вы хотите перенести их на это устройство? Или наоборот, книги, которые вы скачали на это устройство, хотите сохранить понадёжнее? Это можно сделать, не соединяя компьютер и телефон (планшет) между собой. В FBReader есть возможность синхронизации. <a href='fbreader-action:preferences#sync'>Включите её</a>, и все ваши книги автоматически попадут в облако (на ваш собственный Google Drive). С компьютера доступ к коллекции книг происходит через сайт <a href='https://books.fbreader.org/'>Книжной сети FBReader</a>.</p>
|
||||
<p>Хотите читать одну и ту же книгу днём на телефоне, а вечером на планшете? И не искать место, где остановились? Синхронизация FBReader решит и эту проблему. Включите её на обоих устройствах, и вы никогда не потеряете место, на котором остановились.</p>
|
||||
<h3 id='more'>И это всё?</h3>
|
||||
<p>Разумеется, нет. В таком кратком тексте мы не рассказали про возможности настройки FBReader, про интеграцию с программой Calibre, про другие плагины, например, для автоматического чтения вслух. Новые возможности появляются регулярно, ведь обновления FBReader выходят не реже раза в месяц. Если вы хотите узнать больше – заходите <a href='http://ru.fbreader.org/'>на наш сайт</a>, или <a href='http://ru.fbreader.org/content/about-us#social'>ищите нас в социальных сетях</a>.</p>
|
||||
<h3 id='help'>У меня проблема, кто мне поможет?</h3>
|
||||
<p>Лучший способ – написать о проблеме в одной из социальных сетей. Ссылки вы найдёте выше, а в сетях много активных пользователей, которые увидят ваш вопрос. Мы тоже читаем сети, и регулярно в них отвечаем. Если вы уверены, что ваш вопрос обязательно задать напрямую разработчикам, <a href="mailto:contact@fbreader.org">напишите нам электронное письмо</a>.</p>
|
||||
</body>
|
||||
</html>
|
6
help/proto/container.xml
Normal file
6
help/proto/container.xml
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0"?>
|
||||
<container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">
|
||||
<rootfiles>
|
||||
<rootfile full-path="content.opf" media-type="application/oebps-package+xml"/>
|
||||
</rootfiles>
|
||||
</container>
|
18
help/proto/content.opf
Normal file
18
help/proto/content.opf
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<package xmlns='http://www.idpf.org/2007/opf' unique-identifier='dcidid' version='2.0'>
|
||||
<metadata xmlns:dc='http://purl.org/dc/elements/1.1/'>
|
||||
<dc:title>$TITLE$</dc:title>
|
||||
<dc:language>$LANG$</dc:language>
|
||||
<dc:identifier>$ID$</dc:identifier>
|
||||
<dc:creator>FBReader</dc:creator>
|
||||
<meta content='cover_image' name='cover'/>
|
||||
</metadata>
|
||||
<manifest>
|
||||
<item id='stylesheet' href='styles.css' media-type='text/css'/>
|
||||
<item id='main' href='main.html' media-type='application/xhtml+xml'/>
|
||||
<item href='fbreader.png' id='cover_image' media-type='image/png'/>
|
||||
</manifest>
|
||||
<spine toc='ncx'>
|
||||
<itemref idref='main'/>
|
||||
</spine>
|
||||
</package>
|
BIN
help/proto/fbreader.png
Normal file
BIN
help/proto/fbreader.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
2
help/proto/style.css
Normal file
2
help/proto/style.css
Normal file
|
@ -0,0 +1,2 @@
|
|||
p { margin: 10px 0 0 0; }
|
||||
p.opds { text-align: center; text-indent: 0; }
|
|
@ -247,7 +247,7 @@ void BookReader::addVideoEntry(const ZLVideoEntry &entry) {
|
|||
}
|
||||
}
|
||||
|
||||
void BookReader::addFBReaderSpecialEntry(const std::string &action, const std::string &data) {
|
||||
void BookReader::addFBReaderSpecialEntry(const std::string &action, const std::map<std::string,std::string> &data) {
|
||||
if (myCurrentTextModel != 0) {
|
||||
myCurrentTextModel->addFBReaderSpecialEntry(action, data);
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
|
||||
|
@ -73,7 +74,7 @@ public:
|
|||
void addImage(const std::string &id, shared_ptr<const ZLImage> image);
|
||||
|
||||
void addVideoEntry(const ZLVideoEntry &entry);
|
||||
void addFBReaderSpecialEntry(const std::string &action, const std::string &data);
|
||||
void addFBReaderSpecialEntry(const std::string &action, const std::map<std::string,std::string> &data);
|
||||
|
||||
void beginContentsParagraph(int referenceNumber = -1);
|
||||
void endContentsParagraph();
|
||||
|
|
|
@ -543,10 +543,7 @@ void XHTMLTagPreAction::doAtEnd(XHTMLReader &reader) {
|
|||
}
|
||||
|
||||
void XHTMLTagOpdsAction::doAtStart(XHTMLReader &reader, const char **xmlattributes) {
|
||||
const char *src = reader.attributeValue(xmlattributes, "src");
|
||||
if (src != 0) {
|
||||
bookReader(reader).addFBReaderSpecialEntry("opds", src);
|
||||
}
|
||||
bookReader(reader).addFBReaderSpecialEntry("opds", reader.attributeMap(xmlattributes));
|
||||
}
|
||||
|
||||
void XHTMLTagOpdsAction::doAtEnd(XHTMLReader &reader) {
|
||||
|
|
|
@ -168,6 +168,20 @@ const char *ZLXMLReader::attributeValue(const char **xmlattributes, const char *
|
|||
return 0;
|
||||
}
|
||||
|
||||
std::map<std::string,std::string> ZLXMLReader::attributeMap(const char **xmlattributes) const {
|
||||
std::map<std::string,std::string> map;
|
||||
while (*xmlattributes != 0) {
|
||||
std::string key = *xmlattributes;
|
||||
++xmlattributes;
|
||||
if (*xmlattributes == 0) {
|
||||
break;
|
||||
}
|
||||
map[key] = *xmlattributes;
|
||||
++xmlattributes;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
ZLXMLReader::NamePredicate::~NamePredicate() {
|
||||
}
|
||||
|
||||
|
|
|
@ -93,6 +93,7 @@ public:
|
|||
|
||||
const char *attributeValue(const char **xmlattributes, const char *name) const;
|
||||
const char *attributeValue(const char **xmlattributes, const NamePredicate &predicate) const;
|
||||
std::map<std::string,std::string> attributeMap(const char **xmlattributes) const;
|
||||
|
||||
private:
|
||||
void initialize(const char *encoding = 0);
|
||||
|
|
|
@ -385,22 +385,32 @@ void ZLTextModel::addVideoEntry(const ZLVideoEntry &entry) {
|
|||
++myParagraphLengths.back();
|
||||
}
|
||||
|
||||
void ZLTextModel::addFBReaderSpecialEntry(const std::string &action, const std::string &data) {
|
||||
ZLUnicodeUtil::Ucs2String ucs2action;
|
||||
ZLUnicodeUtil::utf8ToUcs2(ucs2action, action);
|
||||
ZLUnicodeUtil::Ucs2String ucs2data;
|
||||
ZLUnicodeUtil::utf8ToUcs2(ucs2data, data);
|
||||
void ZLTextModel::addFBReaderSpecialEntry(const std::string &action, const std::map<std::string,std::string> &data) {
|
||||
std::size_t fullLength = 2; // entry type + map size
|
||||
fullLength += 2 + ZLUnicodeUtil::utf8Length(action) * 2; // action name
|
||||
for (std::map<std::string,std::string>::const_iterator it = data.begin(); it != data.end(); ++it) {
|
||||
fullLength += 2 + ZLUnicodeUtil::utf8Length(it->first) * 2; // data key
|
||||
fullLength += 2 + ZLUnicodeUtil::utf8Length(it->second) * 2; // data value
|
||||
}
|
||||
|
||||
const std::size_t actionLength = ucs2action.size() * 2;
|
||||
const std::size_t dataLength = ucs2data.size() * 2;
|
||||
|
||||
myLastEntryStart = myAllocator->allocate(6 + actionLength + dataLength);
|
||||
myLastEntryStart = myAllocator->allocate(fullLength);
|
||||
*myLastEntryStart = ZLTextParagraphEntry::FBREADER_SPECIAL;
|
||||
*(myLastEntryStart + 1) = 0;
|
||||
*(myLastEntryStart + 1) = data.size();
|
||||
|
||||
char *p = myLastEntryStart + 2;
|
||||
ZLUnicodeUtil::Ucs2String ucs2action;
|
||||
ZLUnicodeUtil::utf8ToUcs2(ucs2action, action);
|
||||
p = ZLCachedMemoryAllocator::writeString(p, ucs2action);
|
||||
p = ZLCachedMemoryAllocator::writeString(p, ucs2data);
|
||||
|
||||
for (std::map<std::string,std::string>::const_iterator it = data.begin(); it != data.end(); ++it) {
|
||||
ZLUnicodeUtil::Ucs2String key;
|
||||
ZLUnicodeUtil::utf8ToUcs2(key, it->first);
|
||||
p = ZLCachedMemoryAllocator::writeString(p, key);
|
||||
ZLUnicodeUtil::Ucs2String value;
|
||||
ZLUnicodeUtil::utf8ToUcs2(value, it->second);
|
||||
p = ZLCachedMemoryAllocator::writeString(p, value);
|
||||
}
|
||||
|
||||
myParagraphs.back()->addEntry(myLastEntryStart);
|
||||
++myParagraphLengths.back();
|
||||
}
|
||||
|
|
|
@ -22,9 +22,10 @@
|
|||
|
||||
#include <jni.h>
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include <ZLHyperlinkType.h>
|
||||
#include <ZLTextParagraph.h>
|
||||
|
@ -77,7 +78,7 @@ public:
|
|||
void addFixedHSpace(unsigned char length);
|
||||
void addBidiReset();
|
||||
void addVideoEntry(const ZLVideoEntry &entry);
|
||||
void addFBReaderSpecialEntry(const std::string &action, const std::string &data);
|
||||
void addFBReaderSpecialEntry(const std::string &action, const std::map<std::string,std::string> &data);
|
||||
|
||||
void flush();
|
||||
|
||||
|
|
|
@ -73,14 +73,14 @@ public abstract class BookUtil {
|
|||
final Locale locale = Locale.getDefault();
|
||||
|
||||
ZLResourceFile file = ZLResourceFile.createResourceFile(
|
||||
"data/help/fb-intro-" + locale.getLanguage() + ".epub"
|
||||
"data/intro/intro-" + locale.getLanguage() + ".epub"
|
||||
);
|
||||
if (file.exists()) {
|
||||
return file;
|
||||
}
|
||||
|
||||
file = ZLResourceFile.createResourceFile(
|
||||
"data/help/fb-intro-ru.epub"
|
||||
"data/intro/intro-ru.epub"
|
||||
);
|
||||
if (file.exists()) {
|
||||
return file;
|
||||
|
|
|
@ -19,11 +19,13 @@
|
|||
|
||||
package org.geometerplus.zlibrary.text.model;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class FBReaderSpecialEntry {
|
||||
public final String Type;
|
||||
public final String Data;
|
||||
public final Map<String,String> Data;
|
||||
|
||||
FBReaderSpecialEntry(String type, String data) {
|
||||
FBReaderSpecialEntry(String type, Map<String,String> data) {
|
||||
Type = type;
|
||||
Data = data;
|
||||
}
|
||||
|
|
|
@ -277,10 +277,18 @@ public class ZLTextPlainModel implements ZLTextModel, ZLTextStyleEntry.Feature {
|
|||
final short kindLength = (short)data[dataOffset++];
|
||||
final String kind = new String(data, dataOffset, kindLength);
|
||||
dataOffset += kindLength;
|
||||
final short paramLength = (short)data[dataOffset++];
|
||||
final String param = new String(data, dataOffset, paramLength);
|
||||
dataOffset += paramLength;
|
||||
myFBReaderSpecialEntry = new FBReaderSpecialEntry(kind, param);
|
||||
|
||||
final Map<String,String> map = new HashMap<String,String>();
|
||||
final short dataSize = (short)((first >> 8) & 0xFF);
|
||||
for (short i = 0; i < dataSize; ++i) {
|
||||
final short keyLength = (short)data[dataOffset++];
|
||||
final String key = new String(data, dataOffset, keyLength);
|
||||
dataOffset += keyLength;
|
||||
final short valueLength = (short)data[dataOffset++];
|
||||
map.put(key, new String(data, dataOffset, valueLength));
|
||||
dataOffset += valueLength;
|
||||
}
|
||||
myFBReaderSpecialEntry = new FBReaderSpecialEntry(kind, map);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -111,6 +111,7 @@ public final class ZLTextParagraphCursor {
|
|||
{
|
||||
final FBReaderSpecialEntry entry = it.getFBReaderSpecialEntry();
|
||||
if ("opds".equals(entry.Type)) {
|
||||
System.err.println("OPDS DATA = " + entry.Data);
|
||||
elements.add(new BookElement(null, null));
|
||||
elements.add(new BookElement(null, null));
|
||||
elements.add(new BookElement(null, null));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue