mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2024-11-21 21:55:38 +00:00
Split the main PDF converter code into its own class
This commit is contained in:
parent
8403b40d4c
commit
ddaf2f01b7
|
@ -21,13 +21,27 @@ if(WIN32)
|
||||||
elseif(APPLE)
|
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_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)
|
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()
|
else()
|
||||||
find_package(Poppler REQUIRED COMPONENTS cpp)
|
find_package(Poppler REQUIRED COMPONENTS cpp)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
include_directories(${POPPLER_CPP_INCLUDE_DIR})
|
include_directories(${POPPLER_CPP_INCLUDE_DIR})
|
||||||
add_executable(es-pdf-convert WIN32 ${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp)
|
if (ANDROID)
|
||||||
target_link_libraries(es-pdf-convert ${POPPLER_CPP_LIBRARY})
|
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)
|
if(WIN32)
|
||||||
set_target_properties(es-pdf-convert PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/es-pdf-converter" INSTALL_RPATH_USE_LINK_PATH TRUE)
|
set_target_properties(es-pdf-convert PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/es-pdf-converter" INSTALL_RPATH_USE_LINK_PATH TRUE)
|
||||||
|
|
194
es-pdf-converter/src/ConvertPDF.cpp
Normal file
194
es-pdf-converter/src/ConvertPDF.cpp
Normal file
|
@ -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 <cmath>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#if defined(__ANDROID__)
|
||||||
|
#include <android/log.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_WIN64)
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <io.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#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<long>(file.tellg())};
|
||||||
|
file.seekg(0, std::ios::beg);
|
||||||
|
std::vector<char> 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<std::string> 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<double>(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;
|
||||||
|
}
|
35
es-pdf-converter/src/ConvertPDF.h
Normal file
35
es-pdf-converter/src/ConvertPDF.h
Normal file
|
@ -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 <string>
|
||||||
|
|
||||||
|
#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
|
|
@ -1,6 +1,6 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0-only
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
//
|
//
|
||||||
// EmulationStation Desktop Edition (ES-DE) PDF converter
|
// ES-DE PDF converter
|
||||||
// main.cpp
|
// main.cpp
|
||||||
//
|
//
|
||||||
// Converts PDF document pages to raw ARGB32 pixel data for maximum performance.
|
// 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.
|
// All ES-DE C++ source code is formatted using clang-format.
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "poppler-document.h"
|
#include "ConvertPDF.h"
|
||||||
#include "poppler-image.h"
|
|
||||||
#include "poppler-page-renderer.h"
|
|
||||||
#include "poppler-page.h"
|
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
#include <fstream>
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
|
@ -101,7 +96,7 @@ int main(int argc, char* argv[])
|
||||||
pageNum = atoi(argv[3]);
|
pageNum = atoi(argv[3]);
|
||||||
width = atoi(argv[4]);
|
width = atoi(argv[4]);
|
||||||
height = atoi(argv[5]);
|
height = atoi(argv[5]);
|
||||||
#endif
|
#endif // _WIN64
|
||||||
if (width < 1 || width > 7680) {
|
if (width < 1 || width > 7680) {
|
||||||
std::cerr << "Invalid horizontal resolution defined: " << argv[3] << std::endl;
|
std::cerr << "Invalid horizontal resolution defined: " << argv[3] << std::endl;
|
||||||
exit(-1);
|
exit(-1);
|
||||||
|
@ -116,107 +111,5 @@ int main(int argc, char* argv[])
|
||||||
// << width << "x" << height << " pixels" << std::endl;
|
// << width << "x" << height << " pixels" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ifstream file;
|
return ConvertPDF::processFile(path, mode, pageNum, width, height);
|
||||||
|
|
||||||
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<long>(file.tellg())};
|
|
||||||
file.seekg(0, std::ios::beg);
|
|
||||||
std::vector<char> 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<std::string> 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<double>(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;
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue