Skip to content

cpprismic/html-to-pdf-converter

Repository files navigation

Многопоточный асинхронный конвертер HTML в PDF

Оглавление

Описание

Профессиональная реализация многопоточного асинхронного конвертера HTML в PDF на C++. Решение обеспечивает потокобезопасную работу с библиотекой wkhtmltopdf, которая не поддерживает конкурентный доступ из нескольких потоков. Конвертер предназначен для высоконагруженных сред, где требуется параллельная обработка множества документов.

Особенности реализации

Архитектурные особенности

  • Singleton-обертка: Гарантирует единственный экземпляр конвертера в процессе
  • Выделенный рабочий поток: Все операции с wkhtmltopdf выполняются в одном потоке
  • Асинхронная очередь задач: Позволяет добавлять задачи конвертации из multiple потоков
  • Двойной интерфейс: Поддержка как синхронных, так и асинхронных вызовов

Ограничения wkhtmltopdf

Библиотека wkhtmltopdf имеет следующие ограничения:

  • Не поддерживает инициализацию в разных потоках
  • Не является потокобезопасной даже при использовании мьютексов
  • Требует вызова инициализации и деинициализации в одном потоке

Решение проблем многопоточности

// Все операции с wkhtmltopdf выполняются в рабочем потоке
void workerThreadFunction() {
    // Инициализация wkhtmltopdf в worker-потоке
    if (wkhtmltopdf_init(0) != 1) {
        std::cerr << "Failed to initialize wkhtmltopdf" << std::endl;
        return;
    }
    
    // Обработка задач из очереди
    while (running_) {
        // Извлечение и выполнение задач конвертации
    }
    
    wkhtmltopdf_deinit();
}

Требования

Системные требования

  • Компилятор C++17 или новее
  • CMake 3.10+
  • Библиотека wkhtmltopdf (libwkhtmltox)
  • Поддерживаемые платформы: Linux, Windows, macOS

Зависимости

  • wkhtmltopdf: Основная библиотека для конвертации HTML в PDF
  • nlohmann/json: Для генерации статистики работы (опционально)

Установка зависимостей

Ubuntu/Debian

sudo apt-get update
sudo apt-get install libwkhtmltox-dev

Сборка

Прямая компиляция

g++ -std=c++17 -pthread -O2 -lwkhtmltox \
    html2pdf_converter.cpp wkhtml2pdf_wrapper.cpp main.cpp \
    -o html2pdf_converter

Настройка путей к библиотекам

export LD_LIBRARY_PATH=/path/to/wkhtmltopdf/lib:$LD_LIBRARY_PATH
g++ -std=c++17 -pthread -O2 -L/path/to/wkhtmltopdf/lib -lwkhtmltox \
    -I/path/to/wkhtmltopdf/include \
    html2pdf_converter.cpp wkhtml2pdf_wrapper.cpp main.cpp \
    -o html2pdf_converter

Использование

Базовая инициализация

#include "wkhtml2pdf_wrapper.hpp"

int main() {
    // Инициализация конвертера (должна быть вызвана из главного потока)
    auto& converter = WkHtmlToPdfWrapper::getInstance();
    if (!converter.initialize()) {
        std::cerr << "Failed to initialize PDF converter" << std::endl;
        return -1;
    }
    
    // Использование конвертера...
    
    // Завершение работы
    converter.shutdown();
    return 0;
}

Простая конвертация

#include "wkhtml2pdf_wrapper.hpp"

void convertSimpleDocument() {
    auto& converter = WkHtmlToPdfWrapper::getInstance();
    converter.initialize();
    
    // Синхронная конвертация
    bool success = converter.convertSync(
        "input.html",
        "output.pdf"
    );
    
    if (success) {
        std::cout << "Conversion successful" << std::endl;
    } else {
        std::cerr << "Conversion failed" << std::endl;
    }
    
    converter.shutdown();
}

API

Основные классы

WkHtmlToPdfWrapper

Главный класс-обертка для потокобезопасной работы с wkhtmltopdf.

class WkHtmlToPdfWrapper {
public:
    using Callback = std::function<void(bool, const std::string&)>;
    
    // Singleton методы
    static WkHtmlToPdfWrapper& getInstance();
    
    // Управление жизненным циклом
    bool initialize();
    void shutdown(bool wait_for_completion = true);
    
    // Методы конвертации
    bool convertAsync(const std::string& input_html_path,
                     const std::string& output_pdf_path,
                     Callback callback = nullptr);
                     
    bool convertSync(const std::string& input_html_path,
                    const std::string& output_pdf_path);
    
    // Утилиты
    bool isInitialized() const;
    std::string getStats() const;
};

HtmlToPdfConverter

Класс для непосредственной конвертации с настройками.

class HtmlToPdfConverter {
public:
    struct Options {
        std::string page_size;              // A4, Letter, Legal, etc.
        std::string orientation;            // Portrait, Landscape
        int dpi;                           // Разрешение для изображений
        int margin_top;                    // Верхнее поле в мм
        int margin_bottom;                 // Нижнее поле в мм
        int margin_left;                   // Левое поле в мм
        int margin_right;                  // Правое поле в мм
        double zoom;                       // Коэффициент масштабирования
        int minimum_font_size;             // Минимальный размер шрифта
        bool disable_smart_shrinking;      // Отключить умное сжатие
        bool enable_local_file_access;     // Разрешить доступ к локальным файлам
        bool grayscale;                    // Чёрно-белый режим
        bool lowquality;                   // Режим низкого качества
        
        Options(); // Конструктор с значениями по умолчанию
    };
    
    static bool convertFile(const std::string& input_html_path,
                           const std::string& output_pdf_path,
                           const Options& opts = Options());
};

Методы управления

Инициализация и завершение

// Инициализация (обязательна перед использованием)
bool initialize();

// Завершение работы
void shutdown(bool wait_for_completion = true);

Конвертация

// Асинхронная конвертация с callback
bool convertAsync(const std::string& input_html_path,
                 const std::string& output_pdf_path,
                 Callback callback = nullptr);

// Синхронная конвертация (блокирующая)
bool convertSync(const std::string& input_html_path,
                const std::string& output_pdf_path);

Настройки конвертации

HtmlToPdfConverter::Options opts;
opts.page_size = "A4";
opts.orientation = "Landscape";
opts.margin_top = 20;
opts.margin_bottom = 20;
opts.dpi = 150;
opts.zoom = 1.5;
opts.enable_local_file_access = true;

Примеры

Многопоточная конвертация

#include "wkhtml2pdf_wrapper.hpp"
#include <thread>
#include <vector>

void convertMultipleDocuments() {
    auto& converter = WkHtmlToPdfWrapper::getInstance();
    converter.initialize();
    
    std::vector<std::thread> threads;
    std::vector<std::string> documents = {
        "doc1.html", "doc2.html", "doc3.html", "doc4.html"
    };
    
    // Запуск конвертации в нескольких потоках
    for (size_t i = 0; i < documents.size(); ++i) {
        threads.emplace_back([&converter, i, &documents]() {
            std::string output_file = "output_" + std::to_string(i) + ".pdf";
            bool success = converter.convertSync(documents[i], output_file);
            
            if (success) {
                std::cout << "Converted " << documents[i] << " to " << output_file << std::endl;
            } else {
                std::cerr << "Failed to convert " << documents[i] << std::endl;
            }
        });
    }
    
    // Ожидание завершения всех потоков
    for (auto& thread : threads) {
        thread.join();
    }
    
    converter.shutdown();
}

Асинхронная конвертация с callback

#include "wkhtml2pdf_wrapper.hpp"

void asyncConversionExample() {
    auto& converter = WkHtmlToPdfWrapper::getInstance();
    converter.initialize();
    
    // Callback функция для обработки результатов
    auto callback = [](bool success, const std::string& error_message) {
        if (success) {
            std::cout << "Conversion completed successfully" << std::endl;
        } else {
            std::cerr << "Conversion failed: " << error_message << std::endl;
        }
    };
    
    // Асинхронная конвертация
    converter.convertAsync("input.html", "output.pdf", callback);
    
    // Можно продолжать выполнение других задач...
    std::this_thread::sleep_for(std::chrono::seconds(2));
    
    converter.shutdown(true); // Ждем завершения всех задач
}

Пакетная обработка с настройками

#include "wkhtml2pdf_wrapper.hpp"
#include "html2pdf_converter.hpp"

void batchProcessingWithOptions() {
    auto& wrapper = WkHtmlToPdfWrapper::getInstance();
    wrapper.initialize();
    
    // Настройки для разных типов документов
    HtmlToPdfConverter::Options report_opts;
    report_opts.page_size = "A4";
    report_opts.orientation = "Portrait";
    report_opts.margin_top = 20;
    report_opts.margin_bottom = 20;
    
    HtmlToPdfConverter::Options presentation_opts;
    presentation_opts.page_size = "A4";
    presentation_opts.orientation = "Landscape";
    presentation_opts.margin_top = 10;
    presentation_opts.margin_bottom = 10;
    
    // Пакетная конвертация
    std::vector<std::pair<std::string, std::string>> documents = {
        {"report.html", "report.pdf"},
        {"presentation.html", "presentation.pdf"},
        {"manual.html", "manual.pdf"}
    };
    
    for (const auto& doc : documents) {
        bool success = wrapper.convertSync(doc.first, doc.second);
        std::cout << doc.first << " -> " << doc.second 
                  << ": " << (success ? "SUCCESS" : "FAILED") << std::endl;
    }
    
    wrapper.shutdown();
}

Мониторинг производительности

// Получение статистики работы
std::string stats = converter.getStats();
std::cout << "Converter statistics:" << std::endl;
std::cout << stats << std::endl;

Пример вывода статистики:

{
  "initialized": true,
  "running": true,
  "total_tasks": 15,
  "completed_tasks": 12,
  "failed_tasks": 1,
  "queue_size": 2,
  "last_error": ""
}

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages