From ddaf2f01b7ac3246fdf6f11087fc00ade939a141 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Mon, 11 Dec 2023 18:31:38 +0100 Subject: [PATCH] Split the main PDF converter code into its own class --- es-pdf-converter/CMakeLists.txt | 18 ++- es-pdf-converter/src/ConvertPDF.cpp | 194 ++++++++++++++++++++++++++++ es-pdf-converter/src/ConvertPDF.h | 35 +++++ es-pdf-converter/src/main.cpp | 115 +---------------- 4 files changed, 249 insertions(+), 113 deletions(-) create mode 100644 es-pdf-converter/src/ConvertPDF.cpp create mode 100644 es-pdf-converter/src/ConvertPDF.h diff --git a/es-pdf-converter/CMakeLists.txt b/es-pdf-converter/CMakeLists.txt index 242efa8ad..23ed46a90 100644 --- a/es-pdf-converter/CMakeLists.txt +++ b/es-pdf-converter/CMakeLists.txt @@ -21,13 +21,27 @@ if(WIN32) elseif(APPLE) set(POPPLER_CPP_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../external/poppler/cpp ${CMAKE_CURRENT_SOURCE_DIR}/../external/poppler/build/cpp) set(POPPLER_CPP_LIBRARY ${CMAKE_CURRENT_SOURCE_DIR}/../libpoppler-cpp.0.dylib) +elseif(ANDROID) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -llog") + set(POPPLER_CPP_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../external/poppler/cpp ${CMAKE_CURRENT_SOURCE_DIR}/../external/poppler/build/cpp) + set(POPPLER_CPP_LIBRARY ${CMAKE_CURRENT_SOURCE_DIR}/../android/libs/${ANDROID_CPU_ARCH}/libpoppler-cpp.so) else() find_package(Poppler REQUIRED COMPONENTS cpp) endif() include_directories(${POPPLER_CPP_INCLUDE_DIR}) -add_executable(es-pdf-convert WIN32 ${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp) -target_link_libraries(es-pdf-convert ${POPPLER_CPP_LIBRARY}) +if (ANDROID) + set(CONVERTER_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/ConvertPDF.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ConvertPDF.h) + add_library(es-pdf-convert SHARED ${CONVERTER_SOURCE_FILES}) +else() + set(CONVERTER_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ConvertPDF.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ConvertPDF.h) + add_executable(es-pdf-convert WIN32 ${CONVERTER_SOURCE_FILES}) +endif() +target_link_libraries(es-pdf-convert PRIVATE ${POPPLER_CPP_LIBRARY}) if(WIN32) set_target_properties(es-pdf-convert PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/es-pdf-converter" INSTALL_RPATH_USE_LINK_PATH TRUE) diff --git a/es-pdf-converter/src/ConvertPDF.cpp b/es-pdf-converter/src/ConvertPDF.cpp new file mode 100644 index 000000000..a665a2db6 --- /dev/null +++ b/es-pdf-converter/src/ConvertPDF.cpp @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// ES-DE +// ConvertPDF.cpp +// +// Converts PDF document pages to raw ARGB32 pixel data for maximum performance. +// This needs to be separated into its own binary to get around the restrictive GPL +// license used by the Poppler PDF rendering library. +// + +#include "ConvertPDF.h" + +#include "poppler-document.h" +#include "poppler-image.h" +#include "poppler-page-renderer.h" +#include "poppler-page.h" + +#include +#include +#include + +#if defined(__ANDROID__) +#include +#endif + +#if defined(_WIN64) +#include +#include +#include +#endif + +#if defined(_WIN64) +int ConvertPDF::processFile( + const std::wstring path, const std::wstring mode, int pageNum, int width, int height) +#elif defined(__ANDROID__) +int ConvertPDF::processFile(const std::string path, + const std::string mode, + int pageNum, + int width, + int height, + std::string& result) +#else +int ConvertPDF::processFile( + const std::string path, const std::string mode, int pageNum, int width, int height) +#endif +{ + std::ifstream file; + + file.open(path.c_str(), std::ifstream::binary); + if (file.fail()) { +#if defined(__ANDROID__) + __android_log_print(ANDROID_LOG_ERROR, ANDROID_APPLICATION_ID, + "Error: Couldn't open PDF file, permission problems?"); +#else + std::cerr << "Error: Couldn't open PDF file, permission problems?" << std::endl; +#endif + return (-1); + } + + file.seekg(0, std::ios::end); + const long fileLength {static_cast(file.tellg())}; + file.seekg(0, std::ios::beg); + std::vector fileData(fileLength); + file.read(&fileData[0], fileLength); + file.close(); + + const poppler::document* document { + poppler::document::load_from_raw_data(&fileData[0], fileLength)}; + + if (document == nullptr) { +#if defined(__ANDROID__) + __android_log_print(ANDROID_LOG_ERROR, ANDROID_APPLICATION_ID, + "Error: Couldn't open document, invalid PDF file?"); +#else + std::cerr << "Error: Couldn't open document, invalid PDF file?" << std::endl; +#endif + return (-1); + } + + const int pageCount {document->pages()}; +#if defined(_WIN64) + if (mode == L"-fileinfo") { +#else + if (mode == "-fileinfo") { +#endif + std::vector pageInfo; + for (int i {0}; i < pageCount; ++i) { + std::string pageRow; + const poppler::page* page {document->create_page(i)}; + if (page == nullptr) { + if (page == nullptr) { +#if defined(__ANDROID__) + __android_log_print(ANDROID_LOG_ERROR, ANDROID_APPLICATION_ID, + "Error: Couldn't read page %i", i + 1); +#else + std::cerr << "Error: Couldn't read page " << i + 1 << std::endl; +#endif + return (-1); + } + } + + std::string orientation; + if (page->orientation() == poppler::page::portrait) + orientation = "portrait"; + else if (page->orientation() == poppler::page::upside_down) + orientation = "upside_down"; + else if (page->orientation() == poppler::page::seascape) + orientation = "seascape"; + else + orientation = "landscape"; + + const poppler::rectf pageRect {page->page_rect()}; + pageRow.append(std::to_string(i + 1)) + .append(";") + .append(orientation) + .append(";") + .append(std::to_string(pageRect.width())) + .append(";") + .append(std::to_string(pageRect.height())); + pageInfo.emplace_back(pageRow); + } + for (auto& row : pageInfo) { +#if defined(__ANDROID__) + result.append(row).append("\n"); +#else + std::cout << row << std::endl; +#endif + } + return (0); + } + + if (pageNum < 1 || pageNum > pageCount) { +#if defined(__ANDROID__) + __android_log_print(ANDROID_LOG_ERROR, ANDROID_APPLICATION_ID, + "Error: Requested page %i does not exist in document", pageNum); +#else + std::cerr << "Error: Requested page " << pageNum << " does not exist in document" + << std::endl; +#endif + return (-1); + } + +#if defined(__ANDROID__) + __android_log_print(ANDROID_LOG_ERROR, "org.es_de.frontend", "PDF CONVERTING BREAK 10"); +#endif + + const poppler::page* page {document->create_page(pageNum - 1)}; + + if (page == nullptr) { +#if defined(__ANDROID__) + __android_log_print(ANDROID_LOG_ERROR, ANDROID_APPLICATION_ID, + "Error: Couldn't read page %i", pageNum); +#else + std::cerr << "Error: Couldn't read page " << pageNum << std::endl; +#endif + return (-1); + } + + poppler::page_renderer pageRenderer; + + pageRenderer.set_render_hint(poppler::page_renderer::text_antialiasing); + pageRenderer.set_render_hint(poppler::page_renderer::antialiasing); + // pageRenderer.set_render_hint(poppler::page_renderer::text_hinting); + + const poppler::rectf pageRect {page->page_rect()}; + const bool rotate {page->orientation() == poppler::page::portrait || + page->orientation() == poppler::page::upside_down}; + const double pageHeight {pageRect.height()}; + const double sizeFactor {static_cast(rotate ? height : width) / pageHeight}; + + poppler::image image { + pageRenderer.render_page(page, 72.0 * sizeFactor, 72.0 * sizeFactor, 0, 0, width, height)}; + + if (!image.is_valid()) { +#if defined(__ANDROID__) + __android_log_print(ANDROID_LOG_ERROR, ANDROID_APPLICATION_ID, "Rendered image is invalid"); +#else + std::cerr << "Rendered image is invalid" << std::endl; +#endif + return (-1); + } + +#if defined(__ANDROID__) + result.insert(0, std::move(image.data()), width * height * 4); +#else + // Necessary as the image data stream may contain null characters. + std::string imageARGB32; + imageARGB32.insert(0, std::move(image.data()), width * height * 4); + + std::cout << imageARGB32; +#endif + + return 0; +} \ No newline at end of file diff --git a/es-pdf-converter/src/ConvertPDF.h b/es-pdf-converter/src/ConvertPDF.h new file mode 100644 index 000000000..8f9a33bfb --- /dev/null +++ b/es-pdf-converter/src/ConvertPDF.h @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// ES-DE +// ConvertPDF.h +// +// Converts PDF document pages to raw ARGB32 pixel data for maximum performance. +// This needs to be separated into its own binary to get around the restrictive GPL +// license used by the Poppler PDF rendering library. +// + +#include + +#ifndef ES_PDF_CONVERTER_CONVERT_PDF_H +#define ES_PDF_CONVERTER_CONVERT_PDF_H + +class ConvertPDF +{ +public: +#if defined(_WIN64) + static int processFile( + const std::wstring path, const std::wstring mode, int pageNum, int width, int height); +#elif defined(__ANDROID__) + __attribute__((visibility("default"))) static int processFile(const std::string path, + const std::string mode, + int pageNum, + int width, + int height, + std::string& result); +#else + static int processFile( + const std::string path, const std::string mode, int pageNum, int width, int height); +#endif +}; + +#endif // ES_PDF_CONVERTER_CONVERT_PDF_H diff --git a/es-pdf-converter/src/main.cpp b/es-pdf-converter/src/main.cpp index 8418fb5d9..552cf7087 100644 --- a/es-pdf-converter/src/main.cpp +++ b/es-pdf-converter/src/main.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only // -// EmulationStation Desktop Edition (ES-DE) PDF converter +// ES-DE PDF converter // main.cpp // // Converts PDF document pages to raw ARGB32 pixel data for maximum performance. @@ -11,13 +11,8 @@ // All ES-DE C++ source code is formatted using clang-format. // -#include "poppler-document.h" -#include "poppler-image.h" -#include "poppler-page-renderer.h" -#include "poppler-page.h" +#include "ConvertPDF.h" -#include -#include #include #if defined(_WIN64) @@ -101,7 +96,7 @@ int main(int argc, char* argv[]) pageNum = atoi(argv[3]); width = atoi(argv[4]); height = atoi(argv[5]); -#endif +#endif // _WIN64 if (width < 1 || width > 7680) { std::cerr << "Invalid horizontal resolution defined: " << argv[3] << std::endl; exit(-1); @@ -116,107 +111,5 @@ int main(int argc, char* argv[]) // << width << "x" << height << " pixels" << std::endl; } - std::ifstream file; - - file.open(path.c_str(), std::ifstream::binary); - if (file.fail()) { - std::cerr << "Error: Couldn't open PDF file, permission problems?" << std::endl; - exit(-1); - } - - file.seekg(0, std::ios::end); - const long fileLength {static_cast(file.tellg())}; - file.seekg(0, std::ios::beg); - std::vector fileData(fileLength); - file.read(&fileData[0], fileLength); - file.close(); - - const poppler::document* document { - poppler::document::load_from_raw_data(&fileData[0], fileLength)}; - - if (document == nullptr) { - std::cerr << "Error: Couldn't open document, invalid PDF file?" << std::endl; - exit(-1); - } - - const int pageCount {document->pages()}; -#if defined(_WIN64) - if (mode == L"-fileinfo") { -#else - if (mode == "-fileinfo") { -#endif - std::vector pageInfo; - for (int i {0}; i < pageCount; ++i) { - std::string pageRow; - const poppler::page* page {document->create_page(i)}; - if (page == nullptr) { - if (page == nullptr) { - std::cerr << "Error: Couldn't read page " << i + 1 << std::endl; - exit(-1); - } - } - std::string orientation; - if (page->orientation() == poppler::page::portrait) - orientation = "portrait"; - else if (page->orientation() == poppler::page::upside_down) - orientation = "upside_down"; - else if (page->orientation() == poppler::page::seascape) - orientation = "seascape"; - else - orientation = "landscape"; - - const poppler::rectf pageRect {page->page_rect()}; - pageRow.append(std::to_string(i + 1)) - .append(";") - .append(orientation) - .append(";") - .append(std::to_string(pageRect.width())) - .append(";") - .append(std::to_string(pageRect.height())); - pageInfo.emplace_back(pageRow); - } - for (auto& row : pageInfo) - std::cout << row << std::endl; - exit(0); - } - - if (pageNum < 1 || pageNum > pageCount) { - std::cerr << "Error: Requested page " << pageNum << " does not exist in document" - << std::endl; - exit(-1); - } - - const poppler::page* page {document->create_page(pageNum - 1)}; - - if (page == nullptr) { - std::cerr << "Error: Couldn't read page " << pageNum << std::endl; - exit(-1); - } - - poppler::page_renderer pageRenderer; - - pageRenderer.set_render_hint(poppler::page_renderer::text_antialiasing); - pageRenderer.set_render_hint(poppler::page_renderer::antialiasing); - // pageRenderer.set_render_hint(poppler::page_renderer::text_hinting); - - const poppler::rectf pageRect {page->page_rect()}; - const bool rotate {page->orientation() == poppler::page::portrait || - page->orientation() == poppler::page::upside_down}; - const double pageHeight {pageRect.height()}; - const double sizeFactor {static_cast(rotate ? height : width) / pageHeight}; - - poppler::image image { - pageRenderer.render_page(page, 72.0 * sizeFactor, 72.0 * sizeFactor, 0, 0, width, height)}; - - if (!image.is_valid()) { - std::cerr << "Rendered image is invalid" << std::endl; - exit(-1); - } - - // Necessary as the image data stream may contain null characters. - std::string imageARGB32; - imageARGB32.insert(0, std::move(image.data()), width * height * 4); - - std::cout << imageARGB32; - return 0; + return ConvertPDF::processFile(path, mode, pageNum, width, height); }