2023-06-21 21:02:19 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
|
|
|
//
|
|
|
|
// EmulationStation Desktop Edition (ES-DE) PDF converter
|
|
|
|
// main.cpp
|
|
|
|
//
|
2023-06-22 19:15:35 +00:00
|
|
|
// Converts PDF document pages to raw ARGB32 pixel data for maximum performance.
|
2023-06-21 21:02:19 +00:00
|
|
|
// This needs to be separated into its own binary to get around the restrictive GPL
|
|
|
|
// license used by the Poppler PDF rendering library.
|
|
|
|
//
|
|
|
|
// The column limit is 100 characters.
|
|
|
|
// 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 <cmath>
|
2023-06-22 19:15:35 +00:00
|
|
|
#include <fstream>
|
2023-06-21 21:02:19 +00:00
|
|
|
#include <iostream>
|
|
|
|
|
2023-06-22 19:15:35 +00:00
|
|
|
#if defined(_WIN64)
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <io.h>
|
|
|
|
#include <windows.h>
|
|
|
|
|
2023-06-23 13:23:14 +00:00
|
|
|
#if defined(_MSC_VER) // MSVC compiler.
|
2023-06-22 19:15:35 +00:00
|
|
|
int wmain(int argc, wchar_t* argv[])
|
|
|
|
{
|
2023-06-23 13:23:14 +00:00
|
|
|
#else
|
|
|
|
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
|
|
|
|
{
|
|
|
|
wchar_t** argv {__wargv};
|
2023-06-29 18:41:18 +00:00
|
|
|
int argc {__argc};
|
2023-06-23 13:23:14 +00:00
|
|
|
#endif
|
2023-06-22 19:15:35 +00:00
|
|
|
HANDLE stdoutHandle {GetStdHandle(STD_OUTPUT_HANDLE)};
|
|
|
|
|
|
|
|
if (stdoutHandle == INVALID_HANDLE_VALUE) {
|
|
|
|
std::cerr << "Error: Invalid stdout handle" << std::endl;
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is required as Windows is braindead and will otherwise add carriage return characters
|
|
|
|
// to the stream when it encounters newline characters, which breaks binary output.
|
|
|
|
_setmode(_fileno(stdout), O_BINARY);
|
|
|
|
|
|
|
|
bool validArguments {true};
|
|
|
|
std::wstring mode;
|
|
|
|
|
|
|
|
if (argc < 3)
|
|
|
|
validArguments = false;
|
|
|
|
else
|
|
|
|
mode = argv[1];
|
|
|
|
|
|
|
|
if ((mode == L"-fileinfo" && argc != 3) || (mode == L"-convert" && argc != 6))
|
|
|
|
validArguments = false;
|
|
|
|
|
|
|
|
if (!validArguments) {
|
|
|
|
std::cout << "This binary is only intended to be executed by EmulationStation.exe (ES-DE)"
|
|
|
|
<< std::endl;
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
const std::wstring path {argv[2]};
|
|
|
|
|
|
|
|
int pageNum {0};
|
|
|
|
int width {0};
|
|
|
|
int height {0};
|
|
|
|
|
|
|
|
if (mode == L"-convert") {
|
|
|
|
pageNum = _wtoi(argv[3]);
|
|
|
|
width = _wtoi(argv[4]);
|
|
|
|
height = _wtoi(argv[5]);
|
|
|
|
#else
|
2023-06-21 21:02:19 +00:00
|
|
|
int main(int argc, char* argv[])
|
|
|
|
{
|
2023-06-22 09:34:03 +00:00
|
|
|
bool validArguments {true};
|
|
|
|
std::string mode;
|
|
|
|
|
|
|
|
if (argc < 3)
|
|
|
|
validArguments = false;
|
|
|
|
else
|
|
|
|
mode = argv[1];
|
|
|
|
|
|
|
|
if ((mode == "-fileinfo" && argc != 3) || (mode == "-convert" && argc != 6))
|
|
|
|
validArguments = false;
|
|
|
|
|
|
|
|
if (!validArguments) {
|
2023-06-25 19:48:15 +00:00
|
|
|
#if defined(__APPLE__)
|
2023-06-24 21:58:33 +00:00
|
|
|
std::cout << "This binary is only intended to be executed by EmulationStation (ES-DE)"
|
|
|
|
#else
|
2023-06-22 09:34:03 +00:00
|
|
|
std::cout << "This binary is only intended to be executed by emulationstation (ES-DE)"
|
2023-06-24 21:58:33 +00:00
|
|
|
#endif
|
2023-06-21 21:02:19 +00:00
|
|
|
<< std::endl;
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
|
2023-06-22 09:34:03 +00:00
|
|
|
const std::string path {argv[2]};
|
|
|
|
int pageNum {0};
|
|
|
|
int width {0};
|
|
|
|
int height {0};
|
2023-06-21 21:02:19 +00:00
|
|
|
|
2023-06-22 09:34:03 +00:00
|
|
|
if (mode == "-convert") {
|
|
|
|
pageNum = atoi(argv[3]);
|
|
|
|
width = atoi(argv[4]);
|
|
|
|
height = atoi(argv[5]);
|
2023-06-22 19:15:35 +00:00
|
|
|
#endif
|
2023-06-22 09:34:03 +00:00
|
|
|
if (width < 1 || width > 7680) {
|
|
|
|
std::cerr << "Invalid horizontal resolution defined: " << argv[3] << std::endl;
|
|
|
|
exit(-1);
|
|
|
|
}
|
2023-06-21 21:02:19 +00:00
|
|
|
|
2023-06-22 09:34:03 +00:00
|
|
|
if (height < 1 || height > 7680) {
|
|
|
|
std::cerr << "Invalid vertical resolution defined: " << argv[4] << std::endl;
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// std::cerr << "Converting file \"" << path << "\", page " << pageNum << " to resolution "
|
|
|
|
// << width << "x" << height << " pixels" << std::endl;
|
|
|
|
}
|
2023-06-21 21:02:19 +00:00
|
|
|
|
2023-06-22 19:15:35 +00:00
|
|
|
std::ifstream file;
|
|
|
|
|
2023-06-23 13:23:14 +00:00
|
|
|
file.open(path.c_str(), std::ifstream::binary);
|
2023-06-22 19:15:35 +00:00
|
|
|
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)};
|
2023-06-21 21:02:19 +00:00
|
|
|
|
2023-06-22 09:34:03 +00:00
|
|
|
if (document == nullptr) {
|
|
|
|
std::cerr << "Error: Couldn't open document, invalid PDF file?" << std::endl;
|
2023-06-21 21:02:19 +00:00
|
|
|
exit(-1);
|
2023-06-22 09:34:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const int pageCount {document->pages()};
|
2023-06-22 19:15:35 +00:00
|
|
|
#if defined(_WIN64)
|
|
|
|
if (mode == L"-fileinfo") {
|
|
|
|
#else
|
2023-06-22 09:34:03 +00:00
|
|
|
if (mode == "-fileinfo") {
|
2023-06-22 19:15:35 +00:00
|
|
|
#endif
|
2023-06-22 09:34:03 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2023-06-28 18:32:49 +00:00
|
|
|
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";
|
|
|
|
|
2023-06-22 09:34:03 +00:00
|
|
|
const poppler::rectf pageRect {page->page_rect()};
|
|
|
|
pageRow.append(std::to_string(i + 1))
|
|
|
|
.append(";")
|
2023-06-28 18:32:49 +00:00
|
|
|
.append(orientation)
|
2023-06-22 09:34:03 +00:00
|
|
|
.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);
|
|
|
|
}
|
2023-06-21 21:02:19 +00:00
|
|
|
|
2023-06-22 09:34:03 +00:00
|
|
|
if (pageNum < 1 || pageNum > pageCount) {
|
2023-06-21 21:02:19 +00:00
|
|
|
std::cerr << "Error: Requested page " << pageNum << " does not exist in document"
|
|
|
|
<< std::endl;
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
const poppler::page* page {document->create_page(pageNum - 1)};
|
2023-06-22 09:34:03 +00:00
|
|
|
|
|
|
|
if (page == nullptr) {
|
|
|
|
std::cerr << "Error: Couldn't read page " << pageNum << std::endl;
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
|
2023-06-21 21:02:19 +00:00
|
|
|
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()};
|
2023-06-28 18:32:49 +00:00
|
|
|
const bool rotate {page->orientation() == poppler::page::portrait ||
|
|
|
|
page->orientation() == poppler::page::upside_down};
|
2023-06-21 21:02:19 +00:00
|
|
|
const double pageHeight {pageRect.height()};
|
2023-06-28 18:32:49 +00:00
|
|
|
const double sizeFactor {static_cast<double>(rotate ? height : width) / pageHeight};
|
2023-06-21 21:02:19 +00:00
|
|
|
|
2023-06-28 21:06:05 +00:00
|
|
|
poppler::image image {
|
|
|
|
pageRenderer.render_page(page, 72.0 * sizeFactor, 72.0 * sizeFactor, 0, 0, width, height)};
|
2023-06-21 21:02:19 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|