В даний час fb2 є популярним форматом для зберігання книг. Файл fb2 - окремий випадок xml. Основним елементом його структури, як і для html, є тег (керуючі слова). У цій статті я розповім, як створити найпростіший переглядач fb2-файлів. Проект з вихідним текстом можна скачати по посиланням .
Загальні відомості
Теги діляться блокові і малі. Блокові теги групуються в пари з відкриваючого тега, який закриває тега між якими розташовується вміст. Наприклад, абзац тексту записується як
<p>Абзац текст</p>
Всередину такої блокової пари можна помістити інші теги. Рядкові теги використовуються для об'єктів, в які вкласти нічого не можна. Наприклад, покажчик на рисунок
<image l:href = “#_0.jpg”/>
містить інформацію: 1) про те, що в дану точку документа потрібно вставити малюнок, 2) посилання на цей малюнок. Нижче розібраний алгоритм вставки малюнка в текст. Розрізнити 3 типи тегів просто за допомогою слеша. У сатиричного тега слеш перед закривається дужкою, у який закриває блочного після відкривається, у відкриває блочного він відсутній.
Якщо хочете повністю розібратися, вивчайте html. Між html і fb2 є деяка різниця, хоча багато в чому вони ідентичні. На такі елементи я буду вказувати по ходу розповіді. Також зазначу, що xml на відміну від html не використовує мову CSS, в нашому випадку це означає те, що в fb2 файлі немає вказівок на те, як відформатований текст (розмір і колір шрифту, розташування абзаців і т.п.). Все це ми повинні (при бажанні) реалізувати самостійно.
Структура fb2-файлу
У першому тезі <? Xml > файлу міститься технічна інформація про формат, його версії і використовуваного кодування. Другий тег
<?xml …> <FictionBook …> <description> … </description> <body> … </body> … </FictionBook>
Крім того, можна зустріти теги епіграф
Після розділу <body > можуть розташовуватися додаткові елементи. Так в окремих тегах <binary > розміщуються малюнки, перетворені до текстовій формі.
Створення програми-читалки
Нашу програму побудуємо таким чином: будемо зчитувати дані з файлу і перетворювати їх в 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 (). ToString (). Для контролю структури документа будемо зберігати список всіх відкритих тегів в об'єкті thisToken класу QStringList. Тому в разі StartElement додає до thisToken ім'я поточного тега і видаляти його в разі EndElement. Крім того, відкриваються (або рядкові) теги можуть містити атрибути. Атрибуту будуть прочитані і збережені в sr як масив рядків. Доступ до них можна отримати за допомогою методу sr.attributes (). Нам вони знадобляться для додавання малюнків в текст. Так при виявленні тега image необхідно додати в текст мітку цього малюнка.
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 ідентичний атрибуту тега
Тепер нам залишається тільки помістити вміст в рядок book. Тут можна використовувати різний набір правил. Наприклад, ігнорувати опис книги
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-читалки необхідно додати обробку посилань, таблиць та деяких інших об'єктів. Але наведеного матеріалу достатньо вилучення основного вмісту книги.