aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJay Berkenbilt <ejb@ql.org>2022-03-05 14:24:51 +0100
committerJay Berkenbilt <jberkenbilt@users.noreply.github.com>2022-03-19 00:53:18 +0100
commitb8aff90997116a84350018f88f1eabdaa368d11b (patch)
tree5ee781b03d3ffa81bfdd813e91f6324a75b266e6
parent105862da3ea72c6763c26038d2e02cf243eb0798 (diff)
downloadqpdf-b8aff90997116a84350018f88f1eabdaa368d11b.tar.zst
Add cmake configuration files
-rw-r--r--CMakeLists.txt328
-rw-r--r--cmake/qpdfCheckFlag.cmake21
-rw-r--r--examples/CMakeLists.txt52
-rw-r--r--fuzz/CMakeLists.txt178
-rw-r--r--include/CMakeLists.txt12
-rw-r--r--libqpdf/CMakeLists.txt581
-rw-r--r--libtests/CMakeLists.txt53
-rw-r--r--manual/CMakeLists.txt133
-rw-r--r--qpdf/CMakeLists.txt78
-rw-r--r--qpdfConfig.cmake.in2
-rw-r--r--zlib-flate/CMakeLists.txt16
11 files changed, 1454 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 00000000..d3284626
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,328 @@
+# manual/installation.rst mentions the minimum cmake version.
+cmake_minimum_required(VERSION 3.16)
+
+# make_dist expects the version line to be on a line by itself after
+# the project line.
+project(qpdf
+ VERSION 10.6.3
+ LANGUAGES C CXX)
+
+# Enable correct rpath handling for MacOSX
+cmake_policy(SET CMP0042 NEW)
+# Honor CMAKE_REQUIRED_LIBRARIES when checking for include files
+cmake_policy(SET CMP0075 NEW)
+
+# *** OPTIONS ***
+
+# Keep all options here. It's easier to see the interdependencies this
+# way than spreading them throughout the files.
+#
+# ***** Keep manual/installation.rst (_build-options) up to date. *****
+
+include(CMakeDependentOption)
+
+# CMAKE_DEPENDENT_OPTION(
+# OPTION "Description" default-value-if-visible
+# "when-visible" value-if-not-visible)
+
+# Don't write tests based on MAINTAINER_MODE or CI_MODE. Instead, use
+# them as the basis for dependent options.
+
+option(MAINTAINER_MODE "Set options for developing qpdf" OFF)
+CMAKE_DEPENDENT_OPTION(
+ CI_MODE "Set options for running in CI" OFF
+ "NOT MAINTAINER_MODE" OFF)
+CMAKE_DEPENDENT_OPTION(
+ WERROR "Treat compiler warnings as errors" OFF
+ "NOT MAINTAINER_MODE; NOT CI_MODE" ON)
+CMAKE_DEPENDENT_OPTION(
+ GENERATE_AUTO_JOB "Automatically regenerate job files" OFF
+ "NOT MAINTAINER_MODE" ON)
+CMAKE_DEPENDENT_OPTION(
+ SHOW_FAILED_TEST_OUTPUT "Show qtest output on failure" OFF
+ "NOT CI_MODE" ON)
+
+# To allow building doc to be disabled in maintainer mode, handle the
+# condition manually rather than using a dependent option.
+if(MAINTAINER_MODE)
+ set(default_BUILD_DOC ON)
+else()
+ set(default_BUILD_DOC OFF)
+endif()
+option(BUILD_DOC "Build documentation" ${default_BUILD_DOC})
+# The values of BUILD_DOC_HTML and BUILD_DOC_PDF are ignored without
+# BUILD_DOC, so setting them to ON when not visible forces them to be
+# on in MAINTAINER_MODE and is harmless if BUILD_DOC is off.
+CMAKE_DEPENDENT_OPTION(
+ BUILD_DOC_HTML "Build HTML documentation"
+ ON "BUILD_DOC;NOT MAINTAINER_MODE" ON)
+CMAKE_DEPENDENT_OPTION(
+ BUILD_DOC_PDF "Build PDF documentation"
+ ON "BUILD_DOC;NOT MAINTAINER_MODE" ON)
+CMAKE_DEPENDENT_OPTION(
+ BUILD_DOC_DIST "Create distribution of manual" ON
+ "BUILD_DOC_PDF;BUILD_DOC_HTML" OFF)
+
+option(BUILD_SHARED_LIBS "Build qpdf shared libraries" ON)
+option(BUILD_STATIC_LIBS "Build qpdf static libraries" ON)
+option(QTEST_COLOR "Whether qtest's output should be in color" ON)
+option(USE_INSECURE_RANDOM "Use insecure random numbers" OFF)
+option(SKIP_OS_SECURE_RANDOM
+ "Suppress use of OS-provided secure random numbers" OFF)
+CMAKE_DEPENDENT_OPTION(
+ AVOID_WINDOWS_HANDLE "Avoid use of HANDLE in Windows" OFF
+ "WIN32" OFF)
+
+option(OSS_FUZZ "Specific build configuration for the oss-fuzz project" OFF)
+
+option(USE_IMPLICIT_CRYPTO "Enable any available external crypto provider" ON)
+CMAKE_DEPENDENT_OPTION(
+ ALLOW_CRYPTO_NATIVE "Allow native crypto as as fallback" ON
+ "USE_IMPLICIT_CRYPTO" OFF)
+CMAKE_DEPENDENT_OPTION(
+ REQUIRE_CRYPTO_NATIVE "Require native crypto provider" OFF
+ "NOT MAINTAINER_MODE; NOT CI_MODE" ON)
+option(REQUIRE_CRYPTO_OPENSSL "Require openssl crypto" OFF)
+option(REQUIRE_CRYPTO_GNUTLS "Require gnutls crypto" OFF)
+set(DEFAULT_CRYPTO CACHE STRING "")
+option(DEFAULT_CRYPTO
+ "Specify default crypto; otherwise chosen automatically" "")
+
+# INSTALL_MANUAL is not dependent on building docs. When creating some
+# distributions, we build the doc in one run, copy doc-dist in, and
+# install it elsewhere.
+option(INSTALL_MANUAL "Install documentation" OFF)
+
+option(INSTALL_PKGCONFIG "Install pkgconfig file" ON)
+option(INSTALL_CMAKE_PACKAGE "Install cmake package files" ON)
+option(INSTALL_EXAMPLES "Install example files" ON)
+
+# *** END OPTIONS ***
+
+if(NOT (BUILD_STATIC_LIBS OR BUILD_SHARED_LIBS))
+ message(
+ FATAL_ERROR "At least one of static or shared libraries must be built")
+endif()
+
+add_compile_definitions($<$<COMPILE_LANGUAGE:CXX>:POINTERHOLDER_TRANSITION=2>)
+
+enable_testing()
+set(RUN_QTEST perl ${qpdf_SOURCE_DIR}/run-qtest)
+
+if(WIN32)
+ find_program(COPY_COMMAND NAMES cp copy)
+ if(COPY_COMMAND STREQUAL "COPY_COMMAND-NOTFOUND")
+ set(COPY_COMMAND "copy")
+ endif()
+else()
+ set(COPY_COMMAND "cp")
+endif()
+
+# For a long time, qpdf used libtool's version system. We are no
+# longer doing that, but to avoid potential conflict with older
+# versions, continue to have the shared library symlink point to a
+# file whose version shares minor and patch with the project version
+# and major with the SOVERSION. Starting with the transition to cmake,
+# increment SOVERSION every time we increment the project major
+# version. This works because qpdf uses semantic versioning. qpdf 10.x
+# was libqpdf28, so start from there.
+math(EXPR qpdf_SOVERSION "${PROJECT_VERSION_MAJOR} + 18")
+set(qpdf_LIBVERSION ${qpdf_SOVERSION}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH})
+
+if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR})
+ message(FATAL_ERROR "
+Please build with cmake in a subdirectory, e.g.
+ mkdir build
+ cmake ..
+ cmake --build .
+Please remove CMakeCache.txt and the CMakeFiles directories.")
+endif()
+
+set(CMAKE_CXX_STANDARD 14)
+set(CMAKE_CXX_EXTENSIONS OFF)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+set(CMAKE_C_VISIBILITY_PRESET hidden)
+set(CMAKE_CXX_VISIBILITY_PRESET hidden)
+
+set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
+
+if(WIN32 AND NOT SKIP_OS_SECURE_RANDOM)
+ list(APPEND CMAKE_REQUIRED_LIBRARIES Advapi32)
+endif()
+
+include(CheckCXXSourceCompiles)
+function(check_atomic)
+ foreach(I 0 1)
+ if(I)
+ set(CMAKE_REQUIRED_LIBRARIES atomic)
+ endif()
+ check_cxx_source_compiles(
+ "#include <atomic>
+int main() {
+ static std::atomic<int> a{0};
+ a = a.fetch_add(1);
+ return 0;
+}"
+ ATOMIC_WORKED${I})
+ if(ATOMIC_WORKED0)
+ return()
+ endif()
+ endforeach()
+ if(ATOMIC_WORKED1)
+ list(APPEND CMAKE_REQUIRED_LIBRARIES atomic)
+ endif()
+endfunction()
+check_atomic()
+
+set(WINDOWS_WMAIN_COMPILE "")
+set(WINDOWS_WMAIN_LINK "")
+if(WIN32)
+ function(check_wmain)
+ foreach(I 0 1)
+ if(NOT WINDOWS_WMAIN_COMPILE)
+ if(I)
+ set(CMAKE_REQUIRED_LINK_OPTIONS -municode)
+ endif()
+ check_cxx_source_compiles(
+ "#include <windows.h>
+#include <string.h>
+#include <stdio.h>
+extern \"C\"
+int wmain(int argc, wchar_t* argv[])
+{
+ size_t x = wcslen(argv[0]);
+ return 0;
+}
+"
+ WMAIN_WORKED${I})
+ endif()
+ endforeach()
+ if(WMAIN_WORKED1 OR WMAIN_WORKED1)
+ set(WINDOWS_WMAIN_COMPILE -DWINDOWS_WMAIN PARENT_SCOPE)
+ if(WMAIN_WORKED1 AND NOT WMAIN_WORKED0)
+ set(WINDOWS_WMAIN_LINK -municode PARENT_SCOPE)
+ endif()
+ endif()
+ endfunction()
+ check_wmain()
+endif()
+
+if(MSVC)
+ list(APPEND CMAKE_REQUIRED_LINK_OPTIONS -link setargv.obj)
+ list(APPEND WINDOWS_WMAIN_LINK -link wsetargv.obj)
+endif()
+
+include(GNUInstallDirs)
+
+# Compiler flags
+#
+# **NOTE** -- each flag must have its own cache variable.
+
+include(qpdfCheckFlag)
+if(WERROR)
+ if(MSVC)
+ add_compile_options(/WX)
+ else()
+ qpdf_maybe_add_flag(C -Werror flag_werror)
+ endif()
+endif()
+
+if(MSVC)
+ # /Gy combines identical functions -- useful with C++ templates
+ add_compile_options(/Gy)
+ add_compile_options(/W3) # warning level 3
+else()
+ qpdf_maybe_add_flag(C -Wall flag_wall)
+ qpdf_maybe_add_flag(C -Wconversion flag_conversion)
+ qpdf_maybe_add_flag(C -Wsign-conversion flag_sign-conversion)
+ qpdf_maybe_add_flag(C -Wshadow=local flag_shadow_local)
+ qpdf_maybe_add_flag(CXX -Wold-style-cast flag_old-style-cast)
+endif()
+
+# We don't include the jpeg library's include path in the PUBLIC
+# interface for libqpdf since only Pl_DCT.hh requires it. This is
+# documented. Some examples and tests use it though so we have to
+# define it. CMakeLists.txt for libqpdf sets the value for
+# JPEG_INCLUDE which can be selectively added to include paths other
+# tools.
+set(JPEG_INCLUDE)
+
+if(${CMAKE_SIZEOF_VOID_P} EQUAL 8)
+ set(WORDSIZE 64)
+else()
+ set(WORDSIZE 32)
+endif()
+if(MSVC)
+ set(CPACK_SYSTEM_NAME "msvc${WORDSIZE}")
+elseif(MINGW)
+ set(CPACK_SYSTEM_NAME "mingw${WORDSIZE}")
+endif()
+set(CPACK_RESOURCE_FILE_LICENSE "${qpdf_SOURCE_DIR}/LICENSE.txt")
+set(CPACK_PACKAGE_HOMEPAGE_URL "https://qpdf.sourceforge.io/")
+set(CPACK_NSIS_MUI_ICON "${qpdf_SOURCE_DIR}/logo/qpdf.ico")
+
+include(CPack)
+
+# Install components -- documented in _installation in
+# manual/installation.rst.
+set(COMPONENT_DEV "dev")
+set(COMPONENT_LIB "lib") # runtime library
+set(COMPONENT_CLI "cli")
+set(COMPONENT_DOC "doc")
+set(COMPONENT_EXAMPLES "examples") # example sources
+
+if(WIN32)
+ include(InstallRequiredSystemLibraries)
+endif()
+
+# add_subdirectory order affects test order
+add_subdirectory(include)
+add_subdirectory(libqpdf)
+add_subdirectory(qpdf)
+add_subdirectory(libtests)
+add_subdirectory(examples)
+add_subdirectory(zlib-flate)
+add_subdirectory(manual)
+add_subdirectory(fuzz)
+
+# We don't need to show everything -- just the things that we really
+# need to be sure are right or that are turned on or off with complex
+# logic.
+get_property(MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
+message(STATUS "")
+message(STATUS "*** Summary ***")
+message(STATUS " qpdf version: ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
+if(MULTI_CONFIG)
+ message(STATUS " build type: specify --config at build time")
+elseif(CMAKE_BUILD_TYPE)
+ message(STATUS " build type: ${CMAKE_BUILD_TYPE}")
+endif()
+message(STATUS " build shared libraries: ${BUILD_SHARED_LIBS}")
+message(STATUS " build static libraries: ${BUILD_STATIC_LIBS}")
+message(STATUS " build manual: ${BUILD_DOC}")
+message(STATUS " compiler warnings are errors: ${WERROR}")
+message(STATUS " system: ${CPACK_SYSTEM_NAME}")
+message(STATUS "")
+message(STATUS "*** Options Summary ***")
+foreach(PROP
+ COMPILE_OPTIONS INTERFACE_COMPILE_OPTIONS
+ COMPILE_DEFINITIONS INTERFACE_COMPILE_DEFINITIONS
+ INCLUDE_DIRECTORIES INTERFACE_INCLUDE_DIRECTORIES
+ INTERFACE_SYSTEM_INCLUDE_DIRECTORIES
+ LINK_OPTIONS INTERFACE_LINK_OPTIONS
+ LINK_LIBRARIES INTERFACE_LINK_LIBRARIES
+ LINK_DIRECTORIES INTERFACE_LINK_DIRECTORIES)
+ get_target_property(VAL libqpdf ${PROP})
+ if(NOT (VAL STREQUAL "VAL-NOTFOUND"))
+ message(STATUS " ${PROP}: ${VAL}")
+ endif()
+endforeach()
+if(APPLE)
+ message(STATUS " CMAKE_OSX_SYSROOT: ${CMAKE_OSX_SYSROOT}")
+endif()
+message(STATUS "")
+message(STATUS "See above for crypto summary.")
+message(STATUS "")
+if(NOT (MULTI_CONFIG OR CMAKE_BUILD_TYPE))
+ message(WARNING " CMAKE_BUILD_TYPE is not set; using default settings")
+endif()
diff --git a/cmake/qpdfCheckFlag.cmake b/cmake/qpdfCheckFlag.cmake
new file mode 100644
index 00000000..8bd72b88
--- /dev/null
+++ b/cmake/qpdfCheckFlag.cmake
@@ -0,0 +1,21 @@
+include(CheckCXXCompilerFlag)
+include(CheckCCompilerFlag)
+
+function(qpdf_maybe_add_flag lang flag var)
+ if(${lang} STREQUAL "C")
+ check_c_compiler_flag(${flag} ${var})
+ elseif(${lang} STREQUAL "CXX")
+ check_cxx_compiler_flag(${flag} ${var})
+ endif()
+ if(${var})
+ message(STATUS "Using ${flag}: YES")
+ if(${lang} STREQUAL "C")
+ # Add for C and C++
+ add_compile_options(${flag})
+ else()
+ add_compile_options($<$<COMPILE_LANGUAGE:CXX>:${flag}>)
+ endif()
+ else()
+ message(STATUS "Using ${flag}: NO")
+ endif()
+endfunction()
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
new file mode 100644
index 00000000..40ecd603
--- /dev/null
+++ b/examples/CMakeLists.txt
@@ -0,0 +1,52 @@
+set(EXAMPLE_CXX_PROGRAMS
+ pdf-attach-file
+ pdf-bookmarks
+ pdf-count-strings
+ pdf-create
+ pdf-custom-filter
+ pdf-double-page-size
+ pdf-filter-tokens
+ pdf-invert-images
+ pdf-mod-info
+ pdf-name-number-tree
+ pdf-npages
+ pdf-overlay-page
+ pdf-parse-content
+ pdf-set-form-values
+ pdf-split-pages
+ qpdf-job)
+set(EXAMPLE_C_PROGRAMS
+ pdf-c-objects
+ pdf-linearize
+ qpdfjob-c)
+
+foreach(PROG ${EXAMPLE_CXX_PROGRAMS})
+ add_executable(${PROG} ${PROG}.cc)
+ target_link_libraries(${PROG} libqpdf)
+endforeach()
+foreach(PROG ${EXAMPLE_C_PROGRAMS})
+ add_executable(${PROG} ${PROG}.c)
+ target_link_libraries(${PROG} libqpdf)
+ set_property(TARGET ${PROG} PROPERTY LINKER_LANGUAGE CXX)
+endforeach()
+target_include_directories(pdf-create PRIVATE ${JPEG_INCLUDE})
+
+add_test(
+ NAME examples
+ COMMAND ${RUN_QTEST}
+ --top ${qpdf_SOURCE_DIR}
+ --bin $<TARGET_FILE_DIR:pdf-create>
+ --bin $<TARGET_FILE_DIR:qpdf>
+ --bin $<TARGET_FILE_DIR:libqpdf> # for Windows to find DLL
+ --code ${qpdf_SOURCE_DIR}/examples
+ --color ${QTEST_COLOR}
+ --show-on-failure ${SHOW_FAILED_TEST_OUTPUT}
+ --tc "${qpdf_SOURCE_DIR}/examples/*.cc"
+ --tc "${qpdf_SOURCE_DIR}/examples/*.c")
+
+file(GLOB EXAMPLES_SRC "*.c" "*.cc")
+if(INSTALL_EXAMPLES)
+ install(FILES ${EXAMPLES_SRC}
+ DESTINATION ${CMAKE_INSTALL_DOCDIR}/examples
+ COMPONENT ${COMPONENT_EXAMPLES})
+endif()
diff --git a/fuzz/CMakeLists.txt b/fuzz/CMakeLists.txt
new file mode 100644
index 00000000..04743005
--- /dev/null
+++ b/fuzz/CMakeLists.txt
@@ -0,0 +1,178 @@
+# This directory contains support for Google's oss-fuzz project. See
+# https://github.com/google/oss-fuzz/tree/master/projects/qpdf
+
+set(FUZZERS
+ qpdf_fuzzer
+ ascii85_fuzzer
+ dct_fuzzer
+ flate_fuzzer
+ hex_fuzzer
+ lzw_fuzzer
+ pngpredictor_fuzzer
+ runlength_fuzzer
+ tiffpredictor_fuzzer)
+
+# The oss-fuzz project provides LIB_FUZZING_ENGINE and OUT environment
+# variables. For local testing, provide values if not set.
+set(LIB_FUZZING_ENGINE $ENV{LIB_FUZZING_ENGINE})
+if(NOT LIB_FUZZING_ENGINE)
+ # When running from oss-fuzz, LIB_FUZZING_ENGINE points to a
+ # static library that contains main.
+ add_library(standalone_fuzzer STATIC standalone_fuzz_target_runner.cc)
+ target_include_directories(
+ standalone_fuzzer PRIVATE ${qpdf_SOURCE_DIR}/include)
+ set(LIB_FUZZING_ENGINE standalone_fuzzer)
+endif()
+set(FUZZ_OUT $ENV{OUT})
+if(NOT FUZZ_OUT)
+ set(FUZZ_OUT ${CMAKE_CURRENT_BINARY_DIR}/fuzz-install)
+endif()
+
+if(OSS_FUZZ)
+ # We need to link jpeg and zlib statically for oss-fuzz. Construct
+ # our own object library without the external dependencies and add
+ # what we need.
+ add_library(libqpdf_fuzz STATIC $<TARGET_OBJECTS:libqpdf_object>)
+ target_link_libraries(libqpdf_fuzz INTERFACE libjpeg.a libz.a)
+ target_include_directories(libqpdf_fuzz
+ PUBLIC
+ ${JPEG_INCLUDE}
+ ${qpdf_SOURCE_DIR}/include
+ ${qpdf_SOURCE_DIR}/libqpdf)
+else()
+ add_library(libqpdf_fuzz ALIAS libqpdf_object)
+endif()
+
+foreach(PROG ${FUZZERS})
+ add_executable(${PROG} ${PROG}.cc)
+ target_link_libraries(${PROG} ${LIB_FUZZING_ENGINE})
+ target_link_libraries(${PROG} libqpdf_fuzz)
+endforeach()
+
+# Files from the test suite that are good for seeding the fuzzer.
+# Update count for qpdf in @fuzzers qtest/fuzz.test if you change this list.
+set(CORPUS_FROM_TEST
+ stream-data.pdf
+ lin5.pdf
+ field-types.pdf
+ image-streams-small.pdf
+ need-appearances.pdf
+ outlines-with-actions.pdf
+ outlines-with-old-root-dests.pdf
+ page-labels-and-outlines.pdf
+ page-labels-num-tree.pdf
+ dr-with-indirect-item.pdf
+ fuzz-16214.pdf
+ issue-99b.pdf
+ issue-99.pdf
+ issue-100.pdf
+ issue-101.pdf
+ issue-106.pdf
+ issue-117.pdf
+ issue-119.pdf
+ issue-120.pdf
+ issue-141a.pdf
+ issue-141b.pdf
+ issue-143.pdf
+ issue-146.pdf
+ issue-147.pdf
+ issue-148.pdf
+ issue-149.pdf
+ issue-150.pdf
+ issue-202.pdf
+ issue-263.pdf
+ issue-335a.pdf
+ issue-335b.pdf)
+
+# Any file that qpdf_fuzzer should be tested with can be named
+# something.fuzz and dropped into qpdf_extra. Update count for qpdf in
+# @fuzzers qtest/fuzz.test if you change this list.
+set(CORPUS_OTHER
+ 15316.fuzz
+ 15387.fuzz
+ 15390.fuzz
+ 15442.fuzz
+ 15445.fuzz
+ 15983.fuzz
+ 16172.fuzz
+ 16301.fuzz
+ 16953.fuzz
+ 18241.fuzz
+ 18247.fuzz
+ 23172.fuzz
+ 23599.fuzz
+ 23642.fuzz
+ 23642-mod.fuzz
+ 26761.fuzz
+ 26994.fuzz
+ 27393.fuzz
+ 28262.fuzz
+ 30507.fuzz
+ 37740.fuzz)
+
+set(CORPUS_DIR ${CMAKE_CURRENT_BINARY_DIR}/qpdf_corpus)
+file(MAKE_DIRECTORY ${CORPUS_DIR})
+function(copy_fuzz FROM)
+ file(SHA1 ${FROM} SHA)
+ set(OUT ${CORPUS_DIR}/${SHA})
+ add_custom_command(
+ OUTPUT ${OUT}
+ COMMAND ${COPY_COMMAND} $<SHELL_PATH:${FROM}> $<SHELL_PATH:${OUT}>)
+ set(CORPUS_FILE ${OUT} PARENT_SCOPE)
+endfunction()
+
+list(APPEND CORPUS_FILES)
+foreach(F ${CORPUS_FROM_TEST})
+ copy_fuzz(${qpdf_SOURCE_DIR}/qpdf/qtest/qpdf/${F})
+ list(APPEND CORPUS_FILES ${CORPUS_FILE})
+endforeach()
+foreach(F ${CORPUS_OTHER})
+ copy_fuzz(${CMAKE_CURRENT_SOURCE_DIR}/qpdf_extra/${F})
+ list(APPEND CORPUS_FILES ${CORPUS_FILE})
+endforeach()
+add_custom_target(qpdf_corpus ALL
+ DEPENDS ${CORPUS_FILES})
+
+add_test(
+ NAME fuzz
+ COMMAND ${RUN_QTEST}
+ --env QPDF_FUZZ_CORPUS=${CORPUS_DIR}
+ --top ${qpdf_SOURCE_DIR}
+ --bin $<TARGET_FILE_DIR:qpdf_fuzzer>
+ --bin $<TARGET_FILE_DIR:qpdf>
+ --code ${qpdf_SOURCE_DIR}/fuzz
+ --color ${QTEST_COLOR}
+ --show-on-failure ${SHOW_FAILED_TEST_OUTPUT})
+
+if(OSS_FUZZ)
+ list(APPEND SEED_CORPUS_ZIPS)
+ foreach(F ${FUZZERS})
+ if(F STREQUAL qpdf_fuzzer)
+ set(SEED_DIR ${CORPUS_DIR})
+ else()
+ set(SEED_DIR ${CMAKE_CURRENT_SOURCE_DIR}/${F}_seed_corpus)
+ endif()
+ set(SEED_ZIP ${CMAKE_CURRENT_BINARY_DIR}/${F}_seed_corpus.zip)
+ add_custom_command(OUTPUT ${SEED_ZIP}
+ COMMAND zip -q -r ${SEED_ZIP} .
+ WORKING_DIRECTORY ${SEED_DIR})
+ list(APPEND SEED_CORPUS_ZIPS ${SEED_ZIP})
+ endforeach()
+ add_custom_target(seed_corpus_zips ALL DEPENDS ${SEED_CORPUS_ZIPS})
+ add_dependencies(seed_corpus_zips qpdf_corpus)
+ add_custom_target(fuzzers)
+ add_dependencies(fuzzers ${FUZZERS} seed_corpus_zips)
+ install(
+ TARGETS ${FUZZERS}
+ DESTINATION ${FUZZ_OUT}
+ EXCLUDE_FROM_ALL
+ COMPONENT fuzz)
+ install(
+ FILES
+ ${CMAKE_CURRENT_SOURCE_DIR}/pdf.dict
+ ${CMAKE_CURRENT_SOURCE_DIR}/qpdf_fuzzer.options
+ ${SEED_CORPUS_ZIPS}
+ DESTINATION ${FUZZ_OUT}
+ EXCLUDE_FROM_ALL
+ COMPONENT fuzz)
+endif()
diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt
new file mode 100644
index 00000000..3110ba9c
--- /dev/null
+++ b/include/CMakeLists.txt
@@ -0,0 +1,12 @@
+set(qpdf_INCLUDE ${CMAKE_CURRENT_SOURCE_DIR} PARENT_SCOPE)
+
+# While globs are not considered best practice, it makes sense for
+# installation o header files. When compiling, you specify an entire
+# directory, so not doing the same at installation time creates a high
+# risk that forgetting to explicitly add a header to an installation
+# list would not be detected in CI or at any time until an end user
+# tries to build code.
+install(DIRECTORY qpdf
+ TYPE INCLUDE
+ COMPONENT ${COMPONENT_DEV}
+ FILES_MATCHING PATTERN "*.h" PATTERN "*.hh")
diff --git a/libqpdf/CMakeLists.txt b/libqpdf/CMakeLists.txt
new file mode 100644
index 00000000..2d8c7b65
--- /dev/null
+++ b/libqpdf/CMakeLists.txt
@@ -0,0 +1,581 @@
+
+if(GENERATE_AUTO_JOB)
+ execute_process(
+ COMMAND ${qpdf_SOURCE_DIR}/generate_auto_job --generate
+ WORKING_DIRECTORY ${qpdf_SOURCE_DIR})
+endif()
+
+set(libqpdf_crypto_native
+ AES_PDF_native.cc
+ MD5_native.cc
+ QPDFCrypto_native.cc
+ RC4_native.cc
+ SHA2_native.cc
+ rijndael.cc
+ sha2.c
+ sha2big.c)
+
+set(libqpdf_crypto_openssl
+ QPDFCrypto_openssl.cc)
+
+set(libqpdf_crypto_gnutls
+ QPDFCrypto_gnutls.cc)
+
+set(libqpdf_SOURCES
+ BitStream.cc
+ BitWriter.cc
+ Buffer.cc
+ BufferInputSource.cc
+ ClosedFileInputSource.cc
+ ContentNormalizer.cc
+ CryptoRandomDataProvider.cc
+ FileInputSource.cc
+ InputSource.cc
+ InsecureRandomDataProvider.cc
+ JSON.cc
+ JSONHandler.cc
+ MD5.cc
+ NNTree.cc
+ OffsetInputSource.cc
+ PDFVersion.cc
+ Pipeline.cc
+ Pl_AES_PDF.cc
+ Pl_ASCII85Decoder.cc
+ Pl_ASCIIHexDecoder.cc
+ Pl_Buffer.cc
+ Pl_Concatenate.cc
+ Pl_Count.cc
+ Pl_DCT.cc
+ Pl_Discard.cc
+ Pl_Flate.cc
+ Pl_LZWDecoder.cc
+ Pl_MD5.cc
+ Pl_PNGFilter.cc
+ Pl_QPDFTokenizer.cc
+ Pl_RC4.cc
+ Pl_RunLength.cc
+ Pl_SHA2.cc
+ Pl_StdioFile.cc
+ Pl_TIFFPredictor.cc
+ QPDF.cc
+ QPDFAcroFormDocumentHelper.cc
+ QPDFAnnotationObjectHelper.cc
+ QPDFArgParser.cc
+ QPDFCryptoProvider.cc
+ QPDFEFStreamObjectHelper.cc
+ QPDFEmbeddedFileDocumentHelper.cc
+ QPDFExc.cc
+ QPDFFileSpecObjectHelper.cc
+ QPDFFormFieldObjectHelper.cc
+ QPDFJob.cc
+ QPDFJob_argv.cc
+ QPDFJob_config.cc
+ QPDFJob_json.cc
+ QPDFMatrix.cc
+ QPDFNameTreeObjectHelper.cc
+ QPDFNumberTreeObjectHelper.cc
+ QPDFObjGen.cc
+ QPDFObject.cc
+ QPDFObjectHandle.cc
+ QPDFOutlineDocumentHelper.cc
+ QPDFOutlineObjectHelper.cc
+ QPDFPageDocumentHelper.cc
+ QPDFPageLabelDocumentHelper.cc
+ QPDFPageObjectHelper.cc
+ QPDFStreamFilter.cc
+ QPDFSystemError.cc
+ QPDFTokenizer.cc
+ QPDFUsage.cc
+ QPDFWriter.cc
+ QPDFXRefEntry.cc
+ QPDF_Array.cc
+ QPDF_Bool.cc
+ QPDF_Dictionary.cc
+ QPDF_InlineImage.cc
+ QPDF_Integer.cc
+ QPDF_Name.cc
+ QPDF_Null.cc
+ QPDF_Operator.cc
+ QPDF_Real.cc
+ QPDF_Reserved.cc
+ QPDF_Stream.cc
+ QPDF_String.cc
+ QPDF_encryption.cc
+ QPDF_linearization.cc
+ QPDF_optimization.cc
+ QPDF_pages.cc
+ QTC.cc
+ QUtil.cc
+ RC4.cc
+ ResourceFinder.cc
+ SecureRandomDataProvider.cc
+ SF_FlateLzwDecode.cc
+ SparseOHArray.cc
+ qpdf-c.cc
+ qpdfjob-c.cc)
+
+include(FindPkgConfig)
+include(CheckTypeSize)
+include(CheckIncludeFile)
+include(CheckCSourceCompiles)
+include(CheckCSourceRuns)
+include(CheckSymbolExists)
+
+set(dep_include_directories)
+set(dep_link_directories)
+set(dep_link_libraries)
+set(ANYTHING_MISSING 0)
+
+if(WIN32 AND (EXISTS ${qpdf_SOURCE_DIR}/external-libs))
+ set(EXTERNAL_LIBS 1)
+else()
+ set(EXTERNAL_LIBS 0)
+endif()
+
+if(EXTERNAL_LIBS)
+ set(EXTLIBDIR ${qpdf_SOURCE_DIR}/external-libs)
+ list(APPEND dep_include_directories ${EXTLIBDIR}/include)
+ set(JPEG_INCLUDE ${EXTLIBDIR}/include)
+ list(APPEND dep_link_libraries
+ z jpeg ssl crypto msvcrt ws2_32 shell32 advapi32 gdi32 user32 crypt32)
+ if (MSVC)
+ list(APPEND dep_link_directories ${EXTLIBDIR}/lib-msvc${WORDSIZE})
+ else()
+ list(APPEND dep_link_directories ${EXTLIBDIR}/lib-mingw${WORDSIZE})
+ endif()
+endif()
+
+if(NOT EXTERNAL_LIBS)
+ pkg_check_modules(pc_zlib zlib)
+ if(pc_zlib_FOUND)
+ list(APPEND dep_include_directories ${pc_zlib_INCLUDEDIR})
+ list(APPEND dep_link_directories ${pc_zlib_LIBDIR})
+ list(APPEND dep_link_libraries ${pc_zlib_LIBRARIES})
+ else()
+ find_path(ZLIB_H_PATH zlib.h)
+ find_library(ZLIB_LIB_PATH z zlib)
+ if(ZLIB_H_PATH AND ZLIB_LIB_PATH)
+ list(APPEND dep_include_directories ${ZLIB_H_PATH})
+ list(APPEND dep_link_libraries ${ZLIB_LIB_PATH})
+ else()
+ message(SEND_ERROR "zlib not found")
+ set(ANYTHING_MISSING 1)
+ endif()
+ endif()
+endif()
+
+if(NOT EXTERNAL_LIBS)
+ pkg_check_modules(pc_libjpeg libjpeg)
+ if(pc_libjpeg_FOUND)
+ list(APPEND dep_include_directories ${pc_libjpeg_INCLUDEDIR})
+ list(APPEND dep_link_directories ${pc_libjpeg_LIBDIR})
+ list(APPEND dep_link_libraries ${pc_libjpeg_LIBRARIES})
+ set(JPEG_INCLUDE ${pc_libjpeg_INCLUDEDIR})
+ else()
+ find_path(LIBJPEG_H_PATH jpeglib.h)
+ find_library(LIBJPEG_LIB_PATH jpeg)
+ if(LIBJPEG_H_PATH AND LIBJPEG_LIB_PATH)
+ list(APPEND dep_include_directories ${LIBJPEG_H_PATH})
+ list(APPEND dep_link_libraries ${LIBJPEG_LIB_PATH})
+ set(JPEG_INCLUDE ${LIBJPEG_H_PATH})
+ else()
+ message(SEND_ERROR "libjpeg not found")
+ set(ANYTHING_MISSING 1)
+ endif()
+ endif()
+endif()
+
+# Update JPEG_INCLUDE in PARENT_SCOPE after we have finished setting it.
+set(JPEG_INCLUDE ${JPEG_INCLUDE} PARENT_SCOPE)
+
+# Crypto provider selection. Prefer external crypto providers. If
+# implicit selection is allowed, use native only when no other options
+# are available or when explicitly requested. Allowing native as a
+# fallback can be disabled using the ALLOW_CRYPTO_NATIVE option.
+list(APPEND CRYPTO_PKG)
+
+set(USE_CRYPTO_GNUTLS OFF)
+set(USE_CRYPTO_OPENSSL OFF)
+set(USE_CRYPTO_NATIVE OFF)
+set(FOUND_CRYPTO OFF)
+
+if(USE_IMPLICIT_CRYPTO OR REQUIRE_CRYPTO_OPENSSL)
+ if(EXTERNAL_LIBS)
+ set(USE_CRYPTO_OPENSSL ON)
+ else()
+ pkg_check_modules(pc_openssl openssl>=1.1.0)
+ if(pc_openssl_FOUND)
+ set(USE_CRYPTO_OPENSSL ON)
+ set(FOUND_CRYPTO ON)
+ set(CRYPTO_PKG "${CRYPTO_PKG}, openssl>=1.1.0")
+ else()
+ find_path(OPENSSL_H_PATH openssl/evp.h)
+ find_library(OPENSSL_LIB_PATH crypto)
+ if(OPENSSL_H_PATH AND OPENSSL_LIB_PATH)
+ list(APPEND dep_include_directories ${OPENSSL_H_PATH})
+ list(APPEND dep_link_libraries ${OPENSSL_LIB_PATH})
+ set(USE_CRYPTO_OPENSSL ON)
+ set(FOUND_CRYPTO ON)
+ elseif(REQUIRE_CRYPTO_OPENSSL)
+ message(SEND_ERROR "openssl not found")
+ set(ANYTHING_MISSING 1)
+ endif()
+ endif()
+ endif()
+endif()
+if(USE_IMPLICIT_CRYPTO OR REQUIRE_CRYPTO_GNUTLS)
+ pkg_check_modules(pc_gnutls gnutls)
+ if(pc_gnutls_FOUND)
+ set(USE_CRYPTO_GNUTLS ON)
+ set(FOUND_CRYPTO ON)
+ set(CRYPTO_PKG "${CRYPTO_PKG}, gnutls")
+ else()
+ find_path(GNUTLS_H_PATH gnutls/gnutls.h)
+ find_library(GNUTLS_LIB_PATH gnutls)
+ if(GNUTLS_H_PATH AND GNUTLS_LIB_PATH)
+ list(APPEND dep_include_directories ${GNUTLS_H_PATH})
+ list(APPEND dep_link_libraries ${GNUTLS_LIB_PATH})
+ set(USE_CRYPTO_GNUTLS ON)
+ set(FOUND_CRYPTO ON)
+ elseif(REQUIRE_CRYPTO_GNUTLS)
+ message(SEND_ERROR "gnutls not found")
+ set(ANYTHING_MISSING 1)
+ endif()
+ endif()
+endif()
+if(REQUIRE_CRYPTO_NATIVE)
+ set(USE_CRYPTO_NATIVE ON)
+ set(FOUND_CRYPTO ON)
+elseif(USE_IMPLICIT_CRYPTO)
+ if(ALLOW_CRYPTO_NATIVE AND (NOT FOUND_CRYPTO))
+ set(USE_CRYPTO_NATIVE ON)
+ set(FOUND_CRYPTO ON)
+ endif()
+endif()
+if(FOUND_CRYPTO)
+ if(NOT DEFAULT_CRYPTO)
+ # The preferred order of crypto providers is documented in
+ # manual/installation.rst in the crypto.build section.
+ if(USE_CRYPTO_GNUTLS)
+ set(DEFAULT_CRYPTO "gnutls")
+ elseif(USE_CRYPTO_OPENSSL)
+ set(DEFAULT_CRYPTO "openssl")
+ else()
+ set(DEFAULT_CRYPTO "native")
+ endif()
+ endif()
+else()
+ message(SEND_ERROR "no crypto provider is available")
+ set(ANYTHING_MISSING 1)
+endif()
+if(ANYTHING_MISSING)
+ message(FATAL_ERROR "Missing dependencies; unable to continue")
+endif()
+
+message(STATUS "")
+message(STATUS "*** Crypto Summary ***")
+message(STATUS " GNU TLS crypto enabled: " ${USE_CRYPTO_GNUTLS})
+message(STATUS " OpenSSL crypto enabled: " ${USE_CRYPTO_OPENSSL})
+message(STATUS " Native crypto enabled: " ${USE_CRYPTO_NATIVE})
+message(STATUS " Default crypto: " ${DEFAULT_CRYPTO})
+message(STATUS "")
+
+if(USE_CRYPTO_OPENSSL)
+ list(APPEND libqpdf_SOURCES ${libqpdf_crypto_openssl})
+ if(NOT EXTERNAL_LIBS)
+ list(APPEND dep_include_directories ${pc_openssl_INCLUDEDIR})
+ list(APPEND dep_link_directories ${pc_openssl_LIBDIR})
+ list(APPEND dep_link_libraries ${pc_openssl_LIBRARIES})
+ endif()
+endif()
+if(USE_CRYPTO_GNUTLS)
+ list(APPEND libqpdf_SOURCES ${libqpdf_crypto_gnutls})
+ list(APPEND dep_include_directories ${pc_gnutls_INCLUDEDIR})
+ list(APPEND dep_link_directories ${pc_gnutls_LIBDIR})
+ list(APPEND dep_link_libraries ${pc_gnutls_LIBRARIES})
+endif()
+if(USE_CRYPTO_NATIVE)
+ list(APPEND libqpdf_SOURCES ${libqpdf_crypto_native})
+endif()
+
+if(APPLE)
+ # 2022: in CI (GitHub actions), pkg-config for zlib was adding a
+ # broken directory to the include path. This effectively filters it
+ # out.
+ list(FILTER dep_include_directories EXCLUDE REGEX "^/Library/")
+endif()
+
+list(REMOVE_DUPLICATES dep_include_directories)
+list(REMOVE_DUPLICATES dep_link_directories)
+list(REMOVE_DUPLICATES dep_link_libraries)
+
+check_type_size(size_t SIZEOF_SIZE_T)
+check_include_file("inttypes.h" HAVE_INTTYPES_H)
+check_symbol_exists(fseeko "stdio.h" HAVE_FSEEKO)
+check_symbol_exists(fseeko64 "stdio.h" HAVE_FSEEKO64)
+check_symbol_exists(localtime_r "time.h" HAVE_LOCALTIME_R)
+check_symbol_exists(random "stdlib.h" HAVE_RANDOM)
+find_file(RANDOM_DEVICE
+ "urandom" "arandom" "arandom" PATHS "/dev" NO_DEFAULT_PATH)
+
+check_c_source_compiles(
+"#include <time.h>
+#include <stdio.h>
+int main(int argc, char* argv[]) {
+ tzset();
+ printf(\"%ld\", timezone);
+ return 0;
+}"
+ HAVE_EXTERN_LONG_TIMEZONE)
+
+check_c_source_compiles(
+"#include <time.h>
+int main(int argc, char* argv[]) {
+ struct tm tm;
+ tm.tm_gmtoff = 1;
+ return 0;
+}"
+ HAVE_EXTERN_TM_GMTOFF)
+
+check_c_source_compiles(
+"#include <stdio.h>
+#include <sys/types.h>
+int main(int argc, char* argv[]) {
+ int a[sizeof(off_t) >= 8 ? 1 : -1];
+}"
+ LFS_WITHOUT_MACROS)
+
+check_c_source_compiles(
+"#define _FILE_OFFSET_BITS 64
+#include <stdio.h>
+#include <sys/types.h>
+int main(int argc, char* argv[]) {
+ int a[sizeof(off_t) >= 8 ? 1 : -1];
+}"
+ LFS_WITH_MACROS)
+if(LFS_WITH_MACROS AND NOT LFS_WITHOUT_MACROS)
+ set(_FILE_OFFSET_BITS 64)
+endif()
+
+function(qpdf_check_ll_fmt fmt var)
+ if(NOT DEFINED LL_FMT)
+ check_c_source_runs(
+ "#define _CRT_SECURE_NO_WARNINGS
+#include <stdio.h>
+#include <string.h>
+int main(int argc, char* argv[]) {
+ long long int a = 123456789012345ll;
+ char s[30];
+ sprintf(s, \"${fmt}\", a);
+ return (strcmp(s, \"123456789012345\") == 0) ? 0 : 1;
+}" ${var})
+ if(${var})
+ set(LL_FMT "${fmt}" PARENT_SCOPE)
+ endif()
+ endif()
+endfunction()
+
+qpdf_check_ll_fmt("%lld" fmt_lld)
+qpdf_check_ll_fmt("%I64d" fmt_i64d)
+qpdf_check_ll_fmt("%I64lld" fmt_i64lld)
+
+configure_file(
+ "${CMAKE_CURRENT_SOURCE_DIR}/qpdf/qpdf-config.h.in"
+ "${CMAKE_CURRENT_BINARY_DIR}/qpdf/qpdf-config.h"
+ NEWLINE_STYLE UNIX)
+
+if(NOT BUILD_STATIC_LIBS)
+ set(OBJECT_LIB_IS_PIC ON)
+else()
+ set(OBJECT_LIB_IS_PIC OFF)
+endif()
+
+# Build an "object library" for use in libtests so we don't have to
+# export symbols that are not officially part of the public API. If we
+# are building static libraries, the object library won't use
+# position-independent code and will provided objects for the static
+# library. If we are only building the shared library, go ahead and
+# use PIC for the object library so we don't have to compile twice.
+set(OBJECT_LIB libqpdf_object)
+add_library(${OBJECT_LIB} OBJECT ${libqpdf_SOURCES})
+set_target_properties(${OBJECT_LIB} PROPERTIES
+ POSITION_INDEPENDENT_CODE ${OBJECT_LIB_IS_PIC})
+target_include_directories(${OBJECT_LIB}
+ SYSTEM PRIVATE ${dep_include_directories})
+target_include_directories(${OBJECT_LIB}
+ PUBLIC
+ ${JPEG_INCLUDE}
+ ${qpdf_INCLUDE}
+ ${qpdf_SOURCE_DIR}/libqpdf
+ ${CMAKE_CURRENT_BINARY_DIR})
+target_link_directories(${OBJECT_LIB} INTERFACE ${dep_link_directories})
+target_link_libraries(${OBJECT_LIB} INTERFACE ${dep_link_libraries})
+
+set(LD_VERSION_FLAGS "")
+function(ld_version_script)
+ # Check if the linker supports linker scripts, and use if it does.
+ # This functionality is currently constrained to compilers using GNU
+ # ld on ELF systems or systems that emulation this behavior.
+ set(ld_script
+ "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/qpdf-tmp/conftest.map")
+ file(WRITE ${ld_script}
+"VERS_1 {
+ global: sym;
+};
+
+VERS_2 {
+ global: sym;
+} VERS_1;
+")
+ set(CMAKE_REQUIRED_LINK_OPTIONS -Wl,--version-script=${ld_script})
+ check_c_source_compiles("int main() { return 0; }" HAVE_LD_SCRIPT)
+ if(HAVE_LD_SCRIPT)
+ set(LD_VERSION_FLAGS
+ -Wl,--version-script=${CMAKE_CURRENT_BINARY_DIR}/libqpdf.map PARENT_SCOPE)
+ configure_file(
+ "${qpdf_SOURCE_DIR}/libqpdf.map.in"
+ "${CMAKE_CURRENT_BINARY_DIR}/libqpdf.map"
+ NEWLINE_STYLE UNIX)
+ endif()
+endfunction()
+if(NOT WIN32)
+ ld_version_script()
+endif()
+
+if(BUILD_SHARED_LIBS)
+ add_compile_definitions(DLL_EXPORT)
+
+ set(SHARED_LIB libqpdf)
+ if(OBJECT_LIB_IS_PIC)
+ add_library(${SHARED_LIB} SHARED $<TARGET_OBJECTS:libqpdf_object>)
+ else()
+ add_library(${SHARED_LIB} SHARED ${libqpdf_SOURCES})
+ endif()
+ if(WIN32)
+ # Goal: the DLL import library should be libqpdf.a or qpdf.lib so
+ # that linking with -lqpdf gets you a shared library link on all
+ # platforms. The DLL should be qpdf${SONAME}.dll rather than just
+ # qpdf.dll. qpdf has always done this, and it gives us some
+ # protection against binary incompatible DLLs being installed.
+ set(SHARED_OUT qpdf${qpdf_SOVERSION}) # Put API version number in DLL
+ if(MINGW)
+ # Reference: Platform/Windows-GNU.cmake in the cmake installation
+ set(CMAKE_SHARED_LIBRARY_PREFIX "") # libqpdf$v.dll -> qpdf$v.dll
+ set(CMAKE_IMPORT_LIBRARY_SUFFIX ".a") # libqpdf.dll.a -> libqpdf.a
+ endif()
+ if(MSVC)
+ # Avoid linker warning from mixing libraries built with /MT and /MD.
+ set_target_properties(${SHARED_LIB}
+ PROPERTIES LINK_FLAGS "/NODEFAULTLIB:LIBCMT /NODEFAULTLIB:LIBCMTD")
+ endif()
+ else()
+ set(SHARED_OUT qpdf)
+ endif()
+ # Setting OUTPUT_NAME and ARCHIVE_OUTPUT_NAME separate enables us to
+ # have a versioned DLL and an unversioned import library, which
+ # gives us semantics similar to ELF shared libraries and makes
+ # linking against qpdf the same across all platforms.
+ set_target_properties(${SHARED_LIB} PROPERTIES
+ OUTPUT_NAME ${SHARED_OUT}
+ ARCHIVE_OUTPUT_NAME qpdf
+ VERSION ${qpdf_LIBVERSION}
+ SOVERSION ${qpdf_SOVERSION}
+ POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS})
+
+ target_include_directories(${SHARED_LIB}
+ SYSTEM PRIVATE ${dep_include_directories})
+ target_include_directories(${SHARED_LIB}
+ PUBLIC
+ $<BUILD_INTERFACE:${qpdf_INCLUDE}>
+ $<INSTALL_INTERFACE:include>)
+ target_link_directories(${SHARED_LIB} PRIVATE ${dep_link_directories})
+ target_link_libraries(${SHARED_LIB} PRIVATE ${dep_link_libraries})
+ if(LD_VERSION_FLAGS)
+ target_link_options(${SHARED_LIB} PRIVATE ${LD_VERSION_FLAGS})
+ endif()
+
+ target_include_directories(${SHARED_LIB}
+ PRIVATE ${qpdf_SOURCE_DIR}/libqpdf ${CMAKE_CURRENT_BINARY_DIR})
+
+ install(TARGETS ${SHARED_LIB}
+ EXPORT libqpdfTargets
+ TYPE LIBRARY
+ COMPONENT ${COMPONENT_LIB}
+ NAMELINK_COMPONENT ${COMPONENT_DEV}
+ INCLUDES ${qpdf_INCLUDE})
+endif()
+
+if(BUILD_STATIC_LIBS)
+ if(BUILD_SHARED_LIBS)
+ set(STATIC_LIB libqpdf_static)
+ else()
+ set(STATIC_LIB libqpdf)
+ endif()
+ if(OBJECT_LIB_IS_PIC)
+ add_library(${STATIC_LIB} STATIC ${libqpdf_SOURCES})
+ else()
+ add_library(${STATIC_LIB} STATIC $<TARGET_OBJECTS:libqpdf_object>)
+ endif()
+
+ target_include_directories(${STATIC_LIB}
+ SYSTEM PRIVATE ${dep_include_directories})
+ target_include_directories(${STATIC_LIB}
+ PUBLIC
+ $<BUILD_INTERFACE:${qpdf_INCLUDE}>
+ $<INSTALL_INTERFACE:include>)
+ target_link_directories(${STATIC_LIB}
+ INTERFACE $<BUILD_INTERFACE:${dep_link_directories}>
+ PRIVATE $<INSTALL_INTERFACE:${dep_link_directories}>)
+ target_link_libraries(${STATIC_LIB} INTERFACE ${dep_link_libraries})
+
+ # Avoid name clashes on Windows with the the DLL import library.
+ if(NOT DEFINED STATIC_SUFFIX AND BUILD_SHARED_LIBS)
+ if (WIN32)
+ set(STATIC_SUFFIX "_static")
+ else()
+ set(STATIC_SUFFIX "")
+ endif()
+ endif()
+
+ set_target_properties(${STATIC_LIB} PROPERTIES
+ OUTPUT_NAME qpdf${STATIC_SUFFIX}
+ VERSION ${PROJECT_VERSION})
+ target_include_directories(${STATIC_LIB}
+ PRIVATE ${qpdf_SOURCE_DIR}/libqpdf ${CMAKE_CURRENT_BINARY_DIR})
+
+ install(TARGETS ${STATIC_LIB}
+ EXPORT libqpdfTargets
+ TYPE ARCHIVE
+ COMPONENT ${COMPONENT_DEV}
+ INCLUDES ${qpdf_INCLUDE})
+endif()
+
+configure_file(
+ "${qpdf_SOURCE_DIR}/libqpdf.pc.in"
+ "${CMAKE_CURRENT_BINARY_DIR}/libqpdf.pc"
+ @ONLY NEWLINE_STYLE UNIX)
+if(INSTALL_PKGCONFIG)
+ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libqpdf.pc
+ DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig
+ COMPONENT ${COMPONENT_DEV})
+endif()
+
+if(INSTALL_CMAKE_PACKAGE)
+ include(CMakePackageConfigHelpers)
+ configure_package_config_file(
+ ${qpdf_SOURCE_DIR}/qpdfConfig.cmake.in
+ ${CMAKE_CURRENT_BINARY_DIR}/qpdfConfig.cmake
+ INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/qpdf)
+ write_basic_package_version_file(
+ "${CMAKE_CURRENT_BINARY_DIR}/qpdfConfigVersion.cmake"
+ VERSION ${PROJECT_VERSION}
+ COMPATIBILITY SameMajorVersion)
+ install(EXPORT libqpdfTargets
+ NAMESPACE qpdf::
+ FILE libqpdfTargets.cmake
+ DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/qpdf)
+ install(FILES
+ ${CMAKE_CURRENT_BINARY_DIR}/qpdfConfigVersion.cmake
+ ${CMAKE_CURRENT_BINARY_DIR}/qpdfConfig.cmake
+ DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/qpdf)
+endif()
diff --git a/libtests/CMakeLists.txt b/libtests/CMakeLists.txt
new file mode 100644
index 00000000..96f93482
--- /dev/null
+++ b/libtests/CMakeLists.txt
@@ -0,0 +1,53 @@
+set(TEST_PROGRAMS
+ cxx11
+ aes
+ arg_parser
+ ascii85
+ bits
+ buffer
+ closed_file_input_source
+ concatenate
+ dct_compress
+ dct_uncompress
+ flate
+ hex
+ input_source
+ json
+ json_handler
+ json_parse
+ lzw
+ main_from_wmain
+ matrix
+ md5
+ nntree
+ numrange
+ pdf_version
+ pointer_holder
+ predictors
+ qintc
+ qutil
+ random
+ rc4
+ runlength
+ sha2
+ sparse_array)
+foreach(PROG ${TEST_PROGRAMS})
+ add_executable(${PROG} ${PROG}.cc)
+ target_link_libraries(${PROG} libqpdf_object)
+endforeach()
+
+# Since libtests link with the object library and don't use the DLL,
+# we don't need to (and shouldn't) add the libqpdf target directory to
+# the path for libtests.
+add_test(
+ NAME libtests
+ COMMAND ${RUN_QTEST}
+ --top ${qpdf_SOURCE_DIR}
+ --bin $<TARGET_FILE_DIR:qutil>
+ --bin $<TARGET_FILE_DIR:qpdf>
+ --code ${qpdf_SOURCE_DIR}/libtests
+ --color ${QTEST_COLOR}
+ --show-on-failure ${SHOW_FAILED_TEST_OUTPUT}
+ --tc "${qpdf_SOURCE_DIR}/libtests/*.cc"
+ --tc "${qpdf_SOURCE_DIR}/libqpdf/*.cc"
+ --tc "${qpdf_SOURCE_DIR}/libqpdf/qpdf/bits_functions.hh")
diff --git a/manual/CMakeLists.txt b/manual/CMakeLists.txt
new file mode 100644
index 00000000..737d2335
--- /dev/null
+++ b/manual/CMakeLists.txt
@@ -0,0 +1,133 @@
+# There is a FindLATEX module, but it does some things we don't care
+# about and doesn't do everything we need.
+find_program(LATEX latex)
+find_program(PDFLATEX pdflatex)
+find_program(LATEXMK latexmk)
+find_program(SPHINX sphinx-build)
+
+# *** NOTE: Never check BUILD_DOC_PDF if BUILD_DOC is not set. See
+# *** comments in top-level CMakeLists.txt.
+
+if(BUILD_DOC)
+ if(SPHINX STREQUAL SPHINX-NOTFOUND)
+ message(FATAL_ERROR "sphinx-build is required for building documentation")
+ endif()
+ if(BUILD_DOC_PDF AND
+ ((LATEX STREQUAL LATEX-NOTFOUND) OR
+ (LATEXMK STREQUAL LATEXMK-NOTFOUND) OR
+ (PDFLATEX STREQUAL PDFLATEX-NOTFOUND)))
+ message(FATAL_ERROR
+ "latex, latexmk, and pdflatex are required to build PDF documentation")
+ endif()
+endif()
+
+set(MANUAL_SRC ${qpdf_SOURCE_DIR}/manual)
+foreach(F qpdf.1 fix-qdf.1 zlib-flate.1)
+ configure_file(
+ ${MANUAL_SRC}/${F}.in
+ ${CMAKE_CURRENT_BINARY_DIR}/${F}
+ NEWLINE_STYLE UNIX)
+endforeach()
+
+SET(MANUAL_DEPS
+ conf.py
+ _ext/qpdf.py
+ acknowledgement.rst
+ cli.rst
+ design.rst
+ download.rst
+ encryption.rst
+ index.rst
+ installation.rst
+ json.rst
+ library.rst
+ license.rst
+ linearization.rst
+ object-streams.rst
+ overview.rst
+ packaging.rst
+ qdf.rst
+ qpdf-job.rst
+ release-notes.rst
+ weak-crypto.rst)
+
+# Prevent targets that run ${SPHINX} from running in parallel by using
+# dependencies. This avoids clashes in temporary files that cause the
+# build to fail with the error "_pickle.UnpicklingError: pickle data
+# was truncated". It would be better if we could use order-only
+# dependencies like gnu make to make it possible to build the targets
+# independently.
+set(DOC_HTML_OUTPUT html/index.html)
+set(DOC_SINGLEHTML_OUTPUT singlehtml/index.html)
+set(DOC_PDF_OUTPUT latex/qpdf.pdf)
+if(BUILD_DOC)
+ add_custom_command(OUTPUT ${DOC_HTML_OUTPUT}
+ COMMAND ${SPHINX} -M html ${MANUAL_SRC} ${CMAKE_CURRENT_BINARY_DIR}
+ COMMAND touch ${DOC_HTML_OUTPUT}
+ DEPENDS ${MANUAL_DEPS})
+ add_custom_command(OUTPUT ${DOC_SINGLEHTML_OUTPUT}
+ COMMAND ${SPHINX} -M singlehtml ${MANUAL_SRC} ${CMAKE_CURRENT_BINARY_DIR}
+ COMMAND touch ${DOC_SINGLEHTML_OUTPUT}
+ DEPENDS ${MANUAL_DEPS})
+ add_custom_command(OUTPUT ${DOC_PDF_OUTPUT}
+ COMMAND ${SPHINX} -M latexpdf ${MANUAL_SRC} ${CMAKE_CURRENT_BINARY_DIR}
+ COMMAND touch ${DOC_PDF_OUTPUT}
+ DEPENDS ${MANUAL_DEPS})
+ add_custom_target(doc_html DEPENDS ${DOC_HTML_OUTPUT})
+ add_custom_target(doc_singlehtml DEPENDS ${DOC_SINGLEHTML_OUTPUT})
+ add_dependencies(doc_singlehtml doc_html)
+ add_custom_target(doc_pdf DEPENDS ${DOC_PDF_OUTPUT})
+ add_custom_target(doc ALL)
+ if(BUILD_DOC_PDF)
+ add_dependencies(doc doc_pdf)
+ if(BUILD_DOC_HTML)
+ add_dependencies(doc_pdf doc_singlehtml)
+ endif()
+ elseif(BUILD_DOC_HTML)
+ add_dependencies(doc doc_singlehtml)
+ endif()
+
+ if(BUILD_DOC_DIST)
+ set(DOC_DIST_HTML doc-dist/manual-html)
+ set(DOC_DIST_SINGLEHTML doc-dist/manual-single-page-html)
+ set(DOC_DIST_PDF doc-dist/qpdf-manual.pdf)
+ add_custom_command(
+ OUTPUT
+ ${DOC_DIST_HTML}
+ ${DOC_DIST_SINGLEHTML}
+ ${DOC_DIST_PDF}
+ COMMAND rm -rf doc-dist
+ COMMAND mkdir -p doc-dist
+ COMMAND cp -r html ${DOC_DIST_HTML}
+ COMMAND cp -r singlehtml ${DOC_DIST_SINGLEHTML}
+ COMMAND cp -r ${DOC_PDF_OUTPUT} ${DOC_DIST_PDF}
+ DEPENDS ${DOC_HTML_OUTPUT} ${DOC_SINGLEHTML_OUTPUT} ${DOC_PDF_OUTPUT})
+ add_custom_target(doc_dist ALL
+ DEPENDS ${DOC_DIST_HTML} ${DOC_DIST_SINGLEHTML} ${DOC_DIST_PDF})
+ add_dependencies(doc_dist doc)
+ endif()
+endif()
+
+# INSTALL_MANUAL is not dependent on building doc -- we sometimes drop in
+# pre-built doc when creating distributions.
+if(INSTALL_MANUAL)
+ install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doc-dist/
+ TYPE DOC
+ COMPONENT ${COMPONENT_DOC})
+else()
+ install(FILES ${qpdf_SOURCE_DIR}/README-doc.txt
+ TYPE DOC
+ COMPONENT ${COMPONENT_DOC})
+endif()
+
+if(NOT WIN32)
+ # There's no reason to install manual pages in a Windows
+ # environment, especially when all they do is refer people to the
+ # manual.
+ install(FILES
+ ${CMAKE_CURRENT_BINARY_DIR}/qpdf.1
+ ${CMAKE_CURRENT_BINARY_DIR}/fix-qdf.1
+ ${CMAKE_CURRENT_BINARY_DIR}/zlib-flate.1
+ DESTINATION ${CMAKE_INSTALL_MANDIR}/man1
+ COMPONENT ${COMPONENT_DOC})
+endif()
diff --git a/qpdf/CMakeLists.txt b/qpdf/CMakeLists.txt
new file mode 100644
index 00000000..300174ce
--- /dev/null
+++ b/qpdf/CMakeLists.txt
@@ -0,0 +1,78 @@
+set(MAIN_CXX_PROGRAMS
+ qpdf
+ fix-qdf
+ pdf_from_scratch
+ test_driver
+ test_large_file
+ test_parsedoffset
+ test_pdf_doc_encoding
+ test_pdf_unicode
+ test_renumber
+ test_shell_glob
+ test_tokenizer
+ test_unicode_filenames
+ test_xref)
+set(MAIN_C_PROGRAMS
+ qpdf-ctest
+ qpdfjob-ctest)
+
+foreach(PROG ${MAIN_CXX_PROGRAMS})
+ add_executable(${PROG} ${PROG}.cc)
+ target_link_libraries(${PROG} libqpdf)
+endforeach()
+foreach(PROG ${MAIN_C_PROGRAMS})
+ add_executable(${PROG} ${PROG}.c)
+ target_link_libraries(${PROG} libqpdf)
+ set_property(TARGET ${PROG} PROPERTY LINKER_LANGUAGE CXX)
+endforeach()
+target_include_directories(qpdf-ctest PRIVATE ${CMAKE_BINARY_DIR}/libqpdf)
+
+foreach(B qpdf test_unicode_filenames fix-qdf test_shell_glob)
+ if(WINDOWS_WMAIN_COMPILE)
+ target_compile_options(${B} PRIVATE ${WINDOWS_WMAIN_COMPILE})
+ endif()
+ if(WINDOWS_WMAIN_LINK)
+ target_link_options(${B} PRIVATE ${WINDOWS_WMAIN_LINK})
+ endif()
+endforeach()
+
+add_test(
+ NAME qpdf
+ COMMAND ${RUN_QTEST}
+ --top ${qpdf_SOURCE_DIR}
+ --bin $<TARGET_FILE_DIR:qpdf>
+ --bin $<TARGET_FILE_DIR:libqpdf> # for Windows to find DLL
+ --code ${qpdf_SOURCE_DIR}/qpdf
+ --color ${QTEST_COLOR}
+ --show-on-failure ${SHOW_FAILED_TEST_OUTPUT}
+ --tc "${qpdf_SOURCE_DIR}/qpdf/*.cc"
+ --tc "${qpdf_SOURCE_DIR}/qpdf/*.c"
+ --tc "${qpdf_SOURCE_DIR}/libqpdf/*.cc")
+
+install(TARGETS qpdf fix-qdf
+ TYPE RUNTIME
+ COMPONENT ${COMPONENT_CLI})
+
+if(MINGW)
+ # For MSVC, including InstallRequiredSystemLibraries in the
+ # top-level CMakeLists.txt is sufficient. For mingw, we have to copy
+ # mingw libraries in ourselves.
+ set(ONE_GNU_DLL extra-dlls/libstdc++-6.dll)
+ add_custom_command(OUTPUT ${ONE_GNU_DLL}
+ COMMAND
+ perl ${qpdf_SOURCE_DIR}/copy_dlls
+ qpdf.exe
+ ${CMAKE_BINARY_DIR}/libqpdf
+ extra-dlls)
+ add_custom_target(extra_dlls ALL DEPENDS ${ONE_GNU_DLL})
+ add_dependencies(extra_dlls qpdf)
+ if(BUILD_SHARED_LIBS)
+ set(EXTRA_DLL_COMPONENT ${COMPONENT_LIB})
+ else()
+ set(EXTRA_DLL_COMPONENT ${COMPONENT_CLI})
+ endif()
+ # The trailing / prevents "extra-dlls" from being created in bin.
+ install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/extra-dlls/
+ TYPE BIN
+ COMPONENT ${EXTRA_DLL_COMPONENT})
+endif()
diff --git a/qpdfConfig.cmake.in b/qpdfConfig.cmake.in
new file mode 100644
index 00000000..95a79286
--- /dev/null
+++ b/qpdfConfig.cmake.in
@@ -0,0 +1,2 @@
+@PACKAGE_INIT@
+include("${CMAKE_CURRENT_LIST_DIR}/libqpdfTargets.cmake")
diff --git a/zlib-flate/CMakeLists.txt b/zlib-flate/CMakeLists.txt
new file mode 100644
index 00000000..4db31ef4
--- /dev/null
+++ b/zlib-flate/CMakeLists.txt
@@ -0,0 +1,16 @@
+add_executable(zlib-flate zlib-flate.cc)
+target_link_libraries(zlib-flate libqpdf)
+
+add_test(
+ NAME zlib-flate
+ COMMAND ${RUN_QTEST}
+ --top ${qpdf_SOURCE_DIR}
+ --bin $<TARGET_FILE_DIR:zlib-flate>
+ --bin $<TARGET_FILE_DIR:libqpdf> # for Windows to find DLL
+ --code ${qpdf_SOURCE_DIR}/zlib-flate
+ --color ${QTEST_COLOR}
+ --show-on-failure ${SHOW_FAILED_TEST_OUTPUT})
+
+install(TARGETS zlib-flate
+ TYPE RUNTIME
+ COMPONENT ${COMPONENT_CLI})