summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/CMakeLists.txt141
-rw-r--r--test/cairo-thread-test.cc561
-rw-r--r--test/goostring-format-checker/README2
-rw-r--r--test/goostring-format-checker/goostring-format-checker.cc3
-rw-r--r--test/gtk-test.cc80
-rw-r--r--test/image-embedding.cc113
-rw-r--r--test/pdf-fullrewrite.cc30
-rw-r--r--test/pdf-inspector.cc20
-rw-r--r--test/perf-test.cc563
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);