В настоящее время fb2 является популярным форматом для хранения книг. Файл fb2 — это частный случай xml. Основным элементом его структуры, как и для html, являются теги (управляющие слова). В этой статье я покажу вам, как создать простую программу просмотра файлов fb2. Проект с исходным текстом можно скачать по ссылка. .
Главная Информация
Теги делятся на блочные и строчные. Блочные теги группируются попарно от открывающего тега, закрывающего тег, между которым находится контент. Например, абзац текста записывается как
<p>Paragraph text</p>
Внутри такой пары блоков можно поставить другие теги. Теги нижнего регистра используются для объектов, в которые ничего нельзя встроить. Например, указатель на рисунок
<image l:href = “#_0.jpg”/>
содержит информацию: 1) о том, что в данном месте документа необходимо вставить рисунок, 2) ссылку на этот рисунок. Алгоритм вставки картинки в текст описан ниже. Различать 3 типа тегов просто с помощью косой черты. В строчном теге косая черта перед закрывающей скобкой, в закрывающем блоке после открывающей, в открывающем блоке она отсутствует.
Если вы хотите полностью понять, изучите html. Между html и fb2 есть некоторая разница, хотя во многом они идентичны. Такие элементы я буду указывать по ходу повествования. Также обратите внимание, что xml, в отличие от html, не использует язык CSS, в нашем случае это означает, что в файле fb2 нет указаний на то, как отформатирован текст (размер и цвет шрифта, расположение абзацев и т.д.). Все это мы должны (при желании) реализовать самостоятельно.
Структура fb2-файла
Первый тег <?xml> содержит техническую информацию о формате, его версии и используемой кодировке. Второй тег
(как в html ).
Кроме того, вы можете найти теги эпиграфа
После раздела
<?xml …>
<FictionBook …>
<description>
…
</description>
<body>
…
</body>
…
</FictionBook>
). Ссылки могут быть внешними и внутренними. Внешние ссылки в качестве параметра содержат исходный URL, внутренние ссылки содержат ссылки на элементы в тексте файла (см. приведенный выше тег изображения). Чертежи содержат аналогичные внутренние ссылки.
Создание программы чтения
Мы будем строить нашу программу следующим образом: мы будем считывать данные из файла и конвертировать их в html, затем с помощью функции setHtml (QString) отправлять сгенерированную строку в текстовое поле. Маленький лайфхак для тех, кто хочет изучить html: объект класса QTextEdit/QTextBrowser может отображать отформатированный документ как исходный текст. Для этого откройте редактор формы, кликните по объекту 2 раза и перейдите на вкладку «Источник».
Для обработки fb2-файлов будем использовать класс QXmlStreamReader. Для работы с ним необходимо подключить к проекту модули xml и xmlpatterns. В качестве аргумента ему должен быть передан указатель на объект класса QFile.
QFile f(name); QXmlStreamReader sr(&f);
Само открытие файла выглядит как цикл с последовательным чтением строк. Нам также нужны 3 переменные
QString book; QString imgId; QString imgType;
book нужен для хранения сгенерированного документа, imgId и imgType для вставки картинок в текст. Класс QXmlStreamReader производит несколько важных действий. Сначала он определяет и устанавливает нужный декодер. Во-вторых, он отделяет теги от контента. В-третьих, он выделяет свойства тегов. Мы можем обрабатывать только разделенные данные. Для чтения данных используется функция readNext(). Все прочитанные в него фрагменты относятся к одному из 5 типов: StartDocument, EndDocument, StartElement, EndElement и Characters. Из них 2 первыми определяют начало и конец файла, 2 следующими читают теги и последними получают заполнитель.
Получив StartDocument, нам нужно добавить строку заголовка документа html и 2 открывающих тега
book = "<!DOCTYPE HTML><html><body style=\"font-size:14px\">";
При достижении EndDocument мы закрываем теги, открытые в начале файла
book.append("</body></html>");
Внешний вид StartElement означает, что читается открывающий или строчный тег. Соответственно, EndElement сигнализирует о чтении закрывающего тега. Имя тега определяется вызовом функции sr.name(). Нанизывать (). Для управления структурой документа мы будем хранить список всех открытых тегов в объекте thisToken класса QStringList. Поэтому в случае StartElement добавляет имя текущего тега к thisToken и удаляет его в случае EndElement. Кроме того, открывающие (или строчные) теги могут содержать атрибуты. Атрибут будет прочитан и сохранен в sr как массив строк. Вы можете получить к ним доступ с помощью метода sr.attributes(). Они нужны нам для добавления картинок к тексту. Итак, если тег найден, нужно добавить метку к картинке в тексте.
book.append("<p align=\"center\">"+sr.attributes().at(0).value().toString()+"</p>");
Затем, если мы найдем тег
imgId = sr.attributes().at(0).value().toString(); imgType = sr.attributes().at(1).value().toString();
Обратите внимание, что imgId идентичен атрибуту тега
Теперь мы можем поместить содержимое только в книгу строк. Здесь вы можете использовать другой набор правил. Например, игнорировать описание книги
if(thisToken.contains("description")) { break; // не выводим }
или выделить заголовки цветом, размером и типом шрифта. Остановимся только на картинках. Для их вставки необходимо сформировать строку вида
QString image = "<img src=\"data:" + imgType +";base64," + sr.text().toString() + "\"/>";
где sr.text(). toString() содержит содержимое тега
book.replace("#"+imgId, image);
Алгоритм чтения fb2-файла
while( !sr.atEnd() ) { switch( sr.readNext() ) { case QXmlStreamReader::NoToken: qDebug() << "QXmlStreamReader::NoToken"; break; case QXmlStreamReader::StartDocument: book = "<!DOCTYPE HTML><html><body style=\"font-size:14px\">"; break; case QXmlStreamReader::EndDocument: book.append("</body></html>"); break; case QXmlStreamReader::StartElement: thisToken.append( sr.name().toString() ); if( sr.name().toString() == "image" ) // расположение рисунков { if(sr.attributes().count() > 0) book.append("<p align=\"center\">"+sr.attributes().at(0).value().toString()+"</p>"); } if(sr.name() == "binary") // хранилище рисунков { imgId = sr.attributes().at(0).value().toString(); imgType = sr.attributes().at(1).value().toString(); } break; case QXmlStreamReader::EndElement: if( thisToken.last() == sr.name().toString() ) thisToken.removeLast(); else qDebug() << "error token"; break; case QXmlStreamReader::Characters: if( sr.text().toString().contains( QRegExp("[A-Z]|[a-z]|[А-Я]|[а-я]") )) // если есть текст в блоке { if(thisToken.contains("description")) // ОПИСАНИЕ КНИГИ { break; // не выводим } if(thisToken.contains("div")) break; if(!thisToken.contains( "binary" )) book.append("<p>" + sr.text().toString() + "</p>"); } if(thisToken.contains( "binary" ) )//для рисунков { QString image = "<img src=\"data:" + imgType +";base64," + sr.text().toString() + "\"/>"; book.replace("#"+imgId, image); } break; } }
Наш документ готов. Осталось только установить сгенерированную строку в текстовое поле
ui->textBrowser->setHtml(book);
Для полноценной работы fb2-читалки нужно добавить обрабатывающие ссылки, таблицы и некоторые другие объекты. Но приведенного выше материала достаточно, чтобы извлечь основное содержание книги.