# pdfengine — the C++23 PDF SDK core.
#

# Generate the version header from the project version.
configure_file(
    "${CMAKE_CURRENT_SOURCE_DIR}/include/pdfengine/version.hpp.in"
    "${CMAKE_CURRENT_BINARY_DIR}/generated/pdfengine/version.hpp"
    @ONLY)

find_package(PNG REQUIRED)

add_library(pdfengine STATIC
    src/core/engine_info.cpp
    src/core/graphics_state.cpp
    src/core/display_list.cpp
    src/core/skia_renderer.cpp
    src/parser/pdfium_loader.cpp
    src/parser/pdfium_document.cpp
    src/parser/content_stream_parser.cpp
    src/parser/decoration_builder.cpp
    src/text/selection.cpp
    src/fonts/face/font_face.cpp
    src/fonts/face/free_type_manager.cpp
    src/fonts/loader/font_resolver.cpp
    src/fonts/pdf_fonts/font_loader.cpp
    src/fonts/shaping/hb_shaper.cpp
    src/fonts/cache/glyph_bitmap.cpp
    src/fonts/cache/glyph_cache.cpp
    src/fonts/pdf_fonts/types/truetype_font.cpp
    src/fonts/pdf_fonts/types/type1_font.cpp
    src/fonts/pdf_fonts/types/cid_font.cpp
    src/fonts/pdf_fonts/font_loader.cpp
    src/fonts/pdf_fonts/font_descriptor.cpp
    src/fonts/pdf_fonts/font_fallback.cpp
    src/fonts/pdf_fonts/font_subset.cpp
    src/fonts/pdf_fonts/encoding/encoding.cpp
    src/fonts/pdf_fonts/encoding/tounicode_parser.cpp
    src/fonts/pdf_fonts/encoding/cjk_collection_db.cpp
)
add_library(pdfengine::pdfengine ALIAS pdfengine)

target_include_directories(pdfengine
    PUBLIC
        "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
        "$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/generated>"
    PRIVATE
        "${CMAKE_CURRENT_SOURCE_DIR}/src"
)

target_link_libraries(pdfengine
    PUBLIC
        spdlog::spdlog
    PRIVATE
        freetype
        harfbuzz::harfbuzz
        harfbuzz::harfbuzz-subset
        PNG::PNG
        nlohmann_json::nlohmann_json
)

if(PDFENGINE_WITH_PDFIUM)
    target_link_libraries(pdfengine PRIVATE pdfium::pdfium)
    target_compile_definitions(pdfengine PRIVATE PDFENGINE_WITH_PDFIUM)
endif()

# Directory of the bundled fallback fonts (Carlito/Tinos). Under WASM they're embedded into the
# module's in-memory FS at /fonts (see wasm/CMakeLists.txt --embed-file); natively the engine reads
# them straight from the source assets dir. font_fallback.cpp prefers these over OS fonts.
if(EMSCRIPTEN)
    target_compile_definitions(pdfengine PRIVATE PDFENGINE_FONT_DIR="/fonts")
else()
    target_compile_definitions(pdfengine PRIVATE PDFENGINE_FONT_DIR="${CMAKE_SOURCE_DIR}/engine/assets/fonts")
endif()

if(PDFENGINE_WITH_SKIA)
    target_link_libraries(pdfengine PRIVATE skia::skia)
    target_compile_definitions(pdfengine PUBLIC PDFENGINE_WITH_SKIA)
endif()

if(PDFENGINE_WITH_QPDF)
    find_package(ZLIB REQUIRED)
    find_package(JPEG REQUIRED)
    if(NOT TARGET zs)
        add_library(zs ALIAS ZLIB::ZLIB)
    endif()
    if(NOT TARGET jpeg)
        add_library(jpeg ALIAS JPEG::JPEG)
    endif()
    target_sources(pdfengine PRIVATE
        src/qpdf/qpdf_extractor.cpp
        src/qpdf/qpdf_writer.cpp
        src/qpdf/qpdf_resource_resolver.cpp
        src/core/image_decoder.cpp
        src/parser/lexer.cpp
        src/parser/parser.cpp
        src/parser/content_builder.cpp
        src/serializer/content_serializer.cpp
        src/serializer/ast_serializer.cpp
    )
    target_link_libraries(pdfengine PRIVATE qpdf::libqpdf ZLIB::ZLIB JPEG::JPEG)
    target_compile_definitions(pdfengine PUBLIC PDFENGINE_WITH_QPDF)
endif()

pdfengine_set_warnings(pdfengine)
pdfengine_enable_sanitizers(pdfengine)

if(PDFENGINE_BUILD_TESTS)
    add_subdirectory(tests)
endif()

# --- Fuzzing -----------------------------------------------------------------
# Coverage-instrument the engine and build the libFuzzer harness. Requires Clang
# (enforced at the top level). PDFENGINE_FUZZ_SANITIZERS lets callers drop ASan
# when the PDFium static lib isn't ASan-compatible (coverage-only fuzzing).
if(PDFENGINE_FUZZING)
    set(PDFENGINE_FUZZ_SANITIZERS "fuzzer,address,undefined" CACHE STRING
        "Sanitizer set for fuzzing (e.g. 'fuzzer,address,undefined' or just 'fuzzer')")

    # Split the set into the libFuzzer driver ('fuzzer', linked only into the
    # harness exe) and the runtime sanitizers (address/undefined/...), which must
    # instrument the engine library itself to catch bugs in its code.
    string(REPLACE "," ";" _fuzz_sans "${PDFENGINE_FUZZ_SANITIZERS}")
    set(_fuzz_runtime_sans "")
    foreach(_s IN LISTS _fuzz_sans)
        if(NOT _s STREQUAL "fuzzer")
            list(APPEND _fuzz_runtime_sans "${_s}")
        endif()
    endforeach()
    list(JOIN _fuzz_runtime_sans "," _fuzz_runtime_str)

    # Coverage-instrument the engine; apply ASan/UBSan to it too so its own code
    # is checked, not just the harness.
    target_compile_options(pdfengine PRIVATE -fsanitize=fuzzer-no-link -fno-omit-frame-pointer)
    if(_fuzz_runtime_str)
        target_compile_options(pdfengine PRIVATE -fsanitize=${_fuzz_runtime_str})
        target_link_options(pdfengine PUBLIC -fsanitize=${_fuzz_runtime_str})
    endif()

    add_executable(pdfengine_fuzz fuzz/fuzz_load.cpp)
    target_link_libraries(pdfengine_fuzz PRIVATE pdfengine)
    target_compile_options(pdfengine_fuzz PRIVATE
        -fsanitize=${PDFENGINE_FUZZ_SANITIZERS} -fno-omit-frame-pointer)
    target_link_options(pdfengine_fuzz PRIVATE
        -fsanitize=${PDFENGINE_FUZZ_SANITIZERS})
endif()
