1
0
Fork 0
mirror of https://github.com/geometer/FBReaderJ.git synced 2025-10-03 17:59:33 +02:00

intro ePub auto generation

This commit is contained in:
Nikolay Pultsin 2014-12-18 19:33:24 +00:00
parent 3d5ab599f4
commit 7fa54dfd9b
20 changed files with 196 additions and 47 deletions

View file

@ -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>Для перелистывания страницы коснитесь правой части экрана (или левой, если хотите вернуться на предыдущую страницу). Другие варианты перелистывания &#8211; провести по экрану пальцем справа налево, или воспользоваться кнопками регулировки громкости.</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.

View file

@ -55,7 +55,15 @@
<echo message="DONE (Checking if native libraries are up-to-date)"/> <echo message="DONE (Checking if native libraries are up-to-date)"/>
</target> </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>
<!-- <!--
<target name="-pre-compile"> <target name="-pre-compile">
@ -69,6 +77,7 @@
<delete dir="gen" /> <delete dir="gen" />
<delete dir="bin" /> <delete dir="bin" />
<delete dir="out" /> <delete dir="out" />
<delete dir="assets/data/intro" />
<echo message="DONE (Deleting temporary files)" /> <echo message="DONE (Deleting temporary files)" />
</target> </target>

63
help/generate.py Executable file
View 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
View 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 &ndash; свободная программа для чтения электронных книг. Если у вас есть книги в виде файлов <code>ePub</code>, <code>fb2</code>, <code>mobi</code> или во многих других форматах, вы можете скопировать их на это устройство, открыть в FBReader, и читать прямо здесь. &laquo;Не совсем текстовые&raquo; форматы книг, <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. Чтобы вернуться потом к этому тексту, выберите в меню пункт &laquo;Стартовый экран&raquo;.</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'>&laquo;Сетевая библиотека&raquo;</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 выходят не реже раза в месяц. Если вы хотите узнать больше &ndash; заходите <a href='http://ru.fbreader.org/'>на наш сайт</a>, или <a href='http://ru.fbreader.org/content/about-us#social'>ищите нас в социальных сетях</a>.</p>
<h3 id='help'>У меня проблема, кто мне поможет?</h3>
<p>Лучший способ &ndash; написать о проблеме в одной из социальных сетей. Ссылки вы найдёте выше, а в сетях много активных пользователей, которые увидят ваш вопрос. Мы тоже читаем сети, и регулярно в них отвечаем. Если вы уверены, что ваш вопрос обязательно задать напрямую разработчикам, <a href="mailto:contact@fbreader.org">напишите нам электронное письмо</a>.</p>
</body>
</html>

6
help/proto/container.xml Normal file
View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

2
help/proto/style.css Normal file
View file

@ -0,0 +1,2 @@
p { margin: 10px 0 0 0; }
p.opds { text-align: center; text-indent: 0; }

View file

@ -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) { if (myCurrentTextModel != 0) {
myCurrentTextModel->addFBReaderSpecialEntry(action, data); myCurrentTextModel->addFBReaderSpecialEntry(action, data);
} }

View file

@ -22,6 +22,7 @@
#include <vector> #include <vector>
#include <list> #include <list>
#include <map>
#include <stack> #include <stack>
#include <string> #include <string>
@ -73,7 +74,7 @@ public:
void addImage(const std::string &id, shared_ptr<const ZLImage> image); void addImage(const std::string &id, shared_ptr<const ZLImage> image);
void addVideoEntry(const ZLVideoEntry &entry); 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 beginContentsParagraph(int referenceNumber = -1);
void endContentsParagraph(); void endContentsParagraph();

View file

@ -543,10 +543,7 @@ void XHTMLTagPreAction::doAtEnd(XHTMLReader &reader) {
} }
void XHTMLTagOpdsAction::doAtStart(XHTMLReader &reader, const char **xmlattributes) { void XHTMLTagOpdsAction::doAtStart(XHTMLReader &reader, const char **xmlattributes) {
const char *src = reader.attributeValue(xmlattributes, "src"); bookReader(reader).addFBReaderSpecialEntry("opds", reader.attributeMap(xmlattributes));
if (src != 0) {
bookReader(reader).addFBReaderSpecialEntry("opds", src);
}
} }
void XHTMLTagOpdsAction::doAtEnd(XHTMLReader &reader) { void XHTMLTagOpdsAction::doAtEnd(XHTMLReader &reader) {

View file

@ -168,6 +168,20 @@ const char *ZLXMLReader::attributeValue(const char **xmlattributes, const char *
return 0; 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() { ZLXMLReader::NamePredicate::~NamePredicate() {
} }

View file

@ -93,6 +93,7 @@ public:
const char *attributeValue(const char **xmlattributes, const char *name) const; const char *attributeValue(const char **xmlattributes, const char *name) const;
const char *attributeValue(const char **xmlattributes, const NamePredicate &predicate) const; const char *attributeValue(const char **xmlattributes, const NamePredicate &predicate) const;
std::map<std::string,std::string> attributeMap(const char **xmlattributes) const;
private: private:
void initialize(const char *encoding = 0); void initialize(const char *encoding = 0);

View file

@ -385,22 +385,32 @@ void ZLTextModel::addVideoEntry(const ZLVideoEntry &entry) {
++myParagraphLengths.back(); ++myParagraphLengths.back();
} }
void ZLTextModel::addFBReaderSpecialEntry(const std::string &action, const std::string &data) { void ZLTextModel::addFBReaderSpecialEntry(const std::string &action, const std::map<std::string,std::string> &data) {
ZLUnicodeUtil::Ucs2String ucs2action; std::size_t fullLength = 2; // entry type + map size
ZLUnicodeUtil::utf8ToUcs2(ucs2action, action); fullLength += 2 + ZLUnicodeUtil::utf8Length(action) * 2; // action name
ZLUnicodeUtil::Ucs2String ucs2data; for (std::map<std::string,std::string>::const_iterator it = data.begin(); it != data.end(); ++it) {
ZLUnicodeUtil::utf8ToUcs2(ucs2data, data); 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; myLastEntryStart = myAllocator->allocate(fullLength);
const std::size_t dataLength = ucs2data.size() * 2;
myLastEntryStart = myAllocator->allocate(6 + actionLength + dataLength);
*myLastEntryStart = ZLTextParagraphEntry::FBREADER_SPECIAL; *myLastEntryStart = ZLTextParagraphEntry::FBREADER_SPECIAL;
*(myLastEntryStart + 1) = 0; *(myLastEntryStart + 1) = data.size();
char *p = myLastEntryStart + 2; char *p = myLastEntryStart + 2;
ZLUnicodeUtil::Ucs2String ucs2action;
ZLUnicodeUtil::utf8ToUcs2(ucs2action, action);
p = ZLCachedMemoryAllocator::writeString(p, ucs2action); 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); myParagraphs.back()->addEntry(myLastEntryStart);
++myParagraphLengths.back(); ++myParagraphLengths.back();
} }

View file

@ -22,9 +22,10 @@
#include <jni.h> #include <jni.h>
#include <vector>
#include <string>
#include <algorithm> #include <algorithm>
#include <string>
#include <vector>
#include <map>
#include <ZLHyperlinkType.h> #include <ZLHyperlinkType.h>
#include <ZLTextParagraph.h> #include <ZLTextParagraph.h>
@ -77,7 +78,7 @@ public:
void addFixedHSpace(unsigned char length); void addFixedHSpace(unsigned char length);
void addBidiReset(); void addBidiReset();
void addVideoEntry(const ZLVideoEntry &entry); 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(); void flush();

View file

@ -73,14 +73,14 @@ public abstract class BookUtil {
final Locale locale = Locale.getDefault(); final Locale locale = Locale.getDefault();
ZLResourceFile file = ZLResourceFile.createResourceFile( ZLResourceFile file = ZLResourceFile.createResourceFile(
"data/help/fb-intro-" + locale.getLanguage() + ".epub" "data/intro/intro-" + locale.getLanguage() + ".epub"
); );
if (file.exists()) { if (file.exists()) {
return file; return file;
} }
file = ZLResourceFile.createResourceFile( file = ZLResourceFile.createResourceFile(
"data/help/fb-intro-ru.epub" "data/intro/intro-ru.epub"
); );
if (file.exists()) { if (file.exists()) {
return file; return file;

View file

@ -19,11 +19,13 @@
package org.geometerplus.zlibrary.text.model; package org.geometerplus.zlibrary.text.model;
import java.util.Map;
public class FBReaderSpecialEntry { public class FBReaderSpecialEntry {
public final String Type; 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; Type = type;
Data = data; Data = data;
} }

View file

@ -277,10 +277,18 @@ public class ZLTextPlainModel implements ZLTextModel, ZLTextStyleEntry.Feature {
final short kindLength = (short)data[dataOffset++]; final short kindLength = (short)data[dataOffset++];
final String kind = new String(data, dataOffset, kindLength); final String kind = new String(data, dataOffset, kindLength);
dataOffset += kindLength; dataOffset += kindLength;
final short paramLength = (short)data[dataOffset++];
final String param = new String(data, dataOffset, paramLength); final Map<String,String> map = new HashMap<String,String>();
dataOffset += paramLength; final short dataSize = (short)((first >> 8) & 0xFF);
myFBReaderSpecialEntry = new FBReaderSpecialEntry(kind, param); 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; break;
} }
} }

View file

@ -111,6 +111,7 @@ public final class ZLTextParagraphCursor {
{ {
final FBReaderSpecialEntry entry = it.getFBReaderSpecialEntry(); final FBReaderSpecialEntry entry = it.getFBReaderSpecialEntry();
if ("opds".equals(entry.Type)) { 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)); elements.add(new BookElement(null, null));
elements.add(new BookElement(null, null)); elements.add(new BookElement(null, null));