Дневник Шестеро Михаила

Oct. 9th, 2014

03:30 am - Преобразование HTML в XHTML на C и C++

Задача преобразования HTML-форматированного текста в XHTML появилась в связи с тем, что нужно было вставлять в XML блоки форматированного текста. (Про чтение таких блоков при SAX-разборе я написал статью: http://lj.rossia.org/users/shestero/141287.html ). Эти вставки писались вручную и часто содержали ошибки форматирования, невидные при просмотре в браузерах, но неприемлимые для строгих парсеров XML. Кроме того в исходных текстах часто были непарные теги, вроде <BR>, которые каждый раз приходилось отыскивать и исправлять вручную.

Для выполнения этой задачи на C++ я успешно приминил бесплатную библиотку Tidy. К сожалению разработка её остановилась в начале 2009, но для моей задачи она сгодилась.

Я использовал MinGW, компилировал из коммандной строки-консоли Qt 4.8.3 под Windows 7.
Так как исходники уже довольно древние, при компиляции возникают небольшие загвоздки. Вот пошаговая инструкция, как я делал:
1. Использовал последний tarbar-архив из CSV (март 2009).
2. После распаковки надо создать вручную директории obj (где Makefile) и bin c lib (в директории tidy) (сами они не создаются)
3. Я использовал Makefile для gmake, но его пришлось изрядно поправить. Также для компиляции в MinGW пришлось поправить директиву выбора блока в файле src/mappedio.c. Исправленные файлы я выложил в архив tidy-changes.7z [здесь].
4. Для сборки библиотеки запустите make из директории build\gmake.

Вот код на C++, обеспечивающий конвертацию:

// Convert HTML to XHTML and clean up using libTidy
#include <tidy.h>
#include <buffio.h>
string CleanHTML(const char *html)
{
    // Initialize a Tidy document
    TidyDoc tidyDoc = tidyCreate();
    TidyBuffer tidyOutputBuffer = {0};

    // Configure Tidy
    // The flags tell Tidy to output XML and disable showing warnings
    bool configSuccess = tidyOptSetBool(tidyDoc, TidyXmlOut, yes)
        && tidyOptSetBool(tidyDoc, TidyQuiet, yes)
        && tidyOptSetBool(tidyDoc, TidyNumEntities, yes)
        && tidyOptSetBool(tidyDoc, TidyShowWarnings, yes) // no
        ;//&& tidyOptSetValue(tidyDoc,TidyCharEncoding, "utf8");

    int tidyResponseCode = -1;

    // Parse input
    if (configSuccess)
        tidyResponseCode = tidyParseString(tidyDoc, html);

    // Process HTML
    if (tidyResponseCode >= 0)
        tidyResponseCode = tidyCleanAndRepair(tidyDoc);

    // Output the HTML to our buffer
    if (tidyResponseCode >= 0)
        tidyResponseCode = tidySaveBuffer(tidyDoc, &tidyOutputBuffer);

    // Any errors from Tidy?
    if (tidyResponseCode < 0) // Tidy encountered an error while parsing an HTML
        throw tidyResponseCode;

    // Grab the result from the buffer and then free Tidy's memory
    std::string tidyResult = (char*)tidyOutputBuffer.bp;
    tidyBufFree(&tidyOutputBuffer);
    tidyRelease(tidyDoc);

    return tidyResult;
}
// ................

        // Convert HTML to XHTML and clean it
        // see also: https://bugs.webkit.org/show_bug.cgi?id=44876
        string xhtml;
        try
        {
            xhtml = CleanHTML( html );
        }
        catch (int e)
        {
            cerr << "Clean HTML error (from libTidy): " << e << endl;
        }
Добавлю, что в моём случае обработка UTF-8 символов (кирилицы) в приложении, запущенном под Windows 7 x64 происходила не правильно (они конвертировались в кодовые &-представления побайтно). Так как мне это не было нужно в тот момент, я не стал разбираться в чём там дело, возможно просто в системных настройках локалей или в каких-то параметрах запуска библиотечных функций или компиляции. Вроде бы Tidy поддерживает UTF-8. Для обработки кириличных HTML-текстов также можно перевести их в однобайтовую кодировку, например в CP-1251.

Для C++ также существует специальная обёртка Tidy-библиотеки TidyPP: http://code.google.com/p/tidypp

Tags: , , ,
Current Mood: busy