diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/CMakeLists.txt | 141 | ||||
-rw-r--r-- | test/cairo-thread-test.cc | 561 | ||||
-rw-r--r-- | test/goostring-format-checker/README | 2 | ||||
-rw-r--r-- | test/goostring-format-checker/goostring-format-checker.cc | 3 | ||||
-rw-r--r-- | test/gtk-test.cc | 80 | ||||
-rw-r--r-- | test/image-embedding.cc | 113 | ||||
-rw-r--r-- | test/pdf-fullrewrite.cc | 30 | ||||
-rw-r--r-- | test/pdf-inspector.cc | 20 | ||||
-rw-r--r-- | test/perf-test.cc | 563 |
9 files changed, 995 insertions, 518 deletions
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 09b2e67a..afa13529 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,28 +1,19 @@ -if (ENABLE_SPLASH) - - if (HAVE_NANOSLEEP OR LIB_RT_HAS_NANOSLEEP) - set (perf_test_SRCS - perf-test.cc - perf-test-preview-dummy.cc - ) - add_executable(perf-test ${perf_test_SRCS}) - target_link_libraries(perf-test poppler) - if (LIB_RT_HAS_NANOSLEEP) - target_link_libraries(perf-test rt) - endif () +if (HAVE_NANOSLEEP OR LIB_RT_HAS_NANOSLEEP) + set (perf_test_SRCS + perf-test.cc + perf-test-preview-dummy.cc + ) + add_executable(perf-test ${perf_test_SRCS}) + target_link_libraries(perf-test poppler) + if (LIB_RT_HAS_NANOSLEEP) + target_link_libraries(perf-test rt) endif () - endif () if (GTK_FOUND) include_directories( - SYSTEM - ${GTK3_INCLUDE_DIRS} - ) - - include_directories( ${CMAKE_SOURCE_DIR}/glib ${CMAKE_BINARY_DIR}/glib ) @@ -31,7 +22,8 @@ if (GTK_FOUND) gtk-test.cc ) poppler_add_test(gtk-test BUILD_GTK_TESTS ${gtk_splash_test_SRCS}) - target_link_libraries(gtk-test ${CAIRO_LIBRARIES} poppler-glib ${GTK3_LIBRARIES}) + target_link_libraries(gtk-test ${CAIRO_LIBRARIES} poppler-glib PkgConfig::GTK3) + target_include_directories(gtk-test SYSTEM PRIVATE ${CAIRO_INCLUDE_DIRS}) if (HAVE_CAIRO) @@ -42,16 +34,35 @@ if (GTK_FOUND) ${CMAKE_SOURCE_DIR}/poppler/CairoRescaleBox.cc ) poppler_add_test(pdf-inspector BUILD_GTK_TESTS ${pdf_inspector_SRCS}) - target_link_libraries(pdf-inspector ${CAIRO_LIBRARIES} ${FREETYPE_LIBRARIES} ${common_libs} ${GTK3_LIBRARIES} poppler) + target_link_libraries(pdf-inspector ${CAIRO_LIBRARIES} Freetype::Freetype ${common_libs} PkgConfig::GTK3 poppler) + target_include_directories(pdf-inspector SYSTEM PRIVATE ${CAIRO_INCLUDE_DIRS}) target_compile_definitions(pdf-inspector PRIVATE -DSRC_DIR="${CMAKE_CURRENT_SOURCE_DIR}") - if(CMAKE_USE_PTHREADS_INIT) - target_link_libraries(pdf-inspector Threads::Threads) - endif() - endif () endif () +if (HAVE_CAIRO) + include(CheckCXXSymbolExists) + set (CMAKE_REQUIRED_INCLUDES ${CAIRO_INCLUDE_DIRS}) + check_cxx_symbol_exists(CAIRO_HAS_PNG_FUNCTIONS "cairo.h" HAVE_CAIRO_PNG) + check_cxx_symbol_exists(CAIRO_HAS_PDF_SURFACE "cairo.h" HAVE_CAIRO_PDF) + check_cxx_symbol_exists(CAIRO_HAS_PS_SURFACE "cairo.h" HAVE_CAIRO_PS) + check_cxx_symbol_exists(CAIRO_HAS_SVG_SURFACE "cairo.h" HAVE_CAIRO_SVG) + + if (HAVE_CAIRO_PNG AND HAVE_CAIRO_PDF AND HAVE_CAIRO_PS AND HAVE_CAIRO_SVG) + find_package(Threads) + set(cairo_thread_test_SRCS + cairo-thread-test.cc + ${CMAKE_SOURCE_DIR}/poppler/CairoFontEngine.cc + ${CMAKE_SOURCE_DIR}/poppler/CairoOutputDev.cc + ${CMAKE_SOURCE_DIR}/poppler/CairoRescaleBox.cc + ) + add_executable(cairo-thread-test ${cairo_thread_test_SRCS}) + target_link_libraries(cairo-thread-test ${CAIRO_LIBRARIES} Freetype::Freetype Threads::Threads poppler) + target_include_directories(cairo-thread-test SYSTEM PRIVATE ${CAIRO_INCLUDE_DIRS}) + endif () +endif () + set (pdf_fullrewrite_SRCS pdf-fullrewrite.cc ../utils/parseargs.cc @@ -59,4 +70,86 @@ set (pdf_fullrewrite_SRCS add_executable(pdf-fullrewrite ${pdf_fullrewrite_SRCS}) target_link_libraries(pdf-fullrewrite poppler) +# Tests for the image embedding API. +if(ENABLE_LIBPNG OR ENABLE_LIBJPEG) + set(image_embedding_SRCS + image-embedding.cc + ../utils/parseargs.cc + ) + add_executable(image-embedding ${image_embedding_SRCS}) + target_link_libraries(image-embedding poppler) + + set(INPUT_PDF ${TESTDATADIR}/unittestcases/xr01.pdf) + set(IMG_DIR ${TESTDATADIR}/unittestcases/images) + set(IMAGE_EMBEDDING_PATH ${EXECUTABLE_OUTPUT_PATH}/image-embedding) + + if (ENABLE_LIBPNG) + add_test( + NAME embed-png-g1 + COMMAND ${IMAGE_EMBEDDING_PATH} ${INPUT_PDF} ${IMG_DIR}/png-g1.png -depth 8 -colorspace DeviceGray + ) + add_test( + NAME embed-png-g2 + COMMAND ${IMAGE_EMBEDDING_PATH} ${INPUT_PDF} ${IMG_DIR}/png-g2.png -depth 8 -colorspace DeviceGray + ) + add_test( + NAME embed-png-g4 + COMMAND ${IMAGE_EMBEDDING_PATH} ${INPUT_PDF} ${IMG_DIR}/png-g4.png -depth 8 -colorspace DeviceGray + ) + add_test( + NAME embed-png-g8 + COMMAND ${IMAGE_EMBEDDING_PATH} ${INPUT_PDF} ${IMG_DIR}/png-g8.png -depth 8 -colorspace DeviceGray + ) + add_test( + NAME embed-png-g16 + COMMAND ${IMAGE_EMBEDDING_PATH} ${INPUT_PDF} ${IMG_DIR}/png-g16.png -depth 16 -colorspace DeviceGray + ) + add_test( + NAME embed-png-ga8 + COMMAND ${IMAGE_EMBEDDING_PATH} ${INPUT_PDF} ${IMG_DIR}/png-ga8.png -depth 8 -colorspace DeviceGray -smask + ) + add_test( + NAME embed-png-ga16 + COMMAND ${IMAGE_EMBEDDING_PATH} ${INPUT_PDF} ${IMG_DIR}/png-ga16.png -depth 16 -colorspace DeviceGray -smask + ) + add_test( + NAME embed-png-palette + COMMAND ${IMAGE_EMBEDDING_PATH} ${INPUT_PDF} ${IMG_DIR}/png-palette.png -depth 8 -colorspace DeviceRGB + ) + add_test( + NAME embed-png-rgb8 + COMMAND ${IMAGE_EMBEDDING_PATH} ${INPUT_PDF} ${IMG_DIR}/png-rgb8.png -depth 8 -colorspace DeviceRGB + ) + add_test( + NAME embed-png-rgb16 + COMMAND ${IMAGE_EMBEDDING_PATH} ${INPUT_PDF} ${IMG_DIR}/png-rgb16.png -depth 16 -colorspace DeviceRGB + ) + add_test( + NAME embed-png-rgba8 + COMMAND ${IMAGE_EMBEDDING_PATH} ${INPUT_PDF} ${IMG_DIR}/png-rgba8.png -depth 8 -colorspace DeviceRGB -smask + ) + add_test( + NAME embed-png-rgba16 + COMMAND ${IMAGE_EMBEDDING_PATH} ${INPUT_PDF} ${IMG_DIR}/png-rgba16.png -depth 16 -colorspace DeviceRGB -smask + ) + add_test( + NAME embed-malformed-png + COMMAND ${IMAGE_EMBEDDING_PATH} ${INPUT_PDF} ${IMG_DIR}/malformed.png -fail + ) + endif() + + if(ENABLE_LIBJPEG) + add_test( + NAME embed-jpeg + COMMAND ${IMAGE_EMBEDDING_PATH} ${INPUT_PDF} ${IMG_DIR}/jpeg.jpg -depth 8 -colorspace DeviceRGB -filter DCTDecode + ) + add_test( + NAME embed-malformed-jpeg + COMMAND ${IMAGE_EMBEDDING_PATH} ${INPUT_PDF} ${IMG_DIR}/malformed.jpg -fail + ) + endif() + unset(IMAGE_EMBEDDING_PATH) + unset(IMG_DIR) + unset(INPUT_PDF) +endif() diff --git a/test/cairo-thread-test.cc b/test/cairo-thread-test.cc new file mode 100644 index 00000000..922ed813 --- /dev/null +++ b/test/cairo-thread-test.cc @@ -0,0 +1,561 @@ +//======================================================================== +// +// cairo-thread-test.cc +// +// This file is licensed under the GPLv2 or later +// +// Copyright (C) 2022 Adrian Johnson <ajohnson@redneon.com> +// +//======================================================================== + +#include "config.h" +#include <poppler-config.h> +#include <condition_variable> +#include <cmath> +#include <cstdio> +#include <mutex> +#include <queue> +#include <thread> +#include <vector> + +#include "goo/GooString.h" +#include "CairoOutputDev.h" +#include "CairoFontEngine.h" +#include "GlobalParams.h" +#include "PDFDoc.h" +#include "PDFDocFactory.h" +#include "../utils/numberofcharacters.h" + +#include <cairo.h> +#include <cairo-pdf.h> +#include <cairo-ps.h> +#include <cairo-svg.h> + +static const int renderResolution = 150; + +enum OutputType +{ + png, + pdf, + ps, + svg +}; + +// Lazy creation of PDFDoc +class Document +{ +public: + explicit Document(const std::string &filenameA) : filename(filenameA) { std::call_once(ftLibOnceFlag, FT_Init_FreeType, &ftLib); } + + std::shared_ptr<PDFDoc> getDoc() + { + std::call_once(docOnceFlag, &Document::openDocument, this); + return doc; + } + + const std::string &getFilename() { return filename; } + CairoFontEngine *getFontEngine() { return fontEngine.get(); } + +private: + void openDocument() + { + doc = PDFDocFactory().createPDFDoc(GooString(filename)); + if (!doc->isOk()) { + fprintf(stderr, "Error opening PDF file %s\n", filename.c_str()); + exit(1); + } + fontEngine = std::make_unique<CairoFontEngine>(ftLib); + } + + std::string filename; + std::shared_ptr<PDFDoc> doc; + std::once_flag docOnceFlag; + std::unique_ptr<CairoFontEngine> fontEngine; + + static FT_Library ftLib; + static std::once_flag ftLibOnceFlag; +}; + +FT_Library Document::ftLib; +std::once_flag Document::ftLibOnceFlag; + +struct Job +{ + Job(OutputType typeA, const std::shared_ptr<Document> &documentA, int pageNumA, const std::string &outputFileA) : type(typeA), document(documentA), pageNum(pageNumA), outputFile(outputFileA) { } + OutputType type; + std::shared_ptr<Document> document; + int pageNum; + std::string outputFile; +}; + +class JobQueue +{ +public: + JobQueue() : shutdownFlag(false) { } + + void pushJob(std::unique_ptr<Job> &job) + { + std::scoped_lock lock { mutex }; + queue.push_back(std::move(job)); + condition.notify_one(); + } + + // Wait for job. If shutdownFlag true, will return null if queue empty. + std::unique_ptr<Job> popJob() + { + std::unique_lock<std::mutex> lock(mutex); + condition.wait(lock, [this] { return !queue.empty() || shutdownFlag; }); + std::unique_ptr<Job> job; + if (!queue.empty()) { + job = std::move(queue.front()); + queue.pop_front(); + } else { + condition.notify_all(); // notify waitUntilEmpty() + } + return job; + } + + // When called, popJob() will not block on an empty queue instead returning nullptr + void shutdown() + { + shutdownFlag = true; + condition.notify_all(); + } + + // wait until queue is empty + void waitUntilEmpty() + { + std::unique_lock<std::mutex> lock(mutex); + condition.wait(lock, [this] { return queue.empty(); }); + } + +private: + std::deque<std::unique_ptr<Job>> queue; + std::mutex mutex; + std::condition_variable condition; + bool shutdownFlag; +}; + +static cairo_status_t writeStream(void *closure, const unsigned char *data, unsigned int length) +{ + FILE *file = (FILE *)closure; + + if (fwrite(data, length, 1, file) == 1) { + return CAIRO_STATUS_SUCCESS; + } else { + return CAIRO_STATUS_WRITE_ERROR; + } +} + +// PDF/PS/SVG output +static void renderDocument(const Job &job) +{ + FILE *f = openFile(job.outputFile.c_str(), "wb"); + if (!f) { + fprintf(stderr, "Error opening output file %s\n", job.outputFile.c_str()); + exit(1); + } + + cairo_surface_t *surface = nullptr; + + switch (job.type) { + case OutputType::pdf: + surface = cairo_pdf_surface_create_for_stream(writeStream, f, 1, 1); + break; + case OutputType::ps: + surface = cairo_ps_surface_create_for_stream(writeStream, f, 1, 1); + break; + case OutputType::svg: + surface = cairo_svg_surface_create_for_stream(writeStream, f, 1, 1); + break; + case OutputType::png: + break; + } + + cairo_surface_set_fallback_resolution(surface, renderResolution, renderResolution); + + std::unique_ptr<CairoOutputDev> cairoOut = std::make_unique<CairoOutputDev>(); + + cairoOut->startDoc(job.document->getDoc().get(), job.document->getFontEngine()); + + cairo_status_t status; + for (int pageNum = 1; pageNum <= job.document->getDoc()->getNumPages(); pageNum++) { + double width = job.document->getDoc()->getPageMediaWidth(pageNum); + double height = job.document->getDoc()->getPageMediaHeight(pageNum); + + if (job.type == OutputType::pdf) { + cairo_pdf_surface_set_size(surface, width, height); + } else if (job.type == OutputType::ps) { + cairo_ps_surface_set_size(surface, width, height); + } + + cairo_t *cr = cairo_create(surface); + + cairoOut->setCairo(cr); + cairoOut->setPrinting(true); + + cairo_save(cr); + job.document->getDoc()->displayPageSlice(cairoOut.get(), pageNum, 72.0, 72.0, 0, /* rotate */ + true, /* useMediaBox */ + false, /* Crop */ + true /*printing*/, -1, -1, -1, -1); + cairo_restore(cr); + cairoOut->setCairo(nullptr); + + status = cairo_status(cr); + if (status) { + fprintf(stderr, "cairo error: %s\n", cairo_status_to_string(status)); + } + cairo_destroy(cr); + } + + cairo_surface_finish(surface); + status = cairo_surface_status(surface); + if (status) { + fprintf(stderr, "cairo error: %s\n", cairo_status_to_string(status)); + } + cairo_surface_destroy(surface); + fclose(f); +} + +// PNG page output +static void renderPage(const Job &job) +{ + double width = job.document->getDoc()->getPageMediaWidth(job.pageNum); + double height = job.document->getDoc()->getPageMediaHeight(job.pageNum); + + // convert from points to pixels + width *= renderResolution / 72.0; + height *= renderResolution / 72.0; + + cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, static_cast<int>(ceil(width)), static_cast<int>(ceil(height))); + + std::unique_ptr<CairoOutputDev> cairoOut = std::make_unique<CairoOutputDev>(); + + cairoOut->startDoc(job.document->getDoc().get(), job.document->getFontEngine()); + cairo_t *cr = cairo_create(surface); + cairo_status_t status; + + cairoOut->setCairo(cr); + cairoOut->setPrinting(false); + + cairo_save(cr); + cairo_scale(cr, renderResolution / 72.0, renderResolution / 72.0); + + job.document->getDoc()->displayPageSlice(cairoOut.get(), job.pageNum, 72.0, 72.0, 0, /* rotate */ + true, /* useMediaBox */ + false, /* Crop */ + false /*printing */, -1, -1, -1, -1); + cairo_restore(cr); + + cairoOut->setCairo(nullptr); + + // Blend onto white page + cairo_save(cr); + cairo_set_operator(cr, CAIRO_OPERATOR_DEST_OVER); + cairo_set_source_rgb(cr, 1, 1, 1); + cairo_paint(cr); + cairo_restore(cr); + + status = cairo_status(cr); + if (status) { + fprintf(stderr, "cairo error: %s\n", cairo_status_to_string(status)); + } + cairo_destroy(cr); + + FILE *f = openFile(job.outputFile.c_str(), "wb"); + if (!f) { + fprintf(stderr, "Error opening output file %s\n", job.outputFile.c_str()); + exit(1); + } + cairo_surface_write_to_png_stream(surface, writeStream, f); + fclose(f); + + cairo_surface_finish(surface); + status = cairo_surface_status(surface); + if (status) { + fprintf(stderr, "cairo error: %s\n", cairo_status_to_string(status)); + } + cairo_surface_destroy(surface); +} + +static void runThread(const std::shared_ptr<JobQueue> &jobQueue) +{ + while (true) { + std::unique_ptr<Job> job = jobQueue->popJob(); + if (!job) { + break; + } + switch (job->type) { + case OutputType::png: + renderPage(*job); + break; + case OutputType::pdf: + case OutputType::ps: + case OutputType::svg: + renderDocument(*job); + break; + } + } +} + +static void printUsage() +{ + int default_threads = std::max(1, (int)std::thread::hardware_concurrency()); + printf("cairo-thread-test [-j jobs] [-p priority] [<output option> <files>...]...\n"); + printf(" -j num number of concurrent threads (default %d)\n", default_threads); + printf(" -p <priority> priority is one of:\n"); + printf(" page one page at a time will be queued from each document in round-robin fashion (default).\n"); + printf(" document all pages in the first document will be queued before processing to the next document.\n"); + printf(" Note: documents with vector output will be handled in one job. They can not be parallelized.\n"); + printf(" <output option> is one of -png, -pdf, -ps, -svg\n"); + printf(" The output option will apply to all documents after the option until a different option is specified\n"); +} + +// Parse -j and -p options. These must appear before any other arguments +static bool getThreadsAndPriority(int &argc, char **&argv, int &numThreads, bool &documentPriority) +{ + numThreads = std::max(1, (int)std::thread::hardware_concurrency()); + documentPriority = false; + + while (argc > 0) { + std::string arg(*argv); + if (arg == "-j") { + argc--; + argv++; + if (argc == 0) { + return false; + } + numThreads = atoi(*argv); + if (numThreads == 0) { + return false; + } + argc--; + argv++; + } else if (arg == "-p") { + argc--; + argv++; + if (argc == 0) { + return false; + } + arg = *argv; + if (arg == "document") { + documentPriority = true; + } else if (arg == "page") { + documentPriority = false; + + } else { + return false; + } + argc--; + argv++; + } else { + // file or output option + break; + } + } + return true; +} + +// eg "-png doc1.pdf -ps doc2.pdf doc3.pdf -png doc4.pdf" +static bool getOutputTypeAndDocument(int &argc, char **&argv, OutputType &outputType, std::string &filename) +{ + static OutputType type; + static bool typeInitialized = false; + + while (argc > 0) { + std::string arg(*argv); + if (arg == "-png") { + argc--; + argv++; + type = OutputType::png; + typeInitialized = true; + } else if (arg == "-pdf") { + argc--; + argv++; + type = OutputType::pdf; + typeInitialized = true; + } else if (arg == "-ps") { + argc--; + argv++; + type = OutputType::ps; + typeInitialized = true; + } else if (arg == "-svg") { + argc--; + argv++; + type = OutputType::svg; + typeInitialized = true; + } else { + // filename + if (!typeInitialized) { + return false; + } + outputType = type; + filename = *argv; + argc--; + argv++; + return true; + } + } + return false; +} + +// "../a/b/foo.pdf" => "foo" +static std::string getBaseName(const std::string &filename) +{ + // strip everything up to last '/' + size_t slash_pos = filename.find_last_of('/'); + std::string basename; + if (slash_pos != std::string::npos) { + basename = filename.substr(slash_pos + 1, std::string::npos); + } else { + basename = filename; + } + + // remove .pdf extension + size_t dot_pos = basename.find_last_of('.'); + if (dot_pos != std::string::npos) { + if (basename.compare(dot_pos, std::string::npos, ".pdf") == 0) { + basename.erase(dot_pos); + } + } + return basename; +} + +// Represents an input file on the command line +struct InputFile +{ + InputFile(const std::string &filename, OutputType typeA) : type(typeA) + { + document = std::make_shared<Document>(filename); + basename = getBaseName(filename); + currentPage = 0; + numPages = 0; // filled in later + numDigits = 0; // filled in later + } + std::shared_ptr<Document> document; + OutputType type; + + // Used when creating jobs for this InputFile + int currentPage; + std::string basename; + int numPages; + int numDigits; +}; + +// eg "basename.out-123.png" or "basename.out.pdf" +static std::string getOutputName(const InputFile &input) +{ + std::string output; + char buf[30]; + switch (input.type) { + case OutputType::png: + std::snprintf(buf, sizeof(buf), ".out-%0*d.png", input.numDigits, input.currentPage); + output = input.basename + buf; + break; + case OutputType::pdf: + output = input.basename + ".out.pdf"; + break; + case OutputType::ps: + output = input.basename + ".out.ps"; + break; + case OutputType::svg: + output = input.basename + ".out.svg"; + break; + } + return output; +} + +int main(int argc, char *argv[]) +{ + if (argc < 3) { + printUsage(); + exit(1); + } + + // skip program name + argc--; + argv++; + + int numThreads; + bool documentPriority; + if (!getThreadsAndPriority(argc, argv, numThreads, documentPriority)) { + printUsage(); + exit(1); + } + + globalParams = std::make_unique<GlobalParams>(); + + std::shared_ptr<JobQueue> jobQueue = std::make_shared<JobQueue>(); + std::vector<std::thread> threads; + threads.reserve(4); + for (int i = 0; i < numThreads; i++) { + threads.emplace_back(runThread, jobQueue); + } + + std::vector<InputFile> inputFiles; + + while (argc > 0) { + std::string filename; + OutputType type; + if (!getOutputTypeAndDocument(argc, argv, type, filename)) { + printUsage(); + exit(1); + } + InputFile input(filename, type); + inputFiles.push_back(input); + } + + if (documentPriority) { + while (true) { + bool jobAdded = false; + for (auto &input : inputFiles) { + if (input.numPages == 0) { + // first time seen + if (input.type == OutputType::png) { + input.numPages = input.document->getDoc()->getNumPages(); + input.numDigits = numberOfCharacters(input.numPages); + } else { + input.numPages = 1; // Use 1 for vector output as there is only one output file + } + } + if (input.currentPage < input.numPages) { + input.currentPage++; + std::string output = getOutputName(input); + std::unique_ptr<Job> job = std::make_unique<Job>(input.type, input.document, input.currentPage, output); + jobQueue->pushJob(job); + jobAdded = true; + } + } + if (!jobAdded) { + break; + } + } + } else { + for (auto &input : inputFiles) { + if (input.type == OutputType::png) { + input.numPages = input.document->getDoc()->getNumPages(); + input.numDigits = numberOfCharacters(input.numPages); + for (int i = 1; i <= input.numPages; i++) { + input.currentPage = i; + std::string output = getOutputName(input); + std::unique_ptr<Job> job = std::make_unique<Job>(input.type, input.document, input.currentPage, output); + jobQueue->pushJob(job); + } + } else { + std::string output = getOutputName(input); + std::unique_ptr<Job> job = std::make_unique<Job>(input.type, input.document, 1, output); + jobQueue->pushJob(job); + } + } + } + + jobQueue->shutdown(); + jobQueue->waitUntilEmpty(); + + for (int i = 0; i < numThreads; i++) { + threads[i].join(); + } + + return 0; +} diff --git a/test/goostring-format-checker/README b/test/goostring-format-checker/README index cc58a5d7..3be801c8 100644 --- a/test/goostring-format-checker/README +++ b/test/goostring-format-checker/README @@ -7,7 +7,7 @@ -Xclang -load -Xclang goostring-format-checker.so -Xclang -plugin -Xclang goostring-format-check Example: -$ clang++ -shared -o goostring-format-checker.so goostring-format-checker.cc -D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS +$ clang++ -fPIC -shared -o goostring-format-checker.so goostring-format-checker.cc -D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS $ export CXX=clang++ $ export CXXFLAGS="-Xclang -load -Xclang $PWD/goostring-format-checker.so -Xclang -add-plugin -Xclang goostring-format-checker" $ mkdir build diff --git a/test/goostring-format-checker/goostring-format-checker.cc b/test/goostring-format-checker/goostring-format-checker.cc index 302772b2..41098561 100644 --- a/test/goostring-format-checker/goostring-format-checker.cc +++ b/test/goostring-format-checker/goostring-format-checker.cc @@ -6,6 +6,7 @@ * Clang++ compiler plugin that checks usage of GooString::format-like functions * * Copyright (C) 2014 Fabio D'Urso <fabiodurso@hotmail.it> + * Copyright (C) 2021 Albert Astals Cid <aacid@kde.org> */ #include <cctype> @@ -341,7 +342,7 @@ private: class GooStringFormatCheckerAction : public PluginASTAction { protected: - ASTConsumer *CreateASTConsumer(CompilerInstance &compInst, llvm::StringRef inFile) { return new GooStringFormatCheckerConsumer(&compInst); } + std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &compInst, llvm::StringRef inFile) { return std::make_unique<GooStringFormatCheckerConsumer>(&compInst); } bool ParseArgs(const CompilerInstance &compInst, const std::vector<std::string> &args) { diff --git a/test/gtk-test.cc b/test/gtk-test.cc index fcefd8b1..1507b0e6 100644 --- a/test/gtk-test.cc +++ b/test/gtk-test.cc @@ -15,15 +15,22 @@ #include <poppler.h> #include <poppler-private.h> #include <gtk/gtk.h> +#include <cerrno> #include <cmath> static int requested_page = 0; static gboolean cairo_output = FALSE; static gboolean splash_output = FALSE; +#ifndef G_OS_WIN32 +static gboolean args_are_fds = FALSE; +#endif static const char **file_arguments = nullptr; static const GOptionEntry options[] = { { "cairo", 'c', 0, G_OPTION_ARG_NONE, &cairo_output, "Cairo Output Device", nullptr }, { "splash", 's', 0, G_OPTION_ARG_NONE, &splash_output, "Splash Output Device", nullptr }, { "page", 'p', 0, G_OPTION_ARG_INT, &requested_page, "Page number", "PAGE" }, +#ifndef G_OS_WIN32 + { "fd", 'f', 0, G_OPTION_ARG_NONE, &args_are_fds, "File descriptors", nullptr }, +#endif { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &file_arguments, nullptr, "PDF-FILES…" }, {} }; @@ -153,11 +160,13 @@ static gboolean drawing_area_draw(GtkWidget *drawing_area, cairo_t *cr, View *vi document.height = view->out->getBitmapHeight(); } - if (!gdk_cairo_get_clip_rectangle(cr, &clip)) + if (!gdk_cairo_get_clip_rectangle(cr, &clip)) { return FALSE; + } - if (!gdk_rectangle_intersect(&document, &clip, &draw)) + if (!gdk_rectangle_intersect(&document, &clip, &draw)) { return FALSE; + } if (cairo_output) { cairo_set_source_surface(cr, view->surface, 0, 0); @@ -183,8 +192,9 @@ static void view_set_page(View *view, int page) w = (int)ceil(width); h = (int)ceil(height); - if (view->surface) + if (view->surface) { cairo_surface_destroy(view->surface); + } view->surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h); cr = cairo_create(view->surface); @@ -216,8 +226,9 @@ static void redraw_callback(void *data) static void view_free(View *view) { - if (G_UNLIKELY(!view)) + if (G_UNLIKELY(!view)) { return; + } g_object_unref(view->doc); delete view->out; @@ -230,8 +241,9 @@ static void destroy_window_callback(GtkWindow *window, View *view) view_list = g_list_remove(view_list, view); view_free(view); - if (!view_list) + if (!view_list) { gtk_main_quit(); + } } static void page_changed_callback(GtkSpinButton *button, View *view) @@ -336,30 +348,52 @@ int main(int argc, char *argv[]) for (int i = 0; file_arguments[i]; i++) { View *view; GFile *file; - PopplerDocument *doc; + PopplerDocument *doc = nullptr; GError *error = nullptr; - - file = g_file_new_for_commandline_arg(file_arguments[i]); - doc = poppler_document_new_from_gfile(file, nullptr, nullptr, &error); - if (!doc) { - gchar *uri; - - uri = g_file_get_uri(file); - g_printerr("Error opening document %s: %s\n", uri, error->message); - g_error_free(error); - g_free(uri); + const char *arg; + + arg = file_arguments[i]; +#ifndef G_OS_WIN32 + if (args_are_fds) { + char *end; + gint64 v; + + errno = 0; + end = nullptr; + v = g_ascii_strtoll(arg, &end, 10); + if (errno || end == arg || v == -1 || v < G_MININT || v > G_MAXINT) { + g_set_error(&error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, "Failed to parse \"%s\" as file descriptor number", arg); + } else { + doc = poppler_document_new_from_fd(int(v), nullptr, &error); + } + } else +#endif /* !G_OS_WIN32 */ + { + file = g_file_new_for_commandline_arg(arg); + doc = poppler_document_new_from_gfile(file, nullptr, nullptr, &error); + if (!doc) { + gchar *uri; + + uri = g_file_get_uri(file); + g_prefix_error(&error, "%s: ", uri); + g_free(uri); + } g_object_unref(file); - - continue; } - g_object_unref(file); - view = view_new(doc); - view_list = g_list_prepend(view_list, view); - view_set_page(view, CLAMP(requested_page, 0, poppler_document_get_n_pages(doc) - 1)); + if (doc) { + view = view_new(doc); + view_list = g_list_prepend(view_list, view); + view_set_page(view, CLAMP(requested_page, 0, poppler_document_get_n_pages(doc) - 1)); + } else { + g_printerr("Error opening document: %s\n", error->message); + g_error_free(error); + } } - gtk_main(); + if (view_list != nullptr) { + gtk_main(); + } return 0; } diff --git a/test/image-embedding.cc b/test/image-embedding.cc new file mode 100644 index 00000000..873b87d2 --- /dev/null +++ b/test/image-embedding.cc @@ -0,0 +1,113 @@ +//======================================================================== +// +// image-embedding.cc +// A test util to check ImageEmbeddingUtils::embed(). +// +// This file is licensed under the GPLv2 or later +// +// Copyright (C) 2021 Georgiy Sgibnev <georgiy@sgibnev.com>. Work sponsored by lab50.net. +// Copyright (C) 2022 by Albert Astals Cid <aacid@kde.org> +// +//======================================================================== + +#include <config.h> +#include <cstdio> +#include <string> + +#include "utils/parseargs.h" +#include "goo/GooString.h" +#include "Object.h" +#include "Dict.h" +#include "PDFDoc.h" +#include "PDFDocFactory.h" +#include "ImageEmbeddingUtils.h" + +static int depth = 0; +static GooString colorSpace; +static GooString filter; +static bool smask = false; +static bool fail = false; +static bool printHelp = false; + +static const ArgDesc argDesc[] = { { "-depth", argInt, &depth, 0, "XObject's property 'BitsPerComponent'" }, + { "-colorspace", argGooString, &colorSpace, 0, "XObject's property 'ColorSpace'" }, + { "-filter", argGooString, &filter, 0, "XObject's property 'Filter'" }, + { "-smask", argFlag, &smask, 0, "SMask should exist" }, + { "-fail", argFlag, &fail, 0, "the image embedding API is expected to fail" }, + { "-h", argFlag, &printHelp, 0, "print usage information" }, + { "-help", argFlag, &printHelp, 0, "print usage information" }, + { "--help", argFlag, &printHelp, 0, "print usage information" }, + { "-?", argFlag, &printHelp, 0, "print usage information" }, + {} }; + +int main(int argc, char *argv[]) +{ + // Parse args. + const bool ok = parseArgs(argDesc, &argc, argv); + if (!ok || (argc != 3) || printHelp) { + printUsage(argv[0], "PDF-FILE IMAGE-FILE", argDesc); + return (printHelp) ? 0 : 1; + } + const GooString docPath(argv[1]); + const GooString imagePath(argv[2]); + + auto doc = std::unique_ptr<PDFDoc>(PDFDocFactory().createPDFDoc(docPath)); + if (!doc->isOk()) { + fprintf(stderr, "Error opening input PDF file.\n"); + return 1; + } + + // Embed an image. + Ref baseImageRef = ImageEmbeddingUtils::embed(doc->getXRef(), imagePath.toStr()); + if (baseImageRef == Ref::INVALID()) { + if (fail) { + return 0; + } else { + fprintf(stderr, "ImageEmbeddingUtils::embed() failed.\n"); + return 1; + } + } + + // Save the updated PDF document. + // const GooString outputPathSuffix(".pdf"); + // const GooString outputPath = GooString(&imagePath, &outputPathSuffix); + // doc->saveAs(&outputPath, writeForceRewrite); + + // Check the base image. + Object baseImageObj = Object(baseImageRef).fetch(doc->getXRef()); + Dict *baseImageDict = baseImageObj.streamGetDict(); + if (std::string("XObject") != baseImageDict->lookup("Type").getName()) { + fprintf(stderr, "A problem with Type.\n"); + return 1; + } + if (std::string("Image") != baseImageDict->lookup("Subtype").getName()) { + fprintf(stderr, "A problem with Subtype.\n"); + return 1; + } + if (depth > 0) { + if (baseImageDict->lookup("BitsPerComponent").getInt() != depth) { + fprintf(stderr, "A problem with BitsPerComponent.\n"); + return 1; + } + } + if (!colorSpace.toStr().empty()) { + if (colorSpace.cmp(baseImageDict->lookup("ColorSpace").getName()) != 0) { + fprintf(stderr, "A problem with ColorSpace.\n"); + return 1; + } + } + if (!filter.toStr().empty()) { + if (filter.cmp(baseImageDict->lookup("Filter").getName()) != 0) { + fprintf(stderr, "A problem with Filter.\n"); + return 1; + } + } + if (smask) { + Object maskObj = baseImageDict->lookup("SMask"); + if (!maskObj.isStream()) { + fprintf(stderr, "A problem with SMask.\n"); + return 1; + } + } + return 0; +} diff --git a/test/pdf-fullrewrite.cc b/test/pdf-fullrewrite.cc index 968f9569..6739da4a 100644 --- a/test/pdf-fullrewrite.cc +++ b/test/pdf-fullrewrite.cc @@ -4,6 +4,7 @@ // // Copyright 2007 Julien Rebetez // Copyright 2012 Fabio D'Urso +// Copyright 2022 Albert Astals Cid <aacid@kde.org> // //======================================================================== @@ -38,10 +39,8 @@ int main(int argc, char *argv[]) { PDFDoc *doc = nullptr; PDFDoc *docOut = nullptr; - GooString *inputName = nullptr; - GooString *outputName = nullptr; - GooString *ownerPW = nullptr; - GooString *userPW = nullptr; + std::optional<GooString> ownerPW; + std::optional<GooString> userPW; int res = 0; // parse args @@ -54,19 +53,16 @@ int main(int argc, char *argv[]) goto done; } - inputName = new GooString(argv[1]); - outputName = new GooString(argv[2]); - if (ownerPassword[0] != '\001') { - ownerPW = new GooString(ownerPassword); + ownerPW = GooString(ownerPassword); } if (userPassword[0] != '\001') { - userPW = new GooString(userPassword); + userPW = GooString(userPassword); } // load input document globalParams = std::make_unique<GlobalParams>(); - doc = new PDFDoc(inputName, ownerPW, userPW); + doc = new PDFDoc(std::make_unique<GooString>(argv[1]), ownerPW, userPW); if (!doc->isOk()) { fprintf(stderr, "Error loading input document\n"); res = 1; @@ -74,7 +70,7 @@ int main(int argc, char *argv[]) } // save it back (in rewrite or incremental update mode) - if (doc->saveAs(outputName, forceIncremental ? writeForceIncremental : writeForceRewrite) != 0) { + if (doc->saveAs(*doc->getFileName(), forceIncremental ? writeForceIncremental : writeForceRewrite) != 0) { fprintf(stderr, "Error saving document\n"); res = 1; goto done; @@ -82,7 +78,7 @@ int main(int argc, char *argv[]) if (checkOutput) { // open the generated document to verify it - docOut = new PDFDoc(outputName, ownerPW, userPW); + docOut = new PDFDoc(std::make_unique<GooString>(argv[2]), ownerPW, userPW); if (!docOut->isOk()) { fprintf(stderr, "Error loading generated document\n"); res = 1; @@ -90,23 +86,20 @@ int main(int argc, char *argv[]) fprintf(stderr, "Verification failed\n"); res = 1; } - } else { - delete outputName; } done: delete docOut; delete doc; - delete userPW; - delete ownerPW; return res; } static bool compareDictionaries(Dict *dictA, Dict *dictB) { const int length = dictA->getLength(); - if (dictB->getLength() != length) + if (dictB->getLength() != length) { return false; + } /* Check that every key in dictA is contained in dictB. * Since keys are unique and we've already checked that dictA and dictB @@ -116,8 +109,9 @@ static bool compareDictionaries(Dict *dictA, Dict *dictB) const char *key = dictA->getKey(i); const Object &valA = dictA->getValNF(i); const Object &valB = dictB->lookupNF(key); - if (!compareObjects(&valA, &valB)) + if (!compareObjects(&valA, &valB)) { return false; + } } return true; diff --git a/test/pdf-inspector.cc b/test/pdf-inspector.cc index eb6c62e1..7fae2426 100644 --- a/test/pdf-inspector.cc +++ b/test/pdf-inspector.cc @@ -4,7 +4,7 @@ // // Copyright 2005 Jonathan Blandford <jrb@redhat.com> // Copyright 2018 Adam Reichold <adam.reichold@t-online.de> -// Copyright 2019 Albert Astals Cid <aacid@kde.org> +// Copyright 2019, 2022 Albert Astals Cid <aacid@kde.org> // //======================================================================== @@ -121,8 +121,9 @@ void PdfInspector::on_file_activated(GtkWidget *widget, PdfInspector *inspector) gchar *file_name; file_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget)); - if (file_name) + if (file_name) { inspector->load(file_name); + } g_free(file_name); } @@ -142,8 +143,9 @@ void PdfInspector::on_selection_changed(GtkTreeSelection *selection, PdfInspecto gtk_tree_model_get(model, &iter, OP_STRING, &op, -1); } - if (op == nullptr) + if (op == nullptr) { return; + } for (i = 0; i < G_N_ELEMENTS(op_mapping); i++) { @@ -222,10 +224,7 @@ void PdfInspector::load(const char *file_name) // load the new file if (file_name) { - GooString *filename_g; - - filename_g = new GooString(file_name); - doc = new PDFDoc(filename_g, nullptr, nullptr); + doc = new PDFDoc(std::make_unique<GooString>(file_name)); } if (doc && !doc->isOk()) { @@ -279,17 +278,18 @@ int main(int argc, char *argv[]) globalParams->setProfileCommands(true); globalParams->setPrintCommands(true); - if (argc == 2) + if (argc == 2) { file_name = argv[1]; - else if (argc > 2) { + } else if (argc > 2) { fprintf(stderr, "usage: %s [PDF-FILE]\n", argv[0]); return -1; } inspector = new PdfInspector(); - if (file_name) + if (file_name) { inspector->set_file_name(file_name); + } inspector->run(); diff --git a/test/perf-test.cc b/test/perf-test.cc index a8fbc109..dcecfd7a 100644 --- a/test/perf-test.cc +++ b/test/perf-test.cc @@ -1,6 +1,6 @@ /* Copyright Krzysztof Kowalczyk 2006-2007 Copyright Hib Eris <hib@hiberis.nl> 2008, 2013 - Copyright 2018 Albert Astals Cid <aacid@kde.org> 2018 + Copyright 2018, 2020, 2022 Albert Astals Cid <aacid@kde.org> 2018 Copyright 2019 Oliver Sander <oliver.sander@tu-dresden.de> Copyright 2020 Adam Reichold <adam.reichold@t-online.de> License: GPLv2 */ @@ -35,7 +35,7 @@ // before it's tested. This is desired if a file is on a slow drive. // Currently copying only works on Windows. // Not enabled by default. -//#define COPY_FILE 1 +// #define COPY_FILE 1 #include <cassert> #include <cstdio> @@ -197,14 +197,6 @@ static bool gfLoadOnly = false; #define UNIX_NEWLINE "\x0a" #define UNIX_NEWLINE_C 0xa -#ifdef _WIN32 -# define DIR_SEP_CHAR '\\' -# define DIR_SEP_STR "\\" -#else -# define DIR_SEP_CHAR '/' -# define DIR_SEP_STR "/" -#endif - static void memzero(void *data, size_t len) { memset(data, 0, len); @@ -213,8 +205,9 @@ static void memzero(void *data, size_t len) static void *zmalloc(size_t len) { void *data = malloc(len); - if (data) + if (data) { memzero(data, len); + } return data; } @@ -229,18 +222,23 @@ static char *str_cat4(const char *str1, const char *str2, const char *str3, cons size_t str3_len = 0; size_t str4_len = 0; - if (str1) + if (str1) { str1_len = strlen(str1); - if (str2) + } + if (str2) { str2_len = strlen(str2); - if (str3) + } + if (str3) { str3_len = strlen(str3); - if (str4) + } + if (str4) { str4_len = strlen(str4); + } str = (char *)zmalloc(str1_len + str2_len + str3_len + str4_len + 1); - if (!str) + if (!str) { return nullptr; + } tmp = str; if (str1) { @@ -268,23 +266,29 @@ static char *str_dup(const char *str) static bool str_eq(const char *str1, const char *str2) { - if (!str1 && !str2) + if (!str1 && !str2) { return true; - if (!str1 || !str2) + } + if (!str1 || !str2) { return false; - if (0 == strcmp(str1, str2)) + } + if (0 == strcmp(str1, str2)) { return true; + } return false; } static bool str_ieq(const char *str1, const char *str2) { - if (!str1 && !str2) + if (!str1 && !str2) { return true; - if (!str1 || !str2) + } + if (!str1 || !str2) { return false; - if (0 == strcasecmp(str1, str2)) + } + if (0 == strcasecmp(str1, str2)) { return true; + } return false; } @@ -293,15 +297,18 @@ static bool str_endswith(const char *txt, const char *end) size_t end_len; size_t txt_len; - if (!txt || !end) + if (!txt || !end) { return false; + } txt_len = strlen(txt); end_len = strlen(end); - if (end_len > txt_len) + if (end_len > txt_len) { return false; - if (str_eq(txt + txt_len - end_len, end)) + } + if (str_eq(txt + txt_len - end_len, end)) { return true; + } return false; } @@ -320,47 +327,20 @@ static void sleep_milliseconds(int milliseconds) tv.tv_nsec = (long)nanosecs; while (true) { int rval = nanosleep(&tv, &tv); - if (rval == 0) + if (rval == 0) { /* Completed the entire sleep time; all done. */ return; - else if (errno == EINTR) + } else if (errno == EINTR) { /* Interrupted by a signal. Try again. */ continue; - else + } else { /* Some other error; bail out. */ return; - } - return; -#endif -} - -#ifndef HAVE_STRCPY_S -static void strcpy_s(char *dst, size_t dst_size, const char *src) -{ - size_t src_size = strlen(src) + 1; - if (src_size <= dst_size) - memcpy(dst, src, src_size); - else { - if (dst_size > 0) { - memcpy(dst, src, dst_size); - dst[dst_size - 1] = 0; } } -} + return; #endif - -#ifndef HAVE_STRCAT_S -static void strcat_s(char *dst, size_t dst_size, const char *src) -{ - size_t dst_len = strlen(dst); - if (dst_len >= dst_size) { - if (dst_size > 0) - dst[dst_size - 1] = 0; - return; - } - strcpy_s(dst + dst_len, dst_size - dst_len, src); } -#endif static SplashColorMode gSplashColorMode = splashModeBGR8; @@ -418,12 +398,8 @@ PdfEnginePoppler::~PdfEnginePoppler() bool PdfEnginePoppler::load(const char *fileName) { setFileName(fileName); - /* note: don't delete fileNameStr since PDFDoc takes ownership and deletes them itself */ - GooString *fileNameStr = new GooString(fileName); - if (!fileNameStr) - return false; - _pdfDoc = new PDFDoc(fileNameStr, nullptr, nullptr, nullptr); + _pdfDoc = new PDFDoc(std::make_unique<GooString>(fileName)); if (!_pdfDoc->isOk()) { return false; } @@ -436,8 +412,9 @@ SplashOutputDev *PdfEnginePoppler::outputDevice() if (!_outputDev) { bool bitmapTopDown = true; _outputDev = new SplashOutputDev(gSplashColorMode, 4, false, gBgColor, bitmapTopDown); - if (_outputDev) + if (_outputDev) { _outputDev->startDoc(_pdfDoc); + } } return _outputDev; } @@ -445,8 +422,9 @@ SplashOutputDev *PdfEnginePoppler::outputDevice() SplashBitmap *PdfEnginePoppler::renderBitmap(int pageNo, double zoomReal, int rotation) { assert(outputDevice()); - if (!outputDevice()) + if (!outputDevice()) { return nullptr; + } double hDPI = (double)PDF_FILE_DPI * zoomReal * 0.01; double vDPI = (double)PDF_FILE_DPI * zoomReal * 0.01; @@ -459,203 +437,14 @@ SplashBitmap *PdfEnginePoppler::renderBitmap(int pageNo, double zoomReal, int ro return bmp; } -struct FindFileState -{ - char path[MAX_FILENAME_SIZE]; - char dirpath[MAX_FILENAME_SIZE]; /* current dir path */ - char pattern[MAX_FILENAME_SIZE]; /* search pattern */ - const char *bufptr; -#ifdef _WIN32 - WIN32_FIND_DATA fileinfo; - HANDLE dir; -#else - DIR *dir; -#endif -}; - -#ifdef _WIN32 -# include <sys/timeb.h> -# include <direct.h> - -__inline char *getcwd(char *buffer, int maxlen) -{ - return _getcwd(buffer, maxlen); -} - -static int fnmatch(const char *pattern, const char *string, int flags) -{ - int prefix_len; - const char *star_pos = strchr(pattern, '*'); - if (!star_pos) - return strcmp(pattern, string) != 0; - - prefix_len = (int)(star_pos - pattern); - if (0 == prefix_len) - return 0; - - if (0 == _strnicmp(pattern, string, prefix_len)) - return 0; - - return 1; -} - -#else -# include <fnmatch.h> -#endif - -#ifdef _WIN32 -/* on windows to query dirs we need foo\* to get files in this directory. - foo\ always fails and foo will return just info about foo directory, - not files in this directory */ -static void win_correct_path_for_FindFirstFile(char *path, int path_max_len) -{ - int path_len = strlen(path); - if (path_len >= path_max_len - 4) - return; - if (DIR_SEP_CHAR != path[path_len]) - path[path_len++] = DIR_SEP_CHAR; - path[path_len++] = '*'; - path[path_len] = 0; -} -#endif - -static FindFileState *find_file_open(const char *path, const char *pattern) -{ - FindFileState *s; - - s = (FindFileState *)malloc(sizeof(FindFileState)); - if (!s) - return nullptr; - strcpy_s(s->path, sizeof(s->path), path); - strcpy_s(s->dirpath, sizeof(s->path), path); -#ifdef _WIN32 - win_correct_path_for_FindFirstFile(s->path, sizeof(s->path)); -#endif - strcpy_s(s->pattern, sizeof(s->pattern), pattern); - s->bufptr = s->path; -#ifdef _WIN32 - s->dir = INVALID_HANDLE_VALUE; -#else - s->dir = nullptr; -#endif - return s; -} - -#if 0 /* re-enable if we #define USE_OWN_GET_AUTH_DATA */ -void *StandardSecurityHandler::getAuthData() -{ - return NULL; -} -#endif - -static char *makepath(char *buf, int buf_size, const char *path, const char *filename) -{ - strcpy_s(buf, buf_size, path); - int len = strlen(path); - if (len > 0 && path[len - 1] != DIR_SEP_CHAR && len + 1 < buf_size) { - buf[len++] = DIR_SEP_CHAR; - buf[len] = '\0'; - } - strcat_s(buf, buf_size, filename); - return buf; -} - -#ifdef _WIN32 -static int skip_matching_file(const char *filename) -{ - if (0 == strcmp(".", filename)) - return 1; - if (0 == strcmp("..", filename)) - return 1; - return 0; -} -#endif - -static int find_file_next(FindFileState *s, char *filename, int filename_size_max) -{ -#ifdef _WIN32 - int fFound; - if (INVALID_HANDLE_VALUE == s->dir) { - s->dir = FindFirstFile(s->path, &(s->fileinfo)); - if (INVALID_HANDLE_VALUE == s->dir) - return -1; - goto CheckFile; - } - - while (1) { - fFound = FindNextFile(s->dir, &(s->fileinfo)); - if (!fFound) - return -1; - CheckFile: - if (skip_matching_file(s->fileinfo.cFileName)) - continue; - if (0 == fnmatch(s->pattern, s->fileinfo.cFileName, 0)) { - makepath(filename, filename_size_max, s->dirpath, s->fileinfo.cFileName); - return 0; - } - } -#else - struct dirent *dirent; - const char *p; - char *q; - - if (s->dir == nullptr) - goto redo; - - for (;;) { - dirent = readdir(s->dir); - if (dirent == nullptr) { - redo: - if (s->dir) { - closedir(s->dir); - s->dir = nullptr; - } - p = s->bufptr; - if (*p == '\0') - return -1; - /* CG: get_str(&p, s->dirpath, sizeof(s->dirpath), ":") */ - q = s->dirpath; - while (*p != ':' && *p != '\0') { - if ((q - s->dirpath) < (int)sizeof(s->dirpath) - 1) - *q++ = *p; - p++; - } - *q = '\0'; - if (*p == ':') - p++; - s->bufptr = p; - s->dir = opendir(s->dirpath); - if (!s->dir) - goto redo; - } else { - if (fnmatch(s->pattern, dirent->d_name, 0) == 0) { - makepath(filename, filename_size_max, s->dirpath, dirent->d_name); - return 0; - } - } - } -#endif -} - -static void find_file_close(FindFileState *s) -{ -#ifdef _WIN32 - if (INVALID_HANDLE_VALUE != s->dir) - FindClose(s->dir); -#else - if (s->dir) - closedir(s->dir); -#endif - free(s); -} - static int StrList_Len(StrList **root) { int len = 0; StrList *cur; assert(root); - if (!root) + if (!root) { return 0; + } cur = *root; while (cur) { ++len; @@ -668,12 +457,14 @@ static int StrList_InsertAndOwn(StrList **root, char *txt) { StrList *el; assert(root && txt); - if (!root || !txt) + if (!root || !txt) { return false; + } el = (StrList *)malloc(sizeof(StrList)); - if (!el) + if (!el) { return false; + } el->str = txt; el->next = *root; *root = el; @@ -685,11 +476,13 @@ static int StrList_Insert(StrList **root, char *txt) char *txtDup; assert(root && txt); - if (!root || !txt) + if (!root || !txt) { return false; + } txtDup = str_dup(txt); - if (!txtDup) + if (!txtDup) { return false; + } if (!StrList_InsertAndOwn(root, txtDup)) { free((void *)txtDup); @@ -698,25 +491,11 @@ static int StrList_Insert(StrList **root, char *txt) return true; } -static StrList *StrList_RemoveHead(StrList **root) -{ - StrList *tmp; - assert(root); - if (!root) - return nullptr; - - if (!*root) - return nullptr; - tmp = *root; - *root = tmp->next; - tmp->next = nullptr; - return tmp; -} - static void StrList_FreeElement(StrList *el) { - if (!el) + if (!el) { return; + } free((void *)el->str); free((void *)el); } @@ -726,8 +505,9 @@ static void StrList_Destroy(StrList **root) StrList *cur; StrList *next; - if (!root) + if (!root) { return; + } cur = *root; while (cur) { next = cur->next; @@ -819,22 +599,23 @@ static void PrintUsageAndExit(int argc, char **argv) static bool ShowPreview() { - if (gfPreview || gfSlowPreview) + if (gfPreview || gfSlowPreview) { return true; + } return false; } static void RenderPdfAsText(const char *fileName) { - GooString *fileNameStr = nullptr; PDFDoc *pdfDoc = nullptr; GooString *txt = nullptr; int pageCount; double timeInMs; assert(fileName); - if (!fileName) + if (!fileName) { return; + } LogInfo("started: %s\n", fileName); @@ -845,12 +626,7 @@ static void RenderPdfAsText(const char *fileName) } GooTimer msTimer; - /* note: don't delete fileNameStr since PDFDoc takes ownership and deletes them itself */ - fileNameStr = new GooString(fileName); - if (!fileNameStr) - goto Exit; - - pdfDoc = new PDFDoc(fileNameStr, nullptr, nullptr, nullptr); + pdfDoc = new PDFDoc(std::make_unique<GooString>(fileName)); if (!pdfDoc->isOk()) { error(errIO, -1, "RenderPdfFile(): failed to open PDF file {0:s}\n", fileName); goto Exit; @@ -864,8 +640,9 @@ static void RenderPdfAsText(const char *fileName) LogInfo("page count: %d\n", pageCount); for (int curPage = 1; curPage <= pageCount; curPage++) { - if ((gPageNo != PAGE_NO_NOT_GIVEN) && (gPageNo != curPage)) + if ((gPageNo != PAGE_NO_NOT_GIVEN) && (gPageNo != curPage)) { continue; + } msTimer.start(); int rotate = 0; @@ -876,8 +653,9 @@ static void RenderPdfAsText(const char *fileName) txt = textOut->getText(0.0, 0.0, 10000.0, 10000.0); msTimer.stop(); timeInMs = msTimer.getElapsed(); - if (gfTimings) + if (gfTimings) { LogInfo("page %d: %.2f ms\n", curPage, timeInMs); + } printf("%s\n", txt->c_str()); delete txt; txt = nullptr; @@ -924,12 +702,14 @@ static void RenderPdf(const char *fileName) pageCount = engineSplash->pageCount(); LogInfo("page count: %d\n", pageCount); - if (gfLoadOnly) + if (gfLoadOnly) { goto Error; + } for (int curPage = 1; curPage <= pageCount; curPage++) { - if ((gPageNo != PAGE_NO_NOT_GIVEN) && (gPageNo != curPage)) + if ((gPageNo != PAGE_NO_NOT_GIVEN) && (gPageNo != curPage)) { continue; + } SplashBitmap *bmpSplash = nullptr; @@ -938,16 +718,18 @@ static void RenderPdf(const char *fileName) msRenderTimer.stop(); timeInMs = msRenderTimer.getElapsed(); if (gfTimings) { - if (!bmpSplash) + if (!bmpSplash) { LogInfo("page splash %d: failed to render\n", curPage); - else + } else { LogInfo("page splash %d (%dx%d): %.2f ms\n", curPage, bmpSplash->getWidth(), bmpSplash->getHeight(), timeInMs); + } } if (ShowPreview()) { PreviewBitmapSplash(bmpSplash); - if (gfSlowPreview) + if (gfSlowPreview) { sleep_milliseconds(SLOW_PREVIEW_TIME); + } } delete bmpSplash; } @@ -974,24 +756,28 @@ static bool ParseInteger(const char *start, const char *end, int *intOut) assert(start && end && intOut); assert(end >= start); - if (!start || !end || !intOut || (start > end)) + if (!start || !end || !intOut || (start > end)) { return false; + } digitsCount = 0; tmp = start; while (tmp <= end) { if (isspace(*tmp)) { /* do nothing, we allow whitespace */ - } else if (!isdigit(*tmp)) + } else if (!isdigit(*tmp)) { return false; + } numBuf[digitsCount] = *tmp; ++digitsCount; - if (digitsCount == dimof(numBuf) - 3) /* -3 to be safe */ + if (digitsCount == dimof(numBuf) - 3) { /* -3 to be safe */ return false; + } ++tmp; } - if (0 == digitsCount) + if (0 == digitsCount) { return false; + } numBuf[digitsCount] = 0; *intOut = atoi(numBuf); return true; @@ -1007,21 +793,27 @@ static bool ParseResolutionString(const char *resolutionString, int *resolutionX assert(resolutionString); assert(resolutionXOut); assert(resolutionYOut); - if (!resolutionString || !resolutionXOut || !resolutionYOut) + if (!resolutionString || !resolutionXOut || !resolutionYOut) { return false; + } *resolutionXOut = 0; *resolutionYOut = 0; posOfX = strchr(resolutionString, 'X'); - if (!posOfX) + if (!posOfX) { posOfX = strchr(resolutionString, 'x'); - if (!posOfX) + } + if (!posOfX) { return false; - if (posOfX == resolutionString) + } + if (posOfX == resolutionString) { return false; - if (!ParseInteger(resolutionString, posOfX - 1, resolutionXOut)) + } + if (!ParseInteger(resolutionString, posOfX - 1, resolutionXOut)) { return false; - if (!ParseInteger(posOfX + 1, resolutionString + strlen(resolutionString) - 1, resolutionYOut)) + } + if (!ParseInteger(posOfX + 1, resolutionString + strlen(resolutionString) - 1, resolutionYOut)) { return false; + } return true; } @@ -1029,8 +821,9 @@ static void ParseCommandLine(int argc, char **argv) { char *arg; - if (argc < 2) + if (argc < 2) { PrintUsageAndExit(argc, argv); + } for (int i = 1; i < argc; i++) { arg = argv[i]; @@ -1040,18 +833,21 @@ static void ParseCommandLine(int argc, char **argv) gfTimings = true; } else if (str_ieq(arg, RESOLUTION_ARG)) { ++i; - if (i == argc) + if (i == argc) { PrintUsageAndExit(argc, argv); /* expect a file name after that */ - if (!ParseResolutionString(argv[i], &gResolutionX, &gResolutionY)) + } + if (!ParseResolutionString(argv[i], &gResolutionX, &gResolutionY)) { PrintUsageAndExit(argc, argv); + } gfForceResolution = true; } else if (str_ieq(arg, RECURSIVE_ARG)) { gfRecursive = true; } else if (str_ieq(arg, OUT_ARG)) { /* expect a file name after that */ ++i; - if (i == argc) + if (i == argc) { PrintUsageAndExit(argc, argv); + } gOutFileName = str_dup(argv[i]); } else if (str_ieq(arg, PREVIEW_ARG)) { gfPreview = true; @@ -1064,11 +860,13 @@ static void ParseCommandLine(int argc, char **argv) } else if (str_ieq(arg, PAGE_ARG)) { /* expect an integer after that */ ++i; - if (i == argc) + if (i == argc) { PrintUsageAndExit(argc, argv); + } gPageNo = atoi(argv[i]); - if (gPageNo < 1) + if (gPageNo < 1) { PrintUsageAndExit(argc, argv); + } } else { /* unknown option */ PrintUsageAndExit(argc, argv); @@ -1081,147 +879,25 @@ static void ParseCommandLine(int argc, char **argv) } } -#if 0 -void RenderFileList(char *pdfFileList) -{ - char *data = NULL; - char *dataNormalized = NULL; - char *pdfFileName; - uint64_t fileSize; - - assert(pdfFileList); - if (!pdfFileList) - return; - data = file_read_all(pdfFileList, &fileSize); - if (!data) { - error(-1, "couldn't load file '%s'", pdfFileList); - return; - } - dataNormalized = str_normalize_newline(data, UNIX_NEWLINE); - if (!dataNormalized) { - error(-1, "couldn't normalize data of file '%s'", pdfFileList); - goto Exit; - } - for (;;) { - pdfFileName = str_split_iter(&dataNormalized, UNIX_NEWLINE_C); - if (!pdfFileName) - break; - str_strip_ws_both(pdfFileName); - if (str_empty(pdfFileName)) { - free((void*)pdfFileName); - continue; - } - RenderFile(pdfFileName); - free((void*)pdfFileName); - } -Exit: - free((void*)dataNormalized); - free((void*)data); -} -#endif - -#ifdef _WIN32 -# include <sys/types.h> -# include <sys/stat.h> - -static bool IsDirectoryName(char *path) -{ - struct _stat buf; - int result; - - result = _stat(path, &buf); - if (0 != result) - return false; - - if (buf.st_mode & _S_IFDIR) - return true; - - return false; -} - -static bool IsFileName(char *path) -{ - struct _stat buf; - int result; - - result = _stat(path, &buf); - if (0 != result) - return false; - - if (buf.st_mode & _S_IFREG) - return true; - - return false; -} -#else -static bool IsDirectoryName(char *path) -{ - /* TODO: implement me */ - return false; -} - -static bool IsFileName(char *path) -{ - /* TODO: implement me */ - return true; -} -#endif - static bool IsPdfFileName(char *path) { - if (str_endswith(path, ".pdf")) + if (str_endswith(path, ".pdf")) { return true; - return false; -} - -static void RenderDirectory(char *path) -{ - FindFileState *ffs; - char filename[MAX_FILENAME_SIZE]; - StrList *dirList = nullptr; - StrList *el; - - StrList_Insert(&dirList, path); - - while (0 != StrList_Len(&dirList)) { - el = StrList_RemoveHead(&dirList); - ffs = find_file_open(el->str, "*"); - while (!find_file_next(ffs, filename, sizeof(filename))) { - if (IsDirectoryName(filename)) { - if (gfRecursive) { - StrList_Insert(&dirList, filename); - } - } else if (IsFileName(filename)) { - if (IsPdfFileName(filename)) { - RenderFile(filename); - } - } - } - find_file_close(ffs); - StrList_FreeElement(el); } - StrList_Destroy(&dirList); + return false; } /* Render 'cmdLineArg', which can be: - - directory name - name of PDF file - - name of text file with names of PDF files */ static void RenderCmdLineArg(char *cmdLineArg) { assert(cmdLineArg); - if (!cmdLineArg) + if (!cmdLineArg) { return; - if (IsDirectoryName(cmdLineArg)) { - RenderDirectory(cmdLineArg); - } else if (IsFileName(cmdLineArg)) { - if (IsPdfFileName(cmdLineArg)) - RenderFile(cmdLineArg); -#if 0 - else - RenderFileList(cmdLineArg); -#endif + } + if (IsPdfFileName(cmdLineArg)) { + RenderFile(cmdLineArg); } else { error(errCommandLine, -1, "unexpected argument '{0:s}'", cmdLineArg); } @@ -1231,14 +907,16 @@ int main(int argc, char **argv) { setErrorCallback(my_error); ParseCommandLine(argc, argv); - if (0 == StrList_Len(&gArgsListRoot)) + if (0 == StrList_Len(&gArgsListRoot)) { PrintUsageAndExit(argc, argv); + } assert(gArgsListRoot); SplashColorsInit(); globalParams = std::make_unique<GlobalParams>(); - if (!globalParams) + if (!globalParams) { return 1; + } globalParams->setErrQuiet(false); FILE *outFile = nullptr; @@ -1249,13 +927,15 @@ int main(int argc, char **argv) return 1; } gOutFile = outFile; - } else + } else { gOutFile = stdout; + } - if (gOutFileName) + if (gOutFileName) { gErrFile = outFile; - else + } else { gErrFile = stderr; + } PreviewBitmapInit(); @@ -1264,8 +944,9 @@ int main(int argc, char **argv) RenderCmdLineArg(curr->str); curr = curr->next; } - if (outFile) + if (outFile) { fclose(outFile); + } PreviewBitmapDestroy(); StrList_Destroy(&gArgsListRoot); free(gOutFileName); |