Программа для чтения электронных книг FictionBook9 ноября 2009 09:32, 14 комментариев

Разработки, Электронные книги

Чуть больше года тому назад мое давнишнее увлечение чтением электронных книг привело меня к формату FictionBook, о программе для чтения которого и пойдет речь.

До этого я читал по большей части в PDF и CHM. Но первый задумывался как формат для печати со всеми вытекающими, а второй делался только для виндовоза. Хотя, и в других осях читать его можно, но это не наш путь. :-)

Через какое-то время мне попался на глаза FictionBook, и выбор пал на него - он мне понравился сразу. Формат изначально задуман для электронных книг и лишен неудобного наследства бумажных книг. Выбор программ для чтения оказался невелик - большинство только под виндовоз или мне не нравятся. А поскольку в Гноме на моей любимой FreeBSD читать толком не чем, решил написать свою.

Средства и инструменты долго выбирать не пришлось - уже давно подсел на GTK+ и Анюту. Для чтения XML-файла с книгой - libxml2 как хорошо проверенную временем и быструю библиотеку. Для хранения служебной и прочей информации - встраиваемую базу данных sqlite3. Все эти средства переносимы, стало быть, работают на разных платформах. С лицензией вопрос тоже не стоял - GPLv3.

Определимся с основными задачами, которые программа должна решать:

  • Полная поддержка формата - что, как оказалось, редкость (забегая вперед, скажу, что от поддержки CSS я все же отказался).
  • Закладки (автоматическое запоминание места, где закрыл книгу, плюс пользовательские закладки).
  • Навигация (оглавление, ссылки, вложения и история).
  • Библиотека (уже на 50-ти книгах я начал ловить себя на том, что ищу нужную мне книгу глазами, а когда их будет тысяча?).
  • Настройка отображения самой книги (формат, за небольшим исключением, не оговаривает способ отображения текста, возлагая эту задачу на программу для чтения).
  • Кое какие дополнительные функции, о которых пока промолчу.

Кроме того, неплохо было бы реализовать интеграцию с Гномом. А это зарегистрированный в среде MIME-тип, ассоциация его с программой, предосмотр в Наутилусе (другими словами, в папках вместо иконки файла видна обложка книжки) и, конечно же, видимость программы в системном меню.

Первая версия программы использовала технологию DOM как одну из самых простых. Суть DOM такова, что в памяти создается структура, полностью соответствующая файлу по иерархии и содержимому. А дальше эта структура читается программой, и из нее извлекаются данные. Прочитав пару десятков книг в своей еще не готовой до конца программе, я убедился, что DOM не совсем подходит для решения задачи. Во-первых, файл парсится минимум в два прохода, а это накладные расходы. Во-вторых, поскольку структура строится в памяти, расход памяти становится просто неприличным. В-третьих, ограничения технологии DOM, на которые я наткнулся, не давали реализовать задуманное простым и элегантным способом. В итоге я решил переписать программу заново с использованием технологии SAX. Это очень быстрая событийная технология, читающая XML-файл за один проход, использующая минимум памяти, а главное, ниже уровнем, чем DOM (SAX все равно работает внутри DOM, когда в памяти создается структура XML-файла). Программного кода в итоге получилось значительно больше, но скорость чтения ощутимо возросла. Плюс к тому, я учел опыт создания и использования первой версии.

Следующей проблемой оказались иллюстрации - они хранятся в файле в формате base64, а это означает необходимость раскодирования перед отображением в программу. Когда в файле единственная картинка - это обложка, то никаких проблем не возникает. Но мне попалась электронная энциклопедия с количеством иллюстраций более пятисот - вот тут пауза при открытии была уже несколько секунд. Проблема решается очевидно - раскодировать картинки отдельным потоком, а поскольку их, картинок, может быть много, то пулом потоков (потому как многократно создавать и освобождать потоки тоже накладные расходы).

Исполняемый файл отдельной программы для показа обложек вместо иконок в Гноме получился чуть больше 10 Кб. Когда я прикрутил его к Гному, на папки с книгами стало приятно смотреть - прямо как в библиотеке. :-)

В интерфейсе программы я старался следовать философии HIG, то есть простота и понятность интерфейса вкупе с продуманной организацией управляющих элементов. По совету разработчиков GTK+ я поглядывал на организацию интерфейса таких программ, как Evince и gedit (а на gedit еще и по организации программного кода). Что мне не нравится в аналогичных программах для чтения - так это большая привязка к формату, чем к интерфейсу. Этого я решил избежать. По моему мнению, читатель не должен и понятия иметь о внутренней структуре файла книги, но должен видеть перед собой очевидные и уже привычные из других программ инструменты.

С самого начала разработки я отдавал себе отчет в том, что без программы-библиотеки мне не обойтись. Я реализовал ее как отдельное окно в программе плюс возможность запуска библиотеки отдельно от читалки с помощью ключа командной строки. Посмотрев на организацию интерфейса аналогичных программ, я получил неприятные впечатления. Во-первых, многошаговые операции с деревом (аля жанр -> автор -> серия -> книга), во-вторых, неочевидная организация интерфейса вообще (включая показ непосредственно книг), в-третьих, отсутствие пользовательских инструментов для управления библиотекой (я не имею в виду разного рода импорты-экспорты и прочие сторонние для библиотеки функции). Мне гораздо больше импонирует логика таких программ как Rhythmbox где можно самому организовать структуру свой библиотеки.

Поскольку библиотека, да и сама программа в целом является однопользовательской, по моему мнению, логично использовать для хранения встраиваемую базу данных. Вот тут я открыл для себя sqlite3 - очень быстрая и мощная база данных (тут тебе и реляционные связи, и индексы, и проверки и прочие приятные мелочи, которые скорее ожидаешь увидеть в "тяжелых" СУБД). А коль скоро я задумал программу как межплатформенную, то решил хранить в sqlite3 вообще все: и пользовательские настройки, и закладки, и служебную информацию библиотеки. Дабы не плодить излишние сущности типа GConf или виндовозного реестра.

Хранить сами книги в библиотеке (сиречь, непосредственно в базе данных) я считаю неправильным, ибо задача библиотеки не хранение книг как таковое, а предоставление пользователю информации об имеющихся книгах плюс некоторый сервис. Таким образом, я решил хранить в базе только служебную информацию о книгах (название, имя автора, обложку и тому подобное), а сами книги в файловой системе.

Пользовательские настройки программы я решил разделить на общие и относящиеся непосредственно к книге. Дабы не усложнять настройку и соответствовать принципу KISS.

От полной реализации стандарта я решил отказаться, а именно от реализации стилей CSS. Во-первых, какая именно из версий CSS должна поддерживаться спецификацией стандарта не оговаривается, во-вторых, верстальщиками книг почти не используется, а, в-третьих, в следующей версии формата стили планируется убрать (я узнал это от авторов формата).

С таблицами вообще получилось забавно - авторы читалок говорят - зачем нам это реализовывать, если толком нету редакторов, которые их поддерживают, а авторы редакторов, в свою очередь, говорят - зачем нам это делать, если нету читалок, которые умеют читать таблицы. Замкнутый круг. :-) Тем не менее, сделать поддержку таблиц в своей программе я считаю необходимым.

В итоге в настоящее время я занимаюсь доделкой разного рода "рюшечек", читать без которых можно, но неудобно, типа точной прокрутки с начала строки, а не с ее середины, выпадающих меню и тому подобного. Планирую закончить программу в ближайшее время.

Добавьте свой комментарий

К этой статье в настоящий момент 14 комментариев. Если вам есть, что добавить, вы можете оставить здесь и свой комментарий. Поля имя и почтовый адрес обязательны для заполнения. Адрес на сайте не публикуется.

Цитаты оформляются так: /* Цитируемый текст */.