From 7e93fcbde4c25bb84e64eaec7ddf939435a9db6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andy=20Teijelo=20P=C3=A9rez?= Date: Mon, 18 Jul 2016 13:26:04 -0400 Subject: [PATCH] Initial commit --- .gitignore | 73 +++++++++++++++++++++++++ CMakeLists.txt | 6 ++ main.cpp | 145 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 224 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 main.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..0e509fc --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,6 @@ +project(pdfrender) +cmake_minimum_required(VERSION 2.8) +aux_source_directory(. SRC_LIST) +add_executable(${PROJECT_NAME} ${SRC_LIST}) +target_compile_options(${PROJECT_NAME} PRIVATE -g -std=c++11) +target_link_libraries(${PROJECT_NAME} poppler-cpp pthread boost_program_options) diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..35ccb32 --- /dev/null +++ b/main.cpp @@ -0,0 +1,145 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace std; +using namespace poppler; + +namespace po = boost::program_options; + +int dpi = 75; +string inputpdf; +string output; +int threads = thread::hardware_concurrency(); + +void render_page(document *doc, int pageno, const string& fmt) { + page *p = doc->create_page(pageno); + page_renderer r; + image img = r.render_page(p); + vector buf(1024); + int size = snprintf(&buf[0], buf.size(), fmt.c_str(), pageno); + if (size >= buf.size()) { + buf.reserve(size + 1); + snprintf(&buf[0], buf.size(), fmt.c_str(), pageno); + } + img.save(&buf[0], "JPEG", dpi); +} + +void render_pages(document *doc, int first, int last, int step, const string& fmt) { + for (int p=first; p<=last; p+=step) { + render_page(doc, p, fmt); + } +} + +bool validate_output(const string& output) +{ + if (output.empty()) { + cout << "Output format must be specified" << endl; + return false; + } + + regex fmt_regex(R"(%(?:0(\d+))?d)"); + + auto beg = sregex_iterator(output.begin(), output.end(), fmt_regex); + auto end = sregex_iterator(); + + int matches = distance(beg, end); + if (matches < 1) { + cout << "Output format must contain %d or %0d" << endl; + return false; + } + + if (matches > 1) { + cout << "Output format must contain only one %d or %0d" << endl; + return false; + } + + auto m = *beg; + if (!(m[1].str().empty())) { + int padding = 0; + try { + padding = stoi(m[1].str()); + } catch (...) { + cout << "error: invalid interger value for padding: " << m[1].str() << endl; + return false; + } + if (padding > 10) { + cout << "error: padding is too large: " << m[1].str() << endl; + return false; + } + } + return true; +} + +void parse_args(int argc, char *argv[]) +{ + po::options_description opts(string("Usage: ") + argv[0] + " [options] input.pdf\nOptions:"); + opts.add_options() + ("help,h", "prints this message") + ("output,o", po::value(&output), + "output format; must contain exactly one instance of %d or " + "%0d where is an integer for the page number; " + "padding, if used, will always be with zeros") + ("dpi", po::value(&dpi)->default_value(75), + "render at the specified dpi") + ("threads,t", po::value(&threads), + "use this many threads to render; by default, uses " + "the result of std::thread::hardware_concurrency()") + ; + + po::options_description hidden("Hidden"); + hidden.add_options() + ("input", po::value(&inputpdf)) + ; + + po::options_description allopts(""); + allopts.add(opts).add(hidden); + + po::positional_options_description pos; + pos.add("input", 1); + + po::variables_map vm; + po::store( + po::command_line_parser(argc, argv) + .options(allopts) + .positional(pos) + .run(), + vm + ); + po::notify(vm); + + if (inputpdf.empty() || vm.count("help")) { + cout << opts; + exit(1); + } + + if (!validate_output(output)) { + cout << "See " << argv[0] << " -h for more info" << endl; + exit(1); + } +} + +int main(int argc, char *argv[]) +{ + parse_args(argc, argv); + + document *doc = document::load_from_file(inputpdf); + + vector> futures; + int pagecount = doc->pages(); + for (int i = 1; i <= threads; i++) { + futures.push_back( + async(launch::async, + render_pages, doc, i, pagecount, threads, output + ) + ); + } +}