Дмитрий
Дмитрий29 серпня 2017 р. 15:26

Відкриваємо fb2-файли засобами Qt

В даний час fb2 є популярним форматом для зберігання книг. Файл fb2 - окремий випадок xml. Основним елементом його структури, як і для html, є тег (керуючі слова). У цій статті я розповім, як створити найпростіший переглядач fb2-файлів. Проект з вихідним текстом можна скачати по посиланням .

Загальні відомості

Теги діляться блокові і малі. Блокові теги групуються в пари з відкриваючого тега, який закриває тега між якими розташовується вміст. Наприклад, абзац тексту записується як

<p>Абзац текст</p>

Всередину такої блокової пари можна помістити інші теги. Рядкові теги використовуються для об'єктів, в які вкласти нічого не можна. Наприклад, покажчик на рисунок

<image l:href = “#_0.jpg”/>

містить інформацію: 1) про те, що в дану точку документа потрібно вставити малюнок, 2) посилання на цей малюнок. Нижче розібраний алгоритм вставки малюнка в текст. Розрізнити 3 типи тегів просто за допомогою слеша. У сатиричного тега слеш перед закривається дужкою, у який закриває блочного після відкривається, у відкриває блочного він відсутній.

Якщо хочете повністю розібратися, вивчайте html. Між html і fb2 є деяка різниця, хоча багато в чому вони ідентичні. На такі елементи я буду вказувати по ходу розповіді. Також зазначу, що xml на відміну від html не використовує мову CSS, в нашому випадку це означає те, що в fb2 файлі немає вказівок на те, як відформатований текст (розмір і колір шрифту, розташування абзаців і т.п.). Все це ми повинні (при бажанні) реалізувати самостійно.

Структура fb2-файлу

У першому тезі <? Xml > файлу міститься технічна інформація про формат, його версії і використовуваного кодування. Другий тег охоплює саму книгу цілком. Як правило, в будь-якій книзі є 2 частини: опис і основна частина (як в html). Опис містить ім'я автора, назва книги, анотацію, і т.д. Основна частина містить заголовки (всієї книги або окремих глав), глави / частини / розділи <section> і текст <p > (як в html). </section>

<?xml …>
  <FictionBook …>
    <description>
    …
    </description>
    <body>
    …
    </body>
  …
  </FictionBook>

Крім того, можна зустріти теги епіграф , посилання (як в html), малюнок <image / > і порожній рядок <empty-line / > (в html <br/>). Посилання можуть бути зовнішні і внутрішні. Зовнішні посилання в якості параметра містять URL джерела, внутрішні - містять посилання на елементи в тексті файлу (див. Вище тег image). Малюнки містять аналогічні внутрішні посилання.

Після розділу <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-читалки необхідно додати обробку посилань, таблиць та деяких інших об'єктів. Але наведеного матеріалу достатньо вилучення основного вмісту книги.

Рекомендуємо хостинг TIMEWEB
Рекомендуємо хостинг TIMEWEB
Стабільний хостинг, на якому розміщується соціальна мережа EVILEG. Для проектів на Django радимо VDS хостинг.

Вам це подобається? Поділіться в соціальних мережах!

Коментарі

Only authorized users can post comments.
Please, Log in or Sign up
sf

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:90бали,
  • Рейтинг балів8
МВ

Qt - Тест 001. Сигналы и слоты

  • Результат:68бали,
  • Рейтинг балів-1
ЛС

C++ - Тест 001. Первая программа и типы данных

  • Результат:53бали,
  • Рейтинг балів-4
Останні коментарі
A
ALO1ZE19 жовтня 2024 р. 08:19
Читалка файлів fb3 на Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь Максимов05 жовтня 2024 р. 07:51
Django - Урок 064. Як написати розширення для Python Markdown Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas505 липня 2024 р. 11:02
QML - Урок 016. База даних SQLite та робота з нею в QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
k
kmssr08 лютого 2024 р. 18:43
Qt Linux - Урок 001. Автозапуск програми Qt під Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
АК
Анатолий Кононенко05 лютого 2024 р. 01:50
Qt WinAPI - Урок 007. Робота з ICMP Ping в Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
Тепер обговоріть на форумі
J
JacobFib17 жовтня 2024 р. 03:27
добавить qlineseries в функции Пользователь может получить любые разъяснения по интересующим вопросам, касающимся обработки его персональных данных, обратившись к Оператору с помощью электронной почты https://topdecorpro.ru…
ИМ
Игорь Максимов03 жовтня 2024 р. 04:05
Реализация навигации по разделам Спасибо Евгений!
JW
Jhon Wick01 жовтня 2024 р. 15:52
Indian Food Restaurant In Columbus OH| Layla’s Kitchen Indian Restaurant If you're looking for a truly authentic https://www.laylaskitchenrestaurantohio.com/ , Layla’s Kitchen Indian Restaurant is your go-to destination. Located at 6152 Cleveland Ave, Colu…
КГ
Кирилл Гусарев27 вересня 2024 р. 09:09
Не запускается программа на Qt: точка входа в процедуру не найдена в библиотеке DLL Написал программу на C++ Qt в Qt Creator, сбилдил Release с помощью MinGW 64-bit, бинарнику напихал dll-ки с помощью windeployqt.exe. При попытке запуска моей сбилженной программы выдаёт три оши…
F
Fynjy22 липня 2024 р. 04:15
при создании qml проекта Kits есть но недоступны для выбора Поставил Qt Creator 11.0.2. Qt 6.4.3 При создании проекта Qml не могу выбрать Kits, они все недоступны, хотя настроены и при создании обычного Qt Widget приложения их можно выбрать. В чем может …

Слідкуйте за нами в соціальних мережах