diff options
Diffstat (limited to 'src/pdf')
71 files changed, 2210 insertions, 3007 deletions
diff --git a/src/pdf/CMakeLists.txt b/src/pdf/CMakeLists.txt index 9e4004adf..4a54b816e 100644 --- a/src/pdf/CMakeLists.txt +++ b/src/pdf/CMakeLists.txt @@ -1,14 +1,15 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + cmake_minimum_required(VERSION 3.19) find_package(Ninja 1.7.2 REQUIRED) -find_package(Python2 2.7.5 REQUIRED) -find_package(Nodejs 10.19 REQUIRED) +find_package(Nodejs 14.19 REQUIRED) find_package(PkgConfig) if(PkgConfig_FOUND) create_pkg_config_host_wrapper(${CMAKE_CURRENT_BINARY_DIR}) endif() set(buildDir "${CMAKE_CURRENT_BINARY_DIR}") -add_subdirectory(plugins/imageformats/pdf) ## # PDF MODULE @@ -17,30 +18,35 @@ add_subdirectory(plugins/imageformats/pdf) qt_internal_add_module(Pdf SOURCES qpdfbookmarkmodel.cpp qpdfbookmarkmodel.h - qpdfdestination.cpp qpdfdestination.h qpdfdestination_p.h qpdfdocument.cpp qpdfdocument.h qpdfdocument_p.h qpdfdocumentrenderoptions.h - qpdflinkmodel.cpp qpdflinkmodel_p.h qpdflinkmodel_p_p.h - qpdfpagenavigation.cpp qpdfpagenavigation.h + qpdffile.cpp qpdffile_p.h + qpdflink.cpp qpdflink.h qpdflink_p.h + qpdflinkmodel.cpp qpdflinkmodel.h qpdflinkmodel_p.h + qpdfpagenavigator.cpp qpdfpagenavigator.h qpdfpagerenderer.cpp qpdfpagerenderer.h qpdfsearchmodel.cpp qpdfsearchmodel.h qpdfsearchmodel_p.h - qpdfsearchresult.cpp qpdfsearchresult.h qpdfsearchresult_p.h qpdfselection.cpp qpdfselection.h qpdfselection_p.h qtpdfglobal.h - qpdfnamespace.h INCLUDE_DIRECTORIES ../3rdparty/chromium DEFINES QT_BUILD_PDF_LIB - NOMINMAX LIBRARIES Qt::CorePrivate Qt::Network PUBLIC_LIBRARIES Qt::Core Qt::Gui + GENERATE_CPP_EXPORTS ) +add_subdirectory(plugins/imageformats/pdf) + +get_install_config(config) +get_architectures(archs) +list(GET archs 0 arch) + ## # PDF DOCS ## @@ -49,12 +55,22 @@ qt_internal_add_docs(Pdf doc/qtpdf.qdocconf ) +add_code_attributions_target( + TARGET generate_pdf_attributions + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/pdf_attributions.qdoc + GN_TARGET :QtPdf + FILE_TEMPLATE doc/about_credits.tmpl + ENTRY_TEMPLATE doc/about_credits_entry.tmpl + BUILDDIR ${buildDir}/${config}/${arch} +) +add_dependencies(generate_pdf_attributions run_pdf_GnDone) +add_dependencies(prepare_docs_Pdf generate_pdf_attributions) ## # TOOLCHAIN SETUP ## -if(LINUX) +if(LINUX OR MINGW OR ANDROID) setup_toolchains() endif() @@ -66,7 +82,6 @@ addSyncTargets(pdf) get_configs(configs) get_architectures(archs) - foreach(arch ${archs}) foreach(config ${configs}) @@ -76,7 +91,9 @@ foreach(arch ${archs}) set(buildGn pdf_${config}_${arch}) add_gn_target(${buildGn} ${config} ${arch} - SOURCES DEFINES CXX_COMPILE_OPTIONS C_COMPILE_OPTIONS INCLUDES MOC_PATH + SOURCES DEFINES CXX_COMPILE_OPTIONS C_COMPILE_OPTIONS + INCLUDES MOC_PATH PNG_INCLUDES JPEG_INCLUDES HARFBUZZ_INCLUDES + FREETYPE_INCLUDES ZLIB_INCLUDES ) resolve_target_includes(gnIncludes Pdf) get_forward_declaration_macro(forwardDeclarationMacro) @@ -99,27 +116,62 @@ foreach(arch ${archs}) list(APPEND gnArgArg qtwebengine_target="${buildDir}/${config}/${arch}:QtPdf" + qt_libpng_config="${buildDir}/${config}/${arch}:qt_libpng_config" + qt_libjpeg_config="${buildDir}/${config}/${arch}:qt_libjpeg_config" + qt_harfbuzz_config="${buildDir}/${config}/${arch}:qt_harfbuzz_config" + qt_freetype_config="${buildDir}/${config}/${arch}:qt_freetype_config" + enable_swiftshader=false + enable_swiftshader_vulkan=false + angle_enable_swiftshader=false + dawn_use_swiftshader=false + use_dawn=false + build_dawn_tests=false + enable_ipc_fuzzer=false enable_remoting=false enable_resource_allowlist_generation=false + enable_vr=false enable_web_speech=false chrome_pgo_phase=0 + strip_absolute_paths_from_debug_symbols=false + use_perfetto_client_library=false + v8_enable_webassembly=false ) - if(LINUX) - list(APPEND gnArgArg - use_x11=false - is_cfi=false - ozone_auto_platforms=false - use_gnome_keyring=false) + if(LINUX OR ANDROID) + list(APPEND gnArgArg + is_cfi=false + ozone_auto_platforms=false + enable_arcore=false + use_ml_inliner=false + ) + extend_gn_list(gnArgArg + ARGS use_system_icu + CONDITION QT_FEATURE_webengine_system_icu + ) + extend_gn_list(gnArgArg + ARGS use_system_libopenjpeg2 + CONDITION QT_FEATURE_webengine_system_libopenjpeg2 + ) endif() if(MACOS) - list(APPEND gnArgArg angle_enable_vulkan=false) + list(APPEND gnArgArg angle_enable_vulkan=false) endif() - if(WIN32) + if(IOS) + list(APPEND gnArgArg enable_base_tracing=false) + extend_gn_list(gnArgArg + ARGS enable_ios_bitcode + CONDITION QT_FEATURE_pdf_bitcode + ) + endif() + if(WIN32 OR ANDROID) list(APPEND gnArgArg ninja_use_custom_environment_files=false safe_browsing_mode=0 ) + extend_gn_list(gnArgArg + ARGS qt_uses_static_runtime + CONDITION QT_FEATURE_pdf_static_runtime + ) endif() extend_gn_list(gnArgArg @@ -146,12 +198,40 @@ foreach(arch ${archs}) ARGS pdf_enable_xfa_tiff CONDITION QT_FEATURE_pdf_xfa_tiff ) + extend_gn_list(gnArgArg + ARGS pdfium_use_system_zlib use_system_zlib + CONDITION QT_FEATURE_webengine_system_zlib + ) + extend_gn_list(gnArgArg + ARGS pdfium_use_system_libpng use_system_libpng + CONDITION QT_FEATURE_webengine_system_libpng + ) + extend_gn_list(gnArgArg + ARGS pdfium_use_qt_libpng + CONDITION QT_FEATURE_webengine_qt_libpng + ) + extend_gn_list(gnArgArg + ARGS pdfium_use_system_libtiff + CONDITION QT_FEATURE_webengine_system_libtiff + ) + extend_gn_list(gnArgArg + ARGS use_qt_libjpeg + CONDITION QT_FEATURE_webengine_qt_libjpeg + ) + extend_gn_list(gnArgArg + ARGS use_qt_harfbuzz + CONDITION QT_FEATURE_webengine_qt_harfbuzz + ) + extend_gn_list(gnArgArg + ARGS use_qt_freetype + CONDITION QT_FEATURE_webengine_qt_freetype + ) add_gn_command( CMAKE_TARGET Pdf NINJA_TARGETS QtPdf GN_TARGET ${buildGn} - GN_ARGS "${gnArgArg}" + GN_ARGS ${gnArgArg} BUILDDIR ${buildDir}/${config}/${arch} MODULE pdf ) @@ -165,8 +245,16 @@ endforeach() # PDF SETUP ## -set(arch ${CMAKE_SYSTEM_PROCESSOR}) +get_architectures(archs) +list(GET archs 0 arch) target_include_directories(Pdf PRIVATE ${buildDir}/$<CONFIG>/${arch}/gen) -add_gn_build_aritfacts_to_target(Pdf QtPdf pdf ${buildDir}) +add_gn_build_artifacts_to_target( + CMAKE_TARGET Pdf + NINJA_TARGET QtPdf + MODULE pdf + BUILDDIR ${buildDir} + COMPLETE_STATIC TRUE + NINJA_STAMP QtPdf.stamp +) add_dependencies(Pdf run_pdf_NinjaDone) diff --git a/src/pdf/config/common.pri b/src/pdf/config/common.pri deleted file mode 100644 index 7e5459a2a..000000000 --- a/src/pdf/config/common.pri +++ /dev/null @@ -1,78 +0,0 @@ -include($$QTWEBENGINE_OUT_ROOT/src/pdf/qtpdf-config.pri) -QT_FOR_CONFIG += pdf-private - -gn_args += use_nss_certs=false - -qtConfig(webengine-qt-png) { - gn_args += pdfium_use_qt_libpng=true - gn_args += "pdfium_qt_libpng_includes=\"$$system_path($$QMAKE_INCDIR_LIBPNG)\"" -} - -#qtConfig(webengine-qt-jpeg) { -# gn_args += use_qt_libjpeg=true -# gn_args += "qt_libjpeg_includes=\"$$system_path($$QMAKE_INCDIR_LIBJPEG)\"" -#} - -qtConfig(webengine-qt-harfbuzz) { - gn_args += use_qt_harfbuzz=true - gn_args += "qt_harfbuzz_includes=\"$$system_path($$QMAKE_INCDIR_HARFBUZZ)\"" -} - -qtConfig(webengine-qt-freetype) { - gn_args += use_qt_freetype=true - gn_args += "qt_freetype_includes=\"$$system_path($$QMAKE_INCDIR_FREETYPE)\"" -} - -qtConfig(webengine-qt-zlib) { - win32 { - CONFIG(debug, debug|release) { - qtzlib = Qt5Cored.lib - } else { - qtzlib = Qt5Core.lib - } - - } else { qtzlib = libQt5Core.a - } - gn_args += use_qt_zlib = true - gn_args += "qt_zlib_includes=\["\ - "\"$$system_path($$[QT_INSTALL_HEADERS])\"," \ - "\"$$system_path($$[QT_INSTALL_HEADERS]/QtCore)\"," \ - "\"$$system_path($$[QT_INSTALL_HEADERS]/QtZlib)\"\]" - gn_args += "qt_zlib=\"$$system_path($$[QT_INSTALL_LIBS]/$$qtzlib)\"" -} - -qtConfig(pdf-v8) { - gn_args += pdf_enable_v8=true -} else { - gn_args += pdf_enable_v8=false -} - -qtConfig(pdf-xfa) { - gn_args += pdf_enable_xfa=true -} else { - gn_args += pdf_enable_xfa=false -} - -qtConfig(pdf-xfa-bmp) { - gn_args += pdf_enable_xfa_bmp=true -} else { - gn_args += pdf_enable_xfa_bmp=false -} - -qtConfig(pdf-xfa-gif) { - gn_args += pdf_enable_xfa_gif=true -} else { - gn_args += pdf_enable_xfa_gif=false -} - -qtConfig(pdf-xfa-png) { - gn_args += pdf_enable_xfa_png=true -} else { - gn_args += pdf_enable_xfa_png=false -} - -qtConfig(pdf-xfa-tiff) { - gn_args += pdf_enable_xfa_tiff=true -} else { - gn_args += pdf_enable_xfa_tiff=false -} diff --git a/src/pdf/config/ios.pri b/src/pdf/config/ios.pri deleted file mode 100644 index c930c3fe3..000000000 --- a/src/pdf/config/ios.pri +++ /dev/null @@ -1,66 +0,0 @@ -load(functions) - -include($$QTWEBENGINE_OUT_ROOT/src/buildtools/qtbuildtools-config.pri) -include($$QTWEBENGINE_OUT_ROOT/src/pdf/qtpdf-config.pri) -QT_FOR_CONFIG += buildtools-private pdf-private - -clang_dir = $$which($${QMAKE_CXX}) -clang_dir = $$clean_path("$$dirname(clang_dir)/../") - -gn_args += \ -use_qt=true \ -closure_compile=false \ -is_component_build=false \ -is_shared=true \ -is_debug=true \ -enable_message_center=false \ -enable_nacl=false \ -enable_remoting=false \ -enable_reporting=false \ -enable_resource_allowlist_generation=false \ -enable_swiftshader=false \ -enable_web_speech=false \ -has_native_accessibility=false \ -enable_debugallocation=false \ -use_allocator_shim=false \ -use_allocator=\"none\" \ -use_custom_libcxx=false \ -v8_use_external_startup_data=false \ -toolkit_views=false \ -treat_warnings_as_errors=false \ -safe_browsing_mode=0 \ -optimize_webui=false \ -forbid_non_component_debug_builds=false \ -clang_use_chrome_plugins=false \ -use_xcode_clang=true \ -clang_base_path=\"$${clang_dir}\" \ -ios_enable_code_signing=false \ -target_os=\"ios\" \ -ios_deployment_target=\"$${QMAKE_IOS_DEPLOYMENT_TARGET}\" \ -mac_sdk_min=\"$${QMAKE_MAC_SDK_VERSION_MAJOR_MINOR}\" \ -enable_ios_bitcode=true \ -use_jumbo_build=false - -device:simulator { - # we do fat libray - gn_args+= \ - target_cpu=\"$${QMAKE_APPLE_DEVICE_ARCHS}\" \ - use_qt_fat_lib=true \ - arm_use_neon=false\ - # note this adds one arch of simulator at the moment, see also additional_target_cpus - target_sysroot=\"$$xcodeSDKInfo(Path, $$device.sdk)\" \ - additional_target_sysroot=[\"$$xcodeSDKInfo(Path, $$simulator.sdk)\"] -} else { - simulator { - equals(QMAKE_APPLE_SIMULATOR_ARCHS,"x86_64") { - gn_args+=target_cpu=\"x64\" - } else { - gn_args+=target_cpu=\"$${QMAKE_APPLE_SIMULATOR_ARCHS}\" - } - gn_args+=target_sysroot=\"$$xcodeSDKInfo(Path, $$simulator.sdk)\" - } - device { - gn_args+=target_cpu=\"$${QMAKE_APPLE_DEVICE_ARCHS}\" - gn_args+=target_sysroot=\"$$xcodeSDKInfo(Path, $$device.sdk)\" - } -} diff --git a/src/pdf/configure.cmake b/src/pdf/configure.cmake index ce1203205..ac4e4e25f 100644 --- a/src/pdf/configure.cmake +++ b/src/pdf/configure.cmake @@ -1,7 +1,10 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + qt_feature("pdf-v8" PRIVATE LABEL "Support V8" PURPOSE "Enables javascript support." - AUTODETECT false + AUTODETECT FALSE CONDITION NOT IOS ) qt_feature("pdf-xfa" PRIVATE @@ -29,6 +32,16 @@ qt_feature("pdf-xfa-tiff" PRIVATE PURPOSE "Enables XFA-TIFF support." CONDITION QT_FEATURE_pdf_xfa ) +qt_feature("pdf-bitcode" PRIVATE + LABEL "Bitcode support" + PURPOSE "Enables bitcode" + CONDITION IOS +) +qt_feature("pdf-static-runtime" PRIVATE + LABEL "Use static runtime" + PURPOSE "Enables static runtime" + CONDITION WIN32 AND QT_FEATURE_static AND QT_FEATURE_static_runtime +) qt_configure_add_summary_section(NAME "Qt PDF") qt_configure_add_summary_entry(ARGS "pdf-v8") qt_configure_add_summary_entry(ARGS "pdf-xfa") @@ -36,4 +49,6 @@ qt_configure_add_summary_entry(ARGS "pdf-xfa-bmp") qt_configure_add_summary_entry(ARGS "pdf-xfa-gif") qt_configure_add_summary_entry(ARGS "pdf-xfa-png") qt_configure_add_summary_entry(ARGS "pdf-xfa-tiff") +qt_configure_add_summary_entry(ARGS "pdf-bitcode") +qt_configure_add_summary_entry(ARGS "pdf-static-runtime") qt_configure_end_summary_section() diff --git a/src/pdf/configure.json b/src/pdf/configure.json deleted file mode 100644 index 069893660..000000000 --- a/src/pdf/configure.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "module": "pdf", - "depends" : [ "buildtools-private" ], - "testDir": "../../config.tests", - "condition": "module.gui && features.webengine-qtpdf-support && features.build-qtpdf", - "libraries": { - }, - "tests": { - }, - "features": { - "pdf-v8": { - "label": "Support V8", - "purpose": "Enables javascript support.", - "autoDetect": "false", - "condition": "!config.ios", - "output": ["privateFeature" ] - }, - "pdf-xfa": { - "label": "Support XFA", - "purpose": "Enables XFA support.", - "condition": "features.pdf-v8", - "output": ["privateFeature" ] - }, - "pdf-xfa-bmp": { - "label": "Support XFA-BMP", - "purpose": "Enables XFA-BMP support.", - "condition": "features.pdf-xfa", - "output": ["privateFeature" ] - }, - "pdf-xfa-gif": { - "label": "Support XFA-GIF", - "purpose": "Enables XFA-GIF support.", - "condition": "features.pdf-xfa", - "output": ["privateFeature" ] - }, - "pdf-xfa-png": { - "label": "Support XFA-PNG", - "purpose": "Enables XFA-PNG support.", - "condition": "features.pdf-xfa", - "output": ["privateFeature" ] - }, - "pdf-xfa-tiff": { - "label": "Support XFA-TIFF", - "purpose": "Enables XFA-TIFF support.", - "condition": "features.pdf-xfa", - "output": ["privateFeature" ] - } - }, - "report": [ - ], - "summary": [ - { - "section": "Qt PDF", - "entries": [ - "pdf-v8", - "pdf-xfa", - "pdf-xfa-bmp", - "pdf-xfa-gif", - "pdf-xfa-png", - "pdf-xfa-tiff" - ] - } - ] -} diff --git a/src/pdf/configure/BUILD.root.gn.in b/src/pdf/configure/BUILD.root.gn.in index 5252b701a..e9f54ed6d 100644 --- a/src/pdf/configure/BUILD.root.gn.in +++ b/src/pdf/configure/BUILD.root.gn.in @@ -1,3 +1,28 @@ +import("//build/config/features.gni") + +config("qt_libpng_config") { + include_dirs = [ @GN_PNG_INCLUDES@ ] + defines = [ "USE_SYSTEM_LIBPNG" ] +} +config ("qt_libjpeg_config") { + include_dirs = [ @GN_JPEG_INCLUDES@ ] +} +config("qt_harfbuzz_config") { + visibility = [ + "//third_party:freetype_harfbuzz", + "//third_party/freetype:freetype_source", + ] + include_dirs = [ @GN_HARFBUZZ_INCLUDES@ ] +} +config("qt_freetype_config") { + visibility = [ + "//build/config/freetype:freetype", + "//third_party:freetype_harfbuzz", + "//third_party/harfbuzz-ng:harfbuzz_source", + ] + include_dirs = [ @GN_FREETYPE_INCLUDES@ ] +} + config("QtPdf_config") { cflags = [ @GN_CFLAGS_C@, @@ -14,23 +39,35 @@ config("QtPdf_config") { ] } -config("cpp17_config") { - # static initialized constexpr expressions must be compiled always as c++14 or always as c++17 - # and our qtwebengine core sources use them as c++17 +config("cpp20_config") { + # Chromium headers now use concepts and requires c++20 if (is_win) { - cflags_cc = [ "/std:c++17" ] + cflags_cc = [ "/std:c++20" ] } else { - cflags_cc = [ "-std=c++17" ] + cflags_cc = [ "-std=c++20" ] } } -shared_library("QtPdf") { - rsp_types = [ "objects", "archives", "libs" ] +static_library("QtPdf") { + complete_static_lib = true + rsp_types = [ "objects", "archives", "libs", "ldir" ] configs += [ - ":cpp17_config", + ":cpp20_config", ":QtPdf_config" ] deps = [ "//third_party/pdfium" ] + if (is_msvc) { + libs = [ + "dloadhelper.lib", + "winmm.lib", + "usp10.lib", + ] + } + if (is_mingw) { + libs = [ + "winmm", + ] + } } diff --git a/src/pdf/doc/about_credits.tmpl b/src/pdf/doc/about_credits.tmpl new file mode 100644 index 000000000..57fae9e78 --- /dev/null +++ b/src/pdf/doc/about_credits.tmpl @@ -0,0 +1 @@ +{{entries}} diff --git a/src/pdf/doc/about_credits_entry.tmpl b/src/pdf/doc/about_credits_entry.tmpl new file mode 100644 index 000000000..294198709 --- /dev/null +++ b/src/pdf/doc/about_credits_entry.tmpl @@ -0,0 +1,13 @@ +/*! +\page qtpdf-3rdparty-{{name-sanitized}}.html +\attribution +\ingroup qtpdf-licensing +\brief {{license-type}} +\title {{name}} + +\l{{{url}}}{Project Homepage} + +\badcode +{{license}} +\endcode +*/ diff --git a/src/pdf/doc/images/multipageviewer.png b/src/pdf/doc/images/multipageviewer.png Binary files differnew file mode 100644 index 000000000..2f0bb62a2 --- /dev/null +++ b/src/pdf/doc/images/multipageviewer.png diff --git a/src/pdf/doc/images/pdfviewer.png b/src/pdf/doc/images/pdfviewer.png Binary files differnew file mode 100644 index 000000000..ac8a31ac0 --- /dev/null +++ b/src/pdf/doc/images/pdfviewer.png diff --git a/src/pdf/doc/images/search-results.png b/src/pdf/doc/images/search-results.png Binary files differnew file mode 100644 index 000000000..91ee53b83 --- /dev/null +++ b/src/pdf/doc/images/search-results.png diff --git a/src/pdf/doc/images/singlepageviewer.webp b/src/pdf/doc/images/singlepageviewer.webp Binary files differnew file mode 100644 index 000000000..e429cb818 --- /dev/null +++ b/src/pdf/doc/images/singlepageviewer.webp diff --git a/src/pdf/doc/images/wrapping-search-result.png b/src/pdf/doc/images/wrapping-search-result.png Binary files differnew file mode 100644 index 000000000..108ec0444 --- /dev/null +++ b/src/pdf/doc/images/wrapping-search-result.png diff --git a/src/pdf/doc/qtpdf.qdocconf b/src/pdf/doc/qtpdf.qdocconf index eafa0726d..d0340fe83 100644 --- a/src/pdf/doc/qtpdf.qdocconf +++ b/src/pdf/doc/qtpdf.qdocconf @@ -1,4 +1,5 @@ include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf) +include($QT_INSTALL_DOCS/config/exampleurl-qtwebengine.qdocconf) project = QtPdf description = Qt Pdf Reference Documentation @@ -29,6 +30,8 @@ qhp.QtPdf.subprojects.examples.indexTitle = Qt PDF Examples qhp.QtPdf.subprojects.examples.selectors = doc:example qhp.QtPdf.subprojects.examples.sortPages = true +manifestmeta.highlighted.names += "QtPdf/PDF Multipage Viewer Example" + depends += qtcore \ qtwidgets \ qtgui \ @@ -36,19 +39,29 @@ depends += qtcore \ qmake \ qtdesigner \ qtquick \ - qtcmake + qtquickcontrols \ + qtcmake \ + qtsvg -headerdirs += ../api \ - ../quick +headerdirs += ../ \ + ../../pdfwidgets -sourcedirs += .. \ - ../quick +sourcedirs += ../ \ + ../../pdfquick \ + ../../pdfwidgets exampledirs += ../../../examples/pdfwidgets \ + ../../../examples/pdf \ snippets/ +# add a generic thumbnail for an example that has no \image in its doc +manifestmeta.thumbnail.names = "QtPdf/PDF Viewer Example" + imagedirs += images navigation.landingpage = "Qt PDF" navigation.cppclassespage = "Qt PDF C++ Classes" -navigation.qmltypespage = "Qt WebEngine QML Types" +navigation.qmltypespage = "Qt Quick PDF QML Types" + +# Enforce zero documentation warnings +warninglimit = 0 diff --git a/src/pdf/doc/snippets/multipageview.qml b/src/pdf/doc/snippets/multipageview.qml new file mode 100644 index 000000000..113444165 --- /dev/null +++ b/src/pdf/doc/snippets/multipageview.qml @@ -0,0 +1,11 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +//! [0] +import QtQuick +import QtQuick.Pdf + +PdfMultiPageView { + document: PdfDocument { source: "my.pdf" } +} +//! [0] diff --git a/src/pdf/doc/snippets/pdfpageview.qml b/src/pdf/doc/snippets/pdfpageview.qml new file mode 100644 index 000000000..5e233961a --- /dev/null +++ b/src/pdf/doc/snippets/pdfpageview.qml @@ -0,0 +1,12 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +//! [0] +import QtQuick +import QtQuick.Pdf + +PdfPageView { + document: PdfDocument { source: "my.pdf" } +} +//! [0] + diff --git a/src/pdf/doc/snippets/qtpdf-build.cmake b/src/pdf/doc/snippets/qtpdf-build.cmake index d46b9c3ee..b4372d411 100644 --- a/src/pdf/doc/snippets/qtpdf-build.cmake +++ b/src/pdf/doc/snippets/qtpdf-build.cmake @@ -1,2 +1,2 @@ -find_package(Qt5 COMPONENTS Pdf REQUIRED) -target_link_libraries(mytarget Qt5::Pdf) +find_package(Qt6 REQUIRED COMPONENTS Pdf) +target_link_libraries(mytarget Qt6::Pdf) diff --git a/src/pdf/doc/snippets/qtpdf_build_snippet.qdoc b/src/pdf/doc/snippets/qtpdf_build_snippet.qdoc index 25593b1ee..7d30ccdfd 100644 --- a/src/pdf/doc/snippets/qtpdf_build_snippet.qdoc +++ b/src/pdf/doc/snippets/qtpdf_build_snippet.qdoc @@ -1,35 +1,6 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the documentation of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:FDL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Free Documentation License Usage -** Alternatively, this file may be used under the terms of the GNU Free -** Documentation License version 1.3 as published by the Free Software -** Foundation and appearing in the file included in the packaging of -** this file. Please review the following information to ensure -** the GNU Free Documentation License version 1.3 requirements -** will be met: https://www.gnu.org/licenses/fdl-1.3.html. -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only //! [0] QT += pdf //! [0] - - -//! [1] -#include <QtPdf> -//! [1] diff --git a/src/pdf/doc/src/qtpdf-examples.qdoc b/src/pdf/doc/src/qtpdf-examples.qdoc index 9daa0e7f8..02dc23dc2 100644 --- a/src/pdf/doc/src/qtpdf-examples.qdoc +++ b/src/pdf/doc/src/qtpdf-examples.qdoc @@ -1,33 +1,8 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the documentation of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:FDL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Free Documentation License Usage -** Alternatively, this file may be used under the terms of the GNU Free -** Documentation License version 1.3 as published by the Free Software -** Foundation and appearing in the file included in the packaging of -** this file. Please review the following information to ensure -** the GNU Free Documentation License version 1.3 requirements -** will be met: https://www.gnu.org/licenses/fdl-1.3.html. -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! \group qtpdf-examples - \ingroup all-examples \title Qt PDF Examples \brief Using the classes and types in the Qt PDF module. diff --git a/src/pdf/doc/src/qtpdf-index.qdoc b/src/pdf/doc/src/qtpdf-index.qdoc index b32787eb5..b72619fbf 100644 --- a/src/pdf/doc/src/qtpdf-index.qdoc +++ b/src/pdf/doc/src/qtpdf-index.qdoc @@ -1,29 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the documentation of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:FDL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Free Documentation License Usage -** Alternatively, this file may be used under the terms of the GNU Free -** Documentation License version 1.3 as published by the Free Software -** Foundation and appearing in the file included in the packaging of -** this file. Please review the following information to ensure -** the GNU Free Documentation License version 1.3 requirements -** will be met: https://www.gnu.org/licenses/fdl-1.3.html. -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! \page qtpdf-index.html @@ -36,8 +12,34 @@ and renders pages from it according to the options provided by the \l QPdfDocumentRenderOptions class. The \l QPdfPageRenderer class manages a queue that collects all render requests. The - \l QPdfPageNavigation class handles the navigation through a - PDF document. + \l QPdfPageNavigator class handles the navigation through a + PDF document. The \l QPdfSearchModel class searches for a string + and holds the search results. The QPdfBookmarkModel class holds the + table of contents, if present. The QPdfLinkModel holds information + about hyperlinks on a page. The \l QPdfView widget is a complete + PDF viewer, and the \l {PDF Viewer Widget Example} shows how to use it. + + For Qt Quick applications, three kinds of full-featured viewer + components are provided. \l PdfMultiPageView should be your + first choice for the most common user experience: flicking + through the pages in the entire document. + \l PdfScrollablePageView shows one page at a time, with scrolling; + and \l PdfPageView shows one full page at a time, without scrolling. + + The full-featured viewer components are composed of lower-level + QML components that can be used separately if you need to write a + more customized PDF viewing application: \l PdfDocument, + \l PdfPageImage, \l PdfPageNavigator, \l PdfSelection, + \l PdfSearchModel, \l PdfLinkModel, and \l PdfBookmarkModel. + + If you only need to render page images, without features such as + text selection, search and navigation, this module includes a + \l QImageIOHandler plugin that treats PDF as a scalable + \l {Qt Image Formats}{image format}, similar to \l {Qt SVG}{SVG}. + You can simply use \l Image, and set the + \l {Image::currentFrame}{currentFrame} property to the page index + that you wish to display. If the PDF file does not render its own + background, the image has a transparent background. \include module-use.qdocinc using qt module \quotefile qtpdf-build.cmake @@ -46,21 +48,10 @@ \section2 Building with qmake - To include the definitions of the module's classes, use the - following directive: - - \snippet qtpdf_build_snippet.qdoc 1 - To link against the module, add this line to your qmake project file: \snippet qtpdf_build_snippet.qdoc 0 - \section1 Articles and Guides - - \list - \li \l{Qt PDF Overview} - \endlist - \section1 Examples \list @@ -73,4 +64,17 @@ \li \l{Qt PDF C++ Classes} \li \l{Qt Quick PDF QML Types} \endlist + + \section1 Articles and Guides + \list + \li {Qt PDF Platform Notes} {Platform Notes} + \endlist + + \section1 Licenses and Attributions + + Qt PDF is available under commercial licenses from \l{The Qt Company}. + In addition, it is available under the + \l{GNU Lesser General Public License, version 3}, or + the \l{GNU General Public License, version 2}. + See \l{Qt PDF Licensing} for further details about this module. */ diff --git a/src/pdf/doc/src/qtpdf-licensing.qdoc b/src/pdf/doc/src/qtpdf-licensing.qdoc new file mode 100644 index 000000000..190ee8331 --- /dev/null +++ b/src/pdf/doc/src/qtpdf-licensing.qdoc @@ -0,0 +1,18 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \group qtpdf-licensing + \title Qt PDF Licensing + + Qt PDF is available under commercial licenses from \l{The Qt Company}. + In addition, it is available under the + \l{GNU Lesser General Public License, version 3}, or + the \l{GNU General Public License, version 2}. + See \l{Qt Licensing} for further details. + + The module includes a snapshot of PDFium. As such, users need to respect + the licenses of PDFium and third-party code included in it. + + Third party licenses included in the sources are: +*/ diff --git a/src/pdf/doc/src/qtpdf-module.qdoc b/src/pdf/doc/src/qtpdf-module.qdoc index 4170deb38..e2ca8e4ce 100644 --- a/src/pdf/doc/src/qtpdf-module.qdoc +++ b/src/pdf/doc/src/qtpdf-module.qdoc @@ -1,29 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the documentation of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:FDL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Free Documentation License Usage -** Alternatively, this file may be used under the terms of the GNU Free -** Documentation License version 1.3 as published by the Free Software -** Foundation and appearing in the file included in the packaging of -** this file. Please review the following information to ensure -** the GNU Free Documentation License version 1.3 requirements -** will be met: https://www.gnu.org/licenses/fdl-1.3.html. -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! @@ -37,11 +13,6 @@ The Qt PDF module contains classes and functions for rendering PDF documents. - To include the definitions of the module's classes, use the - following directive: - - \snippet qtpdf_build_snippet.qdoc 1 - \if !defined(qtforpython) To link against the module, add this line to your qmake project file: diff --git a/src/pdf/doc/src/qtpdf-platformnotes.qdoc b/src/pdf/doc/src/qtpdf-platformnotes.qdoc new file mode 100644 index 000000000..f50be120d --- /dev/null +++ b/src/pdf/doc/src/qtpdf-platformnotes.qdoc @@ -0,0 +1,11 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \page qtpdf-platformnotes.html + \title Qt PDF Platform Notes + + Building Qt PDF for Android is currently + \l{https://bugreports.qt.io/browse/QTBUG-83459} {not supported} on Windows host platforms. +*/ + diff --git a/src/pdf/gn_run.pro b/src/pdf/gn_run.pro deleted file mode 100644 index 70ee582a9..000000000 --- a/src/pdf/gn_run.pro +++ /dev/null @@ -1,69 +0,0 @@ -include($$QTWEBENGINE_OUT_ROOT/src/buildtools/qtbuildtools-config.pri) -QT_FOR_CONFIG += buildtools-private - -TEMPLATE = aux - -qtConfig(debug_and_release): CONFIG += debug_and_release -qtConfig(build_all): CONFIG += build_all - -qtConfig(webengine-system-ninja) { - QT_TOOL.ninja.binary = ninja -} else { - QT_TOOL.ninja.binary = $$shell_quote($$shell_path($$ninjaPath())) -} - -win32 { - # Add the gnuwin32/bin subdir of qt5.git to PATH. Needed for calling bison and friends. - gnuwin32path.name = PATH - gnuwin32path.value = $$shell_path($$clean_path($$QTWEBENGINE_ROOT/../gnuwin32/bin)) - gnuwin32path.CONFIG += prepend - exists($$gnuwin32path.value): QT_TOOL_ENV = gnuwin32path -} - -qtPrepareTool(NINJA, ninja) -QT_TOOL_ENV = - -build_pass|!debug_and_release { - gn_binary = gn - - runninja.target = run_ninja - - # fixme: refine args - gn_args = $$gnPdfArgs() - - # fixme: qtwebengine_target - gn_args += "qtwebengine_target=\"$$system_path($$OUT_PWD/$$getConfigDir()):QtPdf\"" - - # fixme: - !qtConfig(webengine-system-gn) { - gn_binary = $$system_quote($$system_path($$gnPath())) - } - - gn_args = $$system_quote($$gn_args) - gn_src_root = $$system_quote($$system_path($$QTWEBENGINE_ROOT/$$getChromiumSrcDir())) - gn_build_root = $$system_quote($$system_path($$OUT_PWD/$$getConfigDir())) - gn_python = "--script-executable=$$pythonPathForSystem()" - gn_run = $$gn_binary gen $$gn_build_root $$gn_python --args=$$gn_args --root=$$gn_src_root - - message("Running: $$gn_run ") - !system($$gn_run) { - error("GN run error!") - } - - ninjaflags = $$(NINJAFLAGS) - isEmpty(ninjaflags):!silent: ninjaflags = "-v" - - runninja.commands = $$NINJA $$ninjaflags -C $$gn_build_root QtPdf - QMAKE_EXTRA_TARGETS += runninja - - build_pass:build_all: default_target.target = all - else: default_target.target = first - default_target.depends = runninja - QMAKE_EXTRA_TARGETS += default_target -} - -!build_pass:debug_and_release { - # Special GNU make target for the meta Makefile that ensures that our debug and release Makefiles won't both run ninja in parallel. - notParallel.target = .NOTPARALLEL - QMAKE_EXTRA_TARGETS += notParallel -} diff --git a/src/pdf/pdf.pro b/src/pdf/pdf.pro deleted file mode 100644 index 9f98c32b0..000000000 --- a/src/pdf/pdf.pro +++ /dev/null @@ -1,31 +0,0 @@ -include($$QTWEBENGINE_OUT_ROOT/src/buildtools/qtbuildtools-config.pri) -QT_FOR_CONFIG += buildtools-private -TEMPLATE = subdirs -pdfcore.file = pdfcore.pro -pdfcore_generator.file = pdfcore_generator.pro -gn_run.file = gn_run.pro -pdfcore_prl_generator.file = pdfcore_prl_generator.pro -gn_run.depends = pdfcore_generator -pdfcore_prl_generator.depends = gn_run -pdfcore.depends = pdfcore_prl_generator -quick.depends = pdfcore - -!qtConfig(webengine-qtpdf-support):qtConfig(build-qtpdf)::!build_pass { - !qtwebengine_makeCheckPdfError() { - errorbuild.commands = @echo $$shell_quote("QtPdf will not be built. $${skipBuildReason}") - } else { - errorbuild.commands = @echo $$shell_quote("QtPdf module will not be built for unknown reason, please open a bug report at https://bugreports.qt.io") - } - errorbuild.CONFIG = phony - QMAKE_EXTRA_TARGETS += errorbuild - first.depends += errorbuild - QMAKE_EXTRA_TARGETS += first -} else { - SUBDIRS += \ - pdfcore_generator \ - gn_run \ - pdfcore_prl_generator \ - pdfcore \ - quick -} - diff --git a/src/pdf/pdfcore.pro b/src/pdf/pdfcore.pro deleted file mode 100644 index bb7146853..000000000 --- a/src/pdf/pdfcore.pro +++ /dev/null @@ -1,87 +0,0 @@ -TARGET = QtPdf -MODULE = pdf - -QT += gui core core-private -QT_PRIVATE += network - -TEMPLATE = lib - -INCLUDEPATH += $$QTWEBENGINE_ROOT/src/pdf -CHROMIUM_SRC_DIR = $$QTWEBENGINE_ROOT/$$getChromiumSrcDir() -CHROMIUM_GEN_DIR = $$OUT_PWD/../$$getConfigDir()/gen -INCLUDEPATH += $$QTWEBENGINE_ROOT/src/pdf \ - $$CHROMIUM_GEN_DIR \ - $$CHROMIUM_SRC_DIR \ - api - -DEFINES += QT_BUILD_PDF_LIB -win32: DEFINES += NOMINMAX - -QMAKE_DOCS = $$PWD/doc/qtpdf.qdocconf - -gcc { - QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-parameter -} - -msvc { - QMAKE_CXXFLAGS_WARN_ON += -wd"4100" -} - -include($${QTWEBENGINE_ROOT}/src/buildtools/config/linking.pri) - -# install static dependencies and handle prl files for static builds - -static:!isEmpty(NINJA_ARCHIVES) { - static_dep_pri = $$OUT_PWD/$$getConfigDir()/$${TARGET}_static_dep.pri - !include($${static_dep_pri}) { - error("Could not find the prl information.") - } - ninja_archives = $$eval($$list($$NINJA_ARCHIVES)) - ninja_archs_install.files = $${ninja_archives} - ninja_archs_install.path = $$[QT_INSTALL_LIBS]/static_chrome - ninja_archs_install.CONFIG = no_check_exist - INSTALLS += ninja_archs_install -} - -SOURCES += \ - qpdfbookmarkmodel.cpp \ - qpdfdestination.cpp \ - qpdfdocument.cpp \ - qpdflinkmodel.cpp \ - qpdfpagenavigation.cpp \ - qpdfpagerenderer.cpp \ - qpdfsearchmodel.cpp \ - qpdfsearchresult.cpp \ - qpdfselection.cpp \ - -# all "public" headers must be in "api" for sync script and to hide auto generated headers -# by Chromium in case of in-source build - -HEADERS += \ - api/qpdfbookmarkmodel.h \ - api/qpdfdestination.h \ - api/qpdfdestination_p.h \ - api/qpdfdocument.h \ - api/qpdfdocument_p.h \ - api/qpdfdocumentrenderoptions.h \ - api/qtpdfglobal.h \ - api/qpdflinkmodel_p.h \ - api/qpdflinkmodel_p_p.h \ - api/qpdfnamespace.h \ - api/qpdfpagenavigation.h \ - api/qpdfpagerenderer.h \ - api/qpdfsearchmodel.h \ - api/qpdfsearchmodel_p.h \ - api/qpdfsearchresult.h \ - api/qpdfsearchresult_p.h \ - api/qpdfselection.h \ - api/qpdfselection_p.h \ - - -qtConfig(webengine-qt-freetype): QMAKE_USE += freetype -qtConfig(webengine-qt-png): QMAKE_USE += libpng -qtConfig(webengine-qt-harfbuzz): QMAKE_USE += harfbuzz -#qtConfig(webengine-qt-jpeg): QMAKE_USE += libjpeg -qtConfig(webengine-qt-zlib){} #qtzlib is a part of QtCore - -load(qt_module) diff --git a/src/pdf/pdfcore_generator.pro b/src/pdf/pdfcore_generator.pro deleted file mode 100644 index e5c7258b7..000000000 --- a/src/pdf/pdfcore_generator.pro +++ /dev/null @@ -1,15 +0,0 @@ -qtConfig(debug_and_release): CONFIG += debug_and_release - -TARGET = QtPdf -TEMPLATE = lib -CONFIG = gn_generator $$CONFIG -CONFIG -=static # note we still do static when linking -GN_SRC_DIR = $$PWD -GN_FILE = $$OUT_PWD/$$getConfigDir()/BUILD.gn -GN_FIND_MOCABLES_SCRIPT = $$shell_path($$QTWEBENGINE_ROOT/tools/scripts/gn_find_mocables.py) -GN_RUN_BINARY_SCRIPT = $$shell_path($$QTWEBENGINE_ROOT/tools/scripts/gn_run_binary.py) -GN_IMPORTS = $$PWD/qtpdf.gni -GN_CREATE_PRI = true -QMAKE_INTERNAL_INCLUDED_FILES = $$GN_IMPORTS $$GN_INCLUDES $$GN_FILE - - diff --git a/src/pdf/pdfcore_prl_generator.pro b/src/pdf/pdfcore_prl_generator.pro deleted file mode 100644 index 39fdaed40..000000000 --- a/src/pdf/pdfcore_prl_generator.pro +++ /dev/null @@ -1,27 +0,0 @@ - -qtConfig(debug_and_release): CONFIG += debug_and_release - -TARGET = QtPdf -TEMPLATE = aux - -build_pass|!debug_and_relase { - linking_pri = $$OUT_PWD/$$getConfigDir()/$${TARGET}.pri - !include($$linking_pri) { - error("Could not find the linking information that gn should have generated.") - } - - !isEmpty(NINJA_ARCHIVES) { - prl_file = $$OUT_PWD/$$getConfigDir()/$${TARGET}_static_dep.pri - ninja_archives = $$eval($$list($$NINJA_ARCHIVES)) - qqt_libdir = \$\$\$\$[QT_INSTALL_LIBS] - for(ninja_arch, ninja_archives) { - ninja_arch_name = $$basename(ninja_arch) - ninja_arch_dirname = $$dirname(ninja_arch) - prl_content += "ninja_arch_prl_replace_$${ninja_arch_name}.match = $${ninja_arch_dirname}" - prl_content += "ninja_arch_prl_replace_$${ninja_arch_name}.replace = $${qqt_libdir}/static_chrome" - prl_content += "ninja_arch_prl_replace_$${ninja_arch_name}.CONFIG = path" - prl_content += "QMAKE_PRL_INSTALL_REPLACE += ninja_arch_prl_replace_$${ninja_arch_name}" - } - write_file($${prl_file}, prl_content) - } -} diff --git a/src/pdf/plugins/imageformats/pdf/CMakeLists.txt b/src/pdf/plugins/imageformats/pdf/CMakeLists.txt index ee290782b..73a0b3144 100644 --- a/src/pdf/plugins/imageformats/pdf/CMakeLists.txt +++ b/src/pdf/plugins/imageformats/pdf/CMakeLists.txt @@ -1,6 +1,9 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + qt_internal_add_plugin(QPdfPlugin OUTPUT_NAME qpdf - TYPE imageformats + PLUGIN_TYPE imageformats SOURCES main.cpp qpdfiohandler.cpp qpdfiohandler_p.h diff --git a/src/pdf/plugins/imageformats/pdf/main.cpp b/src/pdf/plugins/imageformats/pdf/main.cpp index b4d59353c..cb69c4ca1 100644 --- a/src/pdf/plugins/imageformats/pdf/main.cpp +++ b/src/pdf/plugins/imageformats/pdf/main.cpp @@ -1,38 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtPDF module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL3$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPLv3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qpdfiohandler_p.h" diff --git a/src/pdf/plugins/imageformats/pdf/qpdfiohandler.cpp b/src/pdf/plugins/imageformats/pdf/qpdfiohandler.cpp index 4f610935c..bb3e7c929 100644 --- a/src/pdf/plugins/imageformats/pdf/qpdfiohandler.cpp +++ b/src/pdf/plugins/imageformats/pdf/qpdfiohandler.cpp @@ -1,42 +1,10 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtPDF module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL3$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPLv3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qpdfiohandler_p.h" #include <QLoggingCategory> #include <QPainter> +#include <QtPdf/private/qpdffile_p.h> QT_BEGIN_NAMESPACE @@ -46,6 +14,12 @@ QPdfIOHandler::QPdfIOHandler() { } +QPdfIOHandler::~QPdfIOHandler() +{ + if (m_ownsDocument) + delete m_doc; +} + bool QPdfIOHandler::canRead() const { if (!device()) @@ -73,27 +47,27 @@ int QPdfIOHandler::currentImageNumber() const QRect QPdfIOHandler::currentImageRect() const { - return QRect(QPoint(0, 0), m_doc.pageSize(m_page).toSize()); + return QRect(QPoint(0, 0), m_doc->pagePointSize(m_page).toSize()); } int QPdfIOHandler::imageCount() const { int ret = 0; if (const_cast<QPdfIOHandler *>(this)->load(device())) - ret = m_doc.pageCount(); - qCDebug(qLcPdf) << "imageCount" << ret; + ret = m_doc->pageCount(); + qCDebug(qLcPdf) << ret; return ret; } bool QPdfIOHandler::read(QImage *image) { if (load(device())) { - if (m_page >= m_doc.pageCount()) + if (m_doc.isNull() || m_page >= m_doc->pageCount()) return false; if (m_page < 0) m_page = 0; const bool xform = (m_clipRect.isValid() || m_scaledSize.isValid() || m_scaledClipRect.isValid()); - QSize pageSize = m_doc.pageSize(m_page).toSize(); + QSize pageSize = m_doc->pagePointSize(m_page).toSize(); QSize finalSize = pageSize; QRectF bounds; if (xform && !finalSize.isEmpty()) { @@ -120,7 +94,7 @@ bool QPdfIOHandler::read(QImage *image) t.translate(tr1.x(), tr1.y()); bounds = t.mapRect(bounds); } - qCDebug(qLcPdf) << Q_FUNC_INFO << m_page << finalSize; + qCDebug(qLcPdf) << m_page << finalSize; if (image->size() != finalSize || !image->reinterpretAsFormat(QImage::Format_ARGB32_Premultiplied)) { *image = QImage(finalSize, QImage::Format_ARGB32_Premultiplied); if (!finalSize.isEmpty() && image->isNull()) { @@ -136,9 +110,11 @@ bool QPdfIOHandler::read(QImage *image) options.setScaledSize(pageSize); image->fill(m_backColor.rgba()); QPainter p(image); - QImage pageImage = m_doc.render(m_page, finalSize, options); - p.drawImage(0, 0, pageImage); - p.end(); + if (!m_doc.isNull()) { + QImage pageImage = m_doc->render(m_page, finalSize, options); + p.drawImage(0, 0, pageImage); + p.end(); + } } return true; } @@ -153,7 +129,7 @@ QVariant QPdfIOHandler::option(ImageOption option) const return QImage::Format_ARGB32_Premultiplied; case Size: const_cast<QPdfIOHandler *>(this)->load(device()); - return m_doc.pageSize(qMax(0, m_page)); + return m_doc->pagePointSize(qMax(0, m_page)); case ClipRect: return m_clipRect; case ScaledSize: @@ -163,7 +139,7 @@ QVariant QPdfIOHandler::option(ImageOption option) const case BackgroundColor: return m_backColor; case Name: - return m_doc.metaData(QPdfDocument::Title); + return m_doc->metaData(QPdfDocument::MetaDataField::Title); default: break; } @@ -210,7 +186,7 @@ bool QPdfIOHandler::supportsOption(ImageOption option) const bool QPdfIOHandler::jumpToImage(int frame) { - qCDebug(qLcPdf) << Q_FUNC_INFO << frame; + qCDebug(qLcPdf) << frame; if (frame < 0 || frame >= imageCount()) return false; m_page = frame; @@ -230,8 +206,18 @@ bool QPdfIOHandler::load(QIODevice *device) if (!canRead()) return false; - m_doc.load(device); - m_loaded = (m_doc.error() == QPdfDocument::DocumentError::NoError); + QPdfFile *pdfFile = qobject_cast<QPdfFile *>(device); + if (pdfFile) { + m_doc = pdfFile->document(); + m_ownsDocument = false; + qCDebug(qLcPdf) << "loading via QPdfFile, reusing document instance" << m_doc; + } else { + m_doc = new QPdfDocument(); + m_ownsDocument = true; + m_doc->load(device); + qCDebug(qLcPdf) << "loading via new document instance" << m_doc; + } + m_loaded = (m_doc->error() == QPdfDocument::Error::None); return m_loaded; } diff --git a/src/pdf/plugins/imageformats/pdf/qpdfiohandler_p.h b/src/pdf/plugins/imageformats/pdf/qpdfiohandler_p.h index 99a91154c..c4d8e0f9a 100644 --- a/src/pdf/plugins/imageformats/pdf/qpdfiohandler_p.h +++ b/src/pdf/plugins/imageformats/pdf/qpdfiohandler_p.h @@ -1,38 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtPDF module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL3$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPLv3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QPDFIOHANDLER_H #define QPDFIOHANDLER_H @@ -57,6 +24,7 @@ class QPdfIOHandler : public QImageIOHandler { public: QPdfIOHandler(); + ~QPdfIOHandler() override; bool canRead() const override; static bool canRead(QIODevice *device); int currentImageNumber() const override; @@ -73,7 +41,7 @@ private: bool load(QIODevice *device); private: - QPdfDocument m_doc; + QPointer<QPdfDocument> m_doc; int m_page = -1; QRect m_clipRect; @@ -81,6 +49,7 @@ private: QRect m_scaledClipRect; QColor m_backColor = Qt::transparent; bool m_loaded = false; + bool m_ownsDocument = false; }; QT_END_NAMESPACE diff --git a/src/pdf/qpdfbookmarkmodel.cpp b/src/pdf/qpdfbookmarkmodel.cpp index 0450870a1..93dbf5d1f 100644 --- a/src/pdf/qpdfbookmarkmodel.cpp +++ b/src/pdf/qpdfbookmarkmodel.cpp @@ -1,38 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtPDF module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL3$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPLv3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qpdfbookmarkmodel.h" @@ -42,19 +9,21 @@ #include "third_party/pdfium/public/fpdf_doc.h" #include "third_party/pdfium/public/fpdfview.h" +#include <QLoggingCategory> +#include <QMetaEnum> #include <QPointer> #include <QScopedPointer> #include <private/qabstractitemmodel_p.h> QT_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(qLcBM, "qt.pdf.bookmarks") + class BookmarkNode { public: explicit BookmarkNode(BookmarkNode *parentNode = nullptr) : m_parentNode(parentNode) - , m_level(0) - , m_pageNumber(0) { } @@ -81,7 +50,7 @@ public: int childCount() const { - return m_childNodes.count(); + return m_childNodes.size(); } int row() const @@ -127,32 +96,49 @@ public: m_pageNumber = pageNumber; } + QPointF location() const + { + return m_location; + } + + void setLocation(qreal x, qreal y) + { + m_location = QPointF(x, y); + } + + qreal zoom() const + { + return m_zoom; + } + + void setZoom(qreal zoom) + { + m_zoom = zoom; + } + private: QList<BookmarkNode*> m_childNodes; BookmarkNode *m_parentNode; QString m_title; - int m_level; - int m_pageNumber; + int m_level = 0; + int m_pageNumber = 0; + QPointF m_location; + qreal m_zoom = 0; }; -class QPdfBookmarkModelPrivate : public QAbstractItemModelPrivate +struct QPdfBookmarkModelPrivate { -public: QPdfBookmarkModelPrivate() - : QAbstractItemModelPrivate() - , m_rootNode(new BookmarkNode(nullptr)) + : m_rootNode(new BookmarkNode(nullptr)) , m_document(nullptr) - , m_structureMode(QPdfBookmarkModel::TreeMode) { } void rebuild() { - Q_Q(QPdfBookmarkModel); - - const bool documentAvailable = (m_document && m_document->status() == QPdfDocument::Ready); + const bool documentAvailable = (m_document && m_document->status() == QPdfDocument::Status::Ready); if (documentAvailable) { q->beginResetModel(); @@ -179,21 +165,29 @@ public: while (bookmark) { BookmarkNode *childBookmarkNode = nullptr; - if (m_structureMode == QPdfBookmarkModel::TreeMode) { - childBookmarkNode = new BookmarkNode(parentBookmarkNode); - parentBookmarkNode->appendChild(childBookmarkNode); - } else if (m_structureMode == QPdfBookmarkModel::ListMode) { - childBookmarkNode = new BookmarkNode(m_rootNode.data()); - m_rootNode->appendChild(childBookmarkNode); - } + childBookmarkNode = new BookmarkNode(parentBookmarkNode); + parentBookmarkNode->appendChild(childBookmarkNode); + Q_ASSERT(childBookmarkNode); const int titleLength = int(FPDFBookmark_GetTitle(bookmark, nullptr, 0)); QList<char16_t> titleBuffer(titleLength); - FPDFBookmark_GetTitle(bookmark, titleBuffer.data(), quint32(titleBuffer.length())); + FPDFBookmark_GetTitle(bookmark, titleBuffer.data(), quint32(titleBuffer.size())); const FPDF_DEST dest = FPDFBookmark_GetDest(document, bookmark); const int pageNumber = FPDFDest_GetDestPageIndex(document, dest); + const qreal pageHeight = m_document->pagePointSize(pageNumber).height(); + FPDF_BOOL hasX, hasY, hasZoom; + FS_FLOAT x, y, zoom; + bool ok = FPDFDest_GetLocationInPage(dest, &hasX, &hasY, &hasZoom, &x, &y, &zoom); + if (ok) { + if (hasX && hasY) + childBookmarkNode->setLocation(x, pageHeight - y); + if (hasZoom) + childBookmarkNode->setZoom(zoom); + } else { + qCWarning(qLcBM) << "bookmark with invalid location and/or zoom" << x << y << zoom; + } childBookmarkNode->setTitle(QString::fromUtf16(titleBuffer.data())); childBookmarkNode->setLevel(level); @@ -211,30 +205,67 @@ public: rebuild(); } - Q_DECLARE_PUBLIC(QPdfBookmarkModel) + QPdfBookmarkModel *q = nullptr; QScopedPointer<BookmarkNode> m_rootNode; QPointer<QPdfDocument> m_document; - QPdfBookmarkModel::StructureMode m_structureMode; + QHash<int, QByteArray> m_roleNames; }; +/*! + \class QPdfBookmarkModel + \since 5.10 + \inmodule QtPdf + \inherits QAbstractItemModel + + \brief The QPdfBookmarkModel class holds a tree of of links (anchors) + within a PDF document, such as the table of contents. + + This is used in the \l {Model/View Programming} paradigm to display a + table of contents in the form of a tree or list. +*/ + +/*! + \enum QPdfBookmarkModel::Role + + \value Title The name of the bookmark for display. + \value Level The level of indentation. + \value Page The page number of the destination (int). + \value Location The position of the destination (QPointF). + \value Zoom The suggested zoom level (qreal). + \omitvalue NRoles +*/ + +/*! + Constructs a new bookmark model with parent object \a parent. +*/ QPdfBookmarkModel::QPdfBookmarkModel(QObject *parent) - : QAbstractItemModel(*new QPdfBookmarkModelPrivate, parent) + : QAbstractItemModel(parent), d(new QPdfBookmarkModelPrivate) { + d->q = this; + d->m_roleNames = QAbstractItemModel::roleNames(); + QMetaEnum rolesMetaEnum = metaObject()->enumerator(metaObject()->indexOfEnumerator("Role")); + for (int r = Qt::UserRole; r < int(Role::NRoles); ++r) + d->m_roleNames.insert(r, QByteArray(rolesMetaEnum.valueToKey(r)).toLower()); } +/*! + Destroys the model. +*/ +QPdfBookmarkModel::~QPdfBookmarkModel() = default; + QPdfDocument* QPdfBookmarkModel::document() const { - Q_D(const QPdfBookmarkModel); - return d->m_document; } +/*! + \property QPdfBookmarkModel::document + \brief the PDF document in which bookmarks are to be found. +*/ void QPdfBookmarkModel::setDocument(QPdfDocument *document) { - Q_D(QPdfBookmarkModel); - if (d->m_document == document) return; @@ -250,65 +281,56 @@ void QPdfBookmarkModel::setDocument(QPdfDocument *document) d->rebuild(); } -QPdfBookmarkModel::StructureMode QPdfBookmarkModel::structureMode() const -{ - Q_D(const QPdfBookmarkModel); - - return d->m_structureMode; -} - -void QPdfBookmarkModel::setStructureMode(StructureMode mode) -{ - Q_D(QPdfBookmarkModel); - - if (d->m_structureMode == mode) - return; - - d->m_structureMode = mode; - emit structureModeChanged(d->m_structureMode); - - d->rebuild(); -} - +/*! + \reimp +*/ int QPdfBookmarkModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 1; } +/*! + \reimp +*/ QHash<int, QByteArray> QPdfBookmarkModel::roleNames() const { - QHash<int, QByteArray> names; - - names[TitleRole] = "title"; - names[LevelRole] = "level"; - names[PageNumberRole] = "pageNumber"; - - return names; + return d->m_roleNames; } +/*! + \reimp +*/ QVariant QPdfBookmarkModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); const BookmarkNode *node = static_cast<BookmarkNode*>(index.internalPointer()); - switch (role) { - case TitleRole: + switch (Role(role)) { + case Role::Title: return node->title(); - case LevelRole: + case Role::Level: return node->level(); - case PageNumberRole: + case Role::Page: return node->pageNumber(); - default: - return QVariant(); + case Role::Location: + return node->location(); + case Role::Zoom: + return node->zoom(); + case Role::NRoles: + break; } + if (role == Qt::DisplayRole) + return node->title(); + return QVariant(); } +/*! + \reimp +*/ QModelIndex QPdfBookmarkModel::index(int row, int column, const QModelIndex &parent) const { - Q_D(const QPdfBookmarkModel); - if (!hasIndex(row, column, parent)) return QModelIndex(); @@ -326,10 +348,11 @@ QModelIndex QPdfBookmarkModel::index(int row, int column, const QModelIndex &par return QModelIndex(); } +/*! + \reimp +*/ QModelIndex QPdfBookmarkModel::parent(const QModelIndex &index) const { - Q_D(const QPdfBookmarkModel); - if (!index.isValid()) return QModelIndex(); @@ -342,10 +365,11 @@ QModelIndex QPdfBookmarkModel::parent(const QModelIndex &index) const return createIndex(parentNode->row(), 0, parentNode); } +/*! + \reimp +*/ int QPdfBookmarkModel::rowCount(const QModelIndex &parent) const { - Q_D(const QPdfBookmarkModel); - if (parent.column() > 0) return 0; diff --git a/src/pdf/qpdfbookmarkmodel.h b/src/pdf/qpdfbookmarkmodel.h index 8e06a1547..5a3c24f84 100644 --- a/src/pdf/qpdfbookmarkmodel.h +++ b/src/pdf/qpdfbookmarkmodel.h @@ -1,38 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtPDF module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL3$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPLv3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QPDFBOOKMARKMODEL_H #define QPDFBOOKMARKMODEL_H @@ -43,39 +10,33 @@ QT_BEGIN_NAMESPACE class QPdfDocument; -class QPdfBookmarkModelPrivate; +struct QPdfBookmarkModelPrivate; class Q_PDF_EXPORT QPdfBookmarkModel : public QAbstractItemModel { Q_OBJECT Q_PROPERTY(QPdfDocument* document READ document WRITE setDocument NOTIFY documentChanged) - Q_PROPERTY(StructureMode structureMode READ structureMode WRITE setStructureMode NOTIFY structureModeChanged) public: - enum StructureMode + enum class Role : int { - TreeMode, - ListMode - }; - Q_ENUM(StructureMode) - - enum Role - { - TitleRole = Qt::DisplayRole, - LevelRole = Qt::UserRole, - PageNumberRole + Title = Qt::UserRole, + Level, + Page, + Location, + Zoom, + NRoles }; Q_ENUM(Role) - explicit QPdfBookmarkModel(QObject *parent = nullptr); + QPdfBookmarkModel() : QPdfBookmarkModel(nullptr) {} + explicit QPdfBookmarkModel(QObject *parent); + ~QPdfBookmarkModel() override; QPdfDocument* document() const; void setDocument(QPdfDocument *document); - StructureMode structureMode() const; - void setStructureMode(StructureMode mode); - QVariant data(const QModelIndex &index, int role) const override; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &index) const override; @@ -85,12 +46,13 @@ public: Q_SIGNALS: void documentChanged(QPdfDocument *document); - void structureModeChanged(QPdfBookmarkModel::StructureMode structureMode); private: - Q_DECLARE_PRIVATE(QPdfBookmarkModel) + std::unique_ptr<QPdfBookmarkModelPrivate> d; + + Q_PRIVATE_SLOT(d, void _q_documentStatusChanged()) - Q_PRIVATE_SLOT(d_func(), void _q_documentStatusChanged()) + friend struct QPdfBookmarkModelPrivate; }; QT_END_NAMESPACE diff --git a/src/pdf/qpdfdestination.cpp b/src/pdf/qpdfdestination.cpp deleted file mode 100644 index b70e031ca..000000000 --- a/src/pdf/qpdfdestination.cpp +++ /dev/null @@ -1,145 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtPDF module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL3$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPLv3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qpdfdestination.h" -#include "qpdfdestination_p.h" - -QT_BEGIN_NAMESPACE - -/*! - \class QPdfDestination - \since 5.15 - \inmodule QtPdf - - \brief The QPdfDestination class defines a location on a page in a PDF - document, and a suggested zoom level at which it is intended to be viewed. -*/ - -/*! - Constructs an invalid Destination. - - \sa valid -*/ -QPdfDestination::QPdfDestination() - : d(new QPdfDestinationPrivate()) -{ -} - -QPdfDestination::QPdfDestination(int page, QPointF location, qreal zoom) - : d(new QPdfDestinationPrivate(page, location, zoom)) -{ -} - -QPdfDestination::QPdfDestination(QPdfDestinationPrivate *d) - : d(d) -{ -} - -QPdfDestination::QPdfDestination(const QPdfDestination &other) - : d(other.d) -{ -} - -QPdfDestination::QPdfDestination(QPdfDestination &&other) noexcept - : d(std::move(other.d)) -{ -} - -QPdfDestination::~QPdfDestination() -{ -} - -QPdfDestination &QPdfDestination::operator=(const QPdfDestination &other) -{ - d = other.d; - return *this; -} - -/*! - \property QPdfDestination::valid - - This property holds whether the destination is valid. -*/ -bool QPdfDestination::isValid() const -{ - return d->page >= 0; -} - -/*! - \property QPdfDestination::page - - This property holds the page number. -*/ -int QPdfDestination::page() const -{ - return d->page; -} - -/*! - \property QPdfDestination::location - - This property holds the location on the page, in units of points. -*/ -QPointF QPdfDestination::location() const -{ - return d->location; -} - -/*! - \property QPdfDestination::zoom - - This property holds the suggested magnification level, where 1.0 means default scale - (1 pixel = 1 point). -*/ -qreal QPdfDestination::zoom() const -{ - return d->zoom; -} - -QDebug operator<<(QDebug dbg, const QPdfDestination& dest) -{ - QDebugStateSaver saver(dbg); - dbg.nospace(); - dbg << "QPdfDestination(page=" << dest.page() - << " location=" << dest.location() - << " zoom=" << dest.zoom(); - dbg << ')'; - return dbg; -} - -QT_END_NAMESPACE - -#include "moc_qpdfdestination.cpp" diff --git a/src/pdf/qpdfdestination.h b/src/pdf/qpdfdestination.h deleted file mode 100644 index f9c186ff6..000000000 --- a/src/pdf/qpdfdestination.h +++ /dev/null @@ -1,85 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtPDF module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL3$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPLv3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QPDFDESTINATION_H -#define QPDFDESTINATION_H - -#include <QtPdf/qtpdfglobal.h> -#include <QtCore/qdebug.h> -#include <QtCore/qobject.h> -#include <QtCore/qpoint.h> -#include <QtCore/qshareddata.h> - -QT_BEGIN_NAMESPACE - -class QPdfDestinationPrivate; - -class Q_PDF_EXPORT QPdfDestination -{ - Q_GADGET - Q_PROPERTY(bool valid READ isValid) - Q_PROPERTY(int page READ page) - Q_PROPERTY(QPointF location READ location) - Q_PROPERTY(qreal zoom READ zoom) - -public: - ~QPdfDestination(); - QPdfDestination(const QPdfDestination &other); - QPdfDestination &operator=(const QPdfDestination &other); - QPdfDestination(QPdfDestination &&other) noexcept; - QPdfDestination &operator=(QPdfDestination &&other) noexcept { swap(other); return *this; } - void swap(QPdfDestination &other) noexcept { d.swap(other.d); } - bool isValid() const; - int page() const; - QPointF location() const; - qreal zoom() const; - -protected: - QPdfDestination(); - QPdfDestination(int page, QPointF location, qreal zoom); - QPdfDestination(QPdfDestinationPrivate *d); - friend class QPdfDocument; - friend class QQuickPdfNavigationStack; - -protected: - QExplicitlySharedDataPointer<QPdfDestinationPrivate> d; -}; - -Q_PDF_EXPORT QDebug operator<<(QDebug, const QPdfDestination &); - -QT_END_NAMESPACE - -#endif // QPDFDESTINATION_H diff --git a/src/pdf/qpdfdestination_p.h b/src/pdf/qpdfdestination_p.h deleted file mode 100644 index 3520fb795..000000000 --- a/src/pdf/qpdfdestination_p.h +++ /dev/null @@ -1,71 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtPDF module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL3$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPLv3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QPDFDESTINATION_P_H -#define QPDFDESTINATION_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QPointF> - -QT_BEGIN_NAMESPACE - -class QPdfDestinationPrivate : public QSharedData -{ -public: - QPdfDestinationPrivate() = default; - QPdfDestinationPrivate(int page, QPointF location, qreal zoom) - : page(page), - location(location), - zoom(zoom) { } - - int page = -1; - QPointF location; - qreal zoom = 1; -}; - -QT_END_NAMESPACE - -#endif // QPDFDESTINATION_P_H diff --git a/src/pdf/qpdfdocument.cpp b/src/pdf/qpdfdocument.cpp index 8fd55dd57..17fdb29b9 100644 --- a/src/pdf/qpdfdocument.cpp +++ b/src/pdf/qpdfdocument.cpp @@ -1,38 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtPDF module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL3$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPLv3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qpdfdocument.h" #include "qpdfdocument_p.h" @@ -46,9 +13,13 @@ #include <QFile> #include <QHash> #include <QLoggingCategory> +#include <QMetaEnum> #include <QMutex> +#include <QPixmap> #include <QVector2D> +#include <QtCore/private/qtools_p.h> + QT_BEGIN_NAMESPACE Q_GLOBAL_STATIC(QRecursiveMutex, pdfMutex) @@ -61,12 +32,83 @@ QPdfMutexLocker::QPdfMutexLocker() { } +class Q_PDF_EXPORT QPdfPageModel : public QAbstractListModel +{ + Q_OBJECT +public: + QPdfPageModel(QPdfDocument *doc) : QAbstractListModel(doc) + { + m_roleNames = QAbstractItemModel::roleNames(); + QMetaEnum rolesMetaEnum = doc->metaObject()->enumerator(doc->metaObject()->indexOfEnumerator("PageModelRole")); + for (int r = Qt::UserRole; r < int(QPdfDocument::PageModelRole::NRoles); ++r) { + auto name = QByteArray(rolesMetaEnum.valueToKey(r)); + name[0] = QtMiscUtils::toAsciiLower(name[0]); + m_roleNames.insert(r, name); + } + connect(doc, &QPdfDocument::statusChanged, this, [this](QPdfDocument::Status s) { + if (s == QPdfDocument::Status::Loading) + beginResetModel(); + else if (s == QPdfDocument::Status::Ready) + endResetModel(); + }); + } + + QVariant data(const QModelIndex &index, int role) const override + { + if (!index.isValid()) + return QVariant(); + + switch (QPdfDocument::PageModelRole(role)) { + case QPdfDocument::PageModelRole::Label: + return document()->pageLabel(index.row()); + case QPdfDocument::PageModelRole::PointSize: + return document()->pagePointSize(index.row()); + case QPdfDocument::PageModelRole::NRoles: + break; + } + + switch (role) { + case Qt::DecorationRole: + return pageThumbnail(index.row()); + case Qt::DisplayRole: + return document()->pageLabel(index.row()); + } + + return QVariant(); + } + + int rowCount(const QModelIndex & = QModelIndex()) const override { return document()->pageCount(); } + + QHash<int, QByteArray> roleNames() const override { return m_roleNames; } + +private: + QPdfDocument *document() const { return static_cast<QPdfDocument *>(parent()); } + QPixmap pageThumbnail(int page) const + { + auto it = m_thumbnails.constFind(page); + if (it == m_thumbnails.constEnd()) { + auto doc = document(); + auto size = doc->pagePointSize(page); + size.scale(128, 128, Qt::KeepAspectRatio); + // TODO use QPdfPageRenderer for threading? + auto image = document()->render(page, size.toSize()); + QPixmap ret = QPixmap::fromImage(image); + m_thumbnails.insert(page, ret); + return ret; + } + return it.value(); + } + + QHash<int, QByteArray> m_roleNames; + mutable QHash<int, QPixmap> m_thumbnails; +}; + QPdfDocumentPrivate::QPdfDocumentPrivate() : avail(nullptr) , doc(nullptr) , loadComplete(false) - , status(QPdfDocument::Null) - , lastError(QPdfDocument::NoError) + , status(QPdfDocument::Status::Null) + , lastError(QPdfDocument::Error::None) , pageCount(0) { asyncBuffer.setData(QByteArray()); @@ -74,8 +116,12 @@ QPdfDocumentPrivate::QPdfDocumentPrivate() const QPdfMutexLocker lock; - if (libraryRefCount == 0) + if (libraryRefCount == 0) { + QElapsedTimer timer; + timer.start(); FPDF_InitLibrary(); + qCDebug(qLcDoc) << "FPDF_InitLibrary took" << timer.elapsed() << "ms"; + } ++libraryRefCount; // FPDF_FILEACCESS setup @@ -97,8 +143,10 @@ QPdfDocumentPrivate::~QPdfDocumentPrivate() const QPdfMutexLocker lock; - if (!--libraryRefCount) + if (!--libraryRefCount) { + qCDebug(qLcDoc) << "FPDF_DestroyLibrary"; FPDF_DestroyLibrary(); + } } void QPdfDocumentPrivate::clear() @@ -117,6 +165,7 @@ void QPdfDocumentPrivate::clear() if (pageCount != 0) { pageCount = 0; emit q->pageCountChanged(pageCount); + emit q->pageModelChanged(); } loadComplete = false; @@ -132,7 +181,7 @@ void QPdfDocumentPrivate::clear() void QPdfDocumentPrivate::updateLastError() { if (doc) { - lastError = QPdfDocument::NoError; + lastError = QPdfDocument::Error::None; return; } @@ -141,15 +190,17 @@ void QPdfDocumentPrivate::updateLastError() lock.unlock(); switch (error) { - case FPDF_ERR_SUCCESS: lastError = QPdfDocument::NoError; break; - case FPDF_ERR_UNKNOWN: lastError = QPdfDocument::UnknownError; break; - case FPDF_ERR_FILE: lastError = QPdfDocument::FileNotFoundError; break; - case FPDF_ERR_FORMAT: lastError = QPdfDocument::InvalidFileFormatError; break; - case FPDF_ERR_PASSWORD: lastError = QPdfDocument::IncorrectPasswordError; break; - case FPDF_ERR_SECURITY: lastError = QPdfDocument::UnsupportedSecuritySchemeError; break; + case FPDF_ERR_SUCCESS: lastError = QPdfDocument::Error::None; break; + case FPDF_ERR_UNKNOWN: lastError = QPdfDocument::Error::Unknown; break; + case FPDF_ERR_FILE: lastError = QPdfDocument::Error::FileNotFound; break; + case FPDF_ERR_FORMAT: lastError = QPdfDocument::Error::InvalidFileFormat; break; + case FPDF_ERR_PASSWORD: lastError = QPdfDocument::Error::IncorrectPassword; break; + case FPDF_ERR_SECURITY: lastError = QPdfDocument::Error::UnsupportedSecurityScheme; break; default: Q_UNREACHABLE(); } + if (lastError != QPdfDocument::Error::None) + qCDebug(qLcDoc) << "FPDF error" << error << "->" << lastError; } void QPdfDocumentPrivate::load(QIODevice *newDevice, bool transferDeviceOwnership) @@ -165,19 +216,19 @@ void QPdfDocumentPrivate::load(QIODevice *newDevice, bool transferDeviceOwnershi QNetworkReply *reply = qobject_cast<QNetworkReply*>(sequentialSourceDevice); if (!reply) { - setStatus(QPdfDocument::Error); + setStatus(QPdfDocument::Status::Error); qWarning() << "QPdfDocument: Loading from sequential devices only supported with QNetworkAccessManager."; return; } if (reply->isFinished() && reply->error() != QNetworkReply::NoError) { - setStatus(QPdfDocument::Error); + setStatus(QPdfDocument::Status::Error); return; } QObject::connect(reply, &QNetworkReply::finished, q, [this, reply](){ if (reply->error() != QNetworkReply::NoError || reply->bytesAvailable() == 0) { - this->setStatus(QPdfDocument::Error); + this->setStatus(QPdfDocument::Status::Error); } }); @@ -189,7 +240,7 @@ void QPdfDocumentPrivate::load(QIODevice *newDevice, bool transferDeviceOwnershi device = newDevice; initiateAsyncLoadWithTotalSizeKnown(device->size()); if (!avail) { - setStatus(QPdfDocument::Error); + setStatus(QPdfDocument::Status::Error); return; } @@ -198,7 +249,7 @@ void QPdfDocumentPrivate::load(QIODevice *newDevice, bool transferDeviceOwnershi if (!doc) { updateLastError(); - setStatus(QPdfDocument::Error); + setStatus(QPdfDocument::Status::Error); return; } @@ -208,15 +259,16 @@ void QPdfDocumentPrivate::load(QIODevice *newDevice, bool transferDeviceOwnershi if (newPageCount != pageCount) { pageCount = newPageCount; emit q->pageCountChanged(pageCount); + emit q->pageModelChanged(); } // If it's a local file, and the first couple of pages are available, // probably the whole document is available. if (checkPageComplete(0) && (pageCount < 2 || checkPageComplete(1))) { - setStatus(QPdfDocument::Ready); + setStatus(QPdfDocument::Status::Ready); } else { updateLastError(); - setStatus(QPdfDocument::Error); + setStatus(QPdfDocument::Status::Error); } } } @@ -228,13 +280,13 @@ void QPdfDocumentPrivate::_q_tryLoadingWithSizeFromContentHeader() const QNetworkReply *networkReply = qobject_cast<QNetworkReply*>(sequentialSourceDevice); if (!networkReply) { - setStatus(QPdfDocument::Error); + setStatus(QPdfDocument::Status::Error); return; } const QVariant contentLength = networkReply->header(QNetworkRequest::ContentLengthHeader); if (!contentLength.isValid()) { - setStatus(QPdfDocument::Error); + setStatus(QPdfDocument::Status::Error); return; } @@ -280,11 +332,10 @@ void QPdfDocumentPrivate::tryLoadDocument() break; case PDF_DATA_NOTAVAIL: qCDebug(qLcDoc) << "data not yet available"; - lastError = QPdfDocument::DataNotYetAvailableError; - setStatus(QPdfDocument::Error); + lastError = QPdfDocument::Error::DataNotYetAvailable; break; case PDF_DATA_AVAIL: - // all good + lastError = QPdfDocument::Error::None; break; } @@ -294,12 +345,14 @@ void QPdfDocumentPrivate::tryLoadDocument() lock.unlock(); updateLastError(); + if (lastError != QPdfDocument::Error::None) + setStatus(QPdfDocument::Status::Error); - if (lastError == QPdfDocument::IncorrectPasswordError) { + if (lastError == QPdfDocument::Error::IncorrectPassword) { FPDF_CloseDocument(doc); doc = nullptr; - setStatus(QPdfDocument::Error); + setStatus(QPdfDocument::Status::Error); emit q->passwordRequired(); } } @@ -336,9 +389,10 @@ void QPdfDocumentPrivate::checkComplete() if (newPageCount != pageCount) { pageCount = newPageCount; emit q->pageCountChanged(pageCount); + emit q->pageModelChanged(); } - setStatus(QPdfDocument::Ready); + setStatus(QPdfDocument::Status::Ready); } } @@ -392,7 +446,7 @@ void QPdfDocumentPrivate::fpdf_AddSegment(_FX_DOWNLOADHINTS *pThis, size_t offse Q_UNUSED(size); } -QString QPdfDocumentPrivate::getText(FPDF_TEXTPAGE textPage, int startIndex, int count) +QString QPdfDocumentPrivate::getText(FPDF_TEXTPAGE textPage, int startIndex, int count) const { QList<ushort> buf(count + 1); // TODO is that enough space in case one unicode character is more than one in utf-16? @@ -401,23 +455,73 @@ QString QPdfDocumentPrivate::getText(FPDF_TEXTPAGE textPage, int startIndex, int return QString::fromUtf16(reinterpret_cast<const char16_t *>(buf.constData()), len - 1); } -QPointF QPdfDocumentPrivate::getCharPosition(FPDF_TEXTPAGE textPage, double pageHeight, int charIndex) +QPointF QPdfDocumentPrivate::getCharPosition(FPDF_PAGE pdfPage, FPDF_TEXTPAGE textPage, int charIndex) const { double x, y; - int count = FPDFText_CountChars(textPage); - bool ok = FPDFText_GetCharOrigin(textPage, qMin(count - 1, charIndex), &x, &y); - if (!ok) - return QPointF(); - return QPointF(x, pageHeight - y); + const int count = FPDFText_CountChars(textPage); + if (FPDFText_GetCharOrigin(textPage, qMin(count - 1, charIndex), &x, &y)) + return mapPageToView(pdfPage, x, y); + return {}; } -QRectF QPdfDocumentPrivate::getCharBox(FPDF_TEXTPAGE textPage, double pageHeight, int charIndex) +QRectF QPdfDocumentPrivate::getCharBox(FPDF_PAGE pdfPage, FPDF_TEXTPAGE textPage, int charIndex) const { double l, t, r, b; - bool ok = FPDFText_GetCharBox(textPage, charIndex, &l, &r, &b, &t); - if (!ok) - return QRectF(); - return QRectF(l, pageHeight - t, r - l, t - b); + if (FPDFText_GetCharBox(textPage, charIndex, &l, &r, &b, &t)) + return mapPageToView(pdfPage, l, t, r, b); + return {}; +} + +/*! \internal + Convert the point \a x , \a y to the usual 1x (pixels = points) + 4th-quadrant "view" coordinate system relative to the top-left corner of + the rendered page. Some PDF files have internal transforms that make this + coordinate system different from "page coordinates", so we cannot just + subtract from page height to invert the y coordinates, in general. + */ +QPointF QPdfDocumentPrivate::mapPageToView(FPDF_PAGE pdfPage, double x, double y) const +{ + const auto pageHeight = FPDF_GetPageHeight(pdfPage); + const auto pageWidth = FPDF_GetPageWidth(pdfPage); + int rx, ry; + if (FPDF_PageToDevice(pdfPage, 0, 0, qRound(pageWidth), qRound(pageHeight), 0, x, y, &rx, &ry)) + return QPointF(rx, ry); + return {}; +} + +/*! \internal + Convert the bounding box defined by \a left \a top \a right and \a bottom + to the usual 1x (pixels = points) 4th-quadrant "view" coordinate system + that we use for rendering things on top of the page image. + Some PDF files have internal transforms that make this coordinate + system different from "page coordinates", so we cannot just + subtract from page height to invert the y coordinates, in general. + */ +QRectF QPdfDocumentPrivate::mapPageToView(FPDF_PAGE pdfPage, double left, double top, double right, double bottom) const +{ + const auto pageHeight = FPDF_GetPageHeight(pdfPage); + const auto pageWidth = FPDF_GetPageWidth(pdfPage); + int xfmLeft, xfmTop, xfmRight, xfmBottom; + if ( FPDF_PageToDevice(pdfPage, 0, 0, qRound(pageWidth), qRound(pageHeight), 0, left, top, &xfmLeft, &xfmTop) && + FPDF_PageToDevice(pdfPage, 0, 0, qRound(pageWidth), qRound(pageHeight), 0, right, bottom, &xfmRight, &xfmBottom) ) + return QRectF(xfmLeft, xfmTop, xfmRight - xfmLeft, xfmBottom - xfmTop); + return {}; +} + +/*! \internal + Convert the point \a x , \a y \a from the usual 1x (pixels = points) + 4th-quadrant "view" coordinate system relative to the top-left corner of + the rendered page, to "page coordinates" suited to the given \a pdfPage, + which may have arbitrary internal transforms. + */ +QPointF QPdfDocumentPrivate::mapViewToPage(FPDF_PAGE pdfPage, QPointF position) const +{ + const auto pageHeight = FPDF_GetPageHeight(pdfPage); + const auto pageWidth = FPDF_GetPageWidth(pdfPage); + double rx, ry; + if (FPDF_DeviceToPage(pdfPage, 0, 0, qRound(pageWidth), qRound(pageHeight), 0, position.x(), position.y(), &rx, &ry)) + return QPointF(rx, ry); + return {}; } QPdfDocumentPrivate::TextPosition QPdfDocumentPrivate::hitTest(int page, QPointF position) @@ -426,14 +530,14 @@ QPdfDocumentPrivate::TextPosition QPdfDocumentPrivate::hitTest(int page, QPointF TextPosition result; FPDF_PAGE pdfPage = FPDF_LoadPage(doc, page); - double pageHeight = FPDF_GetPageHeight(pdfPage); FPDF_TEXTPAGE textPage = FPDFText_LoadPage(pdfPage); - int hitIndex = FPDFText_GetCharIndexAtPos(textPage, position.x(), pageHeight - position.y(), + const QPointF pagePos = mapViewToPage(pdfPage, position); + int hitIndex = FPDFText_GetCharIndexAtPos(textPage, pagePos.x(), pagePos.y(), CharacterHitTolerance, CharacterHitTolerance); if (hitIndex >= 0) { - QPointF charPos = getCharPosition(textPage, pageHeight, hitIndex); + QPointF charPos = getCharPosition(pdfPage, textPage, hitIndex); if (!charPos.isNull()) { - QRectF charBox = getCharBox(textPage, pageHeight, hitIndex); + QRectF charBox = getCharBox(pdfPage, textPage, hitIndex); // If the given position is past the end of the line, i.e. if the right edge of the found character's // bounding box is closer to it than the left edge is, we say that we "hit" the next character index after if (qAbs(charBox.right() - position.x()) < qAbs(charPos.x() - position.x())) { @@ -476,24 +580,39 @@ QPdfDocument::~QPdfDocument() { } -QPdfDocument::DocumentError QPdfDocument::load(const QString &fileName) +/*! + Loads the document contents from \a fileName. +*/ +QPdfDocument::Error QPdfDocument::load(const QString &fileName) { qCDebug(qLcDoc) << "loading" << fileName; close(); - d->setStatus(QPdfDocument::Loading); + d->setStatus(QPdfDocument::Status::Loading); - QScopedPointer<QFile> f(new QFile(fileName)); + std::unique_ptr<QFile> f(new QFile(fileName)); if (!f->open(QIODevice::ReadOnly)) { - d->lastError = FileNotFoundError; - d->setStatus(QPdfDocument::Error); + d->lastError = Error::FileNotFound; + d->setStatus(QPdfDocument::Status::Error); } else { - d->load(f.take(), /*transfer ownership*/true); + d->load(f.release(), /*transfer ownership*/true); } return d->lastError; } +/*! \internal + Returns the filename of the document that has been opened, + or an empty string if no document is open. +*/ +QString QPdfDocument::fileName() const +{ + const QFile *f = qobject_cast<QFile *>(d->device.data()); + if (f) + return f->fileName(); + return QString(); +} + /*! \enum QPdfDocument::Status @@ -510,22 +629,35 @@ QPdfDocument::DocumentError QPdfDocument::load(const QString &fileName) */ /*! - Returns the current status of the document. + \property QPdfDocument::status + + This property holds the current status of the document. */ QPdfDocument::Status QPdfDocument::status() const { return d->status; } +/*! + Loads the document contents from \a device. +*/ void QPdfDocument::load(QIODevice *device) { close(); - d->setStatus(QPdfDocument::Loading); + d->setStatus(QPdfDocument::Status::Loading); d->load(device, /*transfer ownership*/false); } +/*! + \property QPdfDocument::password + + This property holds the document password. + + If the document is protected by a password, the user must provide it, and + the application must set this property. Otherwise, it's not needed. +*/ void QPdfDocument::setPassword(const QString &password) { const QByteArray newPassword = password.toUtf8(); @@ -570,53 +702,36 @@ QVariant QPdfDocument::metaData(MetaDataField field) const if (!d->doc) return QString(); + static QMetaEnum fieldsMetaEnum = metaObject()->enumerator(metaObject()->indexOfEnumerator("MetaDataField")); QByteArray fieldName; switch (field) { - case Title: - fieldName = "Title"; - break; - case Subject: - fieldName = "Subject"; - break; - case Author: - fieldName = "Author"; - break; - case Keywords: - fieldName = "Keywords"; - break; - case Producer: - fieldName = "Producer"; - break; - case Creator: - fieldName = "Creator"; - break; - case CreationDate: - fieldName = "CreationDate"; - break; - case ModificationDate: + case MetaDataField::ModificationDate: fieldName = "ModDate"; break; + default: + fieldName = QByteArray(fieldsMetaEnum.valueToKey(int(field))); + break; } QPdfMutexLocker lock; const unsigned long len = FPDF_GetMetaText(d->doc, fieldName.constData(), nullptr, 0); QList<ushort> buf(len); - FPDF_GetMetaText(d->doc, fieldName.constData(), buf.data(), buf.length()); + FPDF_GetMetaText(d->doc, fieldName.constData(), buf.data(), buf.size()); lock.unlock(); QString text = QString::fromUtf16(reinterpret_cast<const char16_t *>(buf.data())); switch (field) { - case Title: // fall through - case Subject: - case Author: - case Keywords: - case Producer: - case Creator: + case MetaDataField::Title: // fall through + case MetaDataField::Subject: + case MetaDataField::Author: + case MetaDataField::Keywords: + case MetaDataField::Producer: + case MetaDataField::Creator: return text; - case CreationDate: // fall through - case ModificationDate: + case MetaDataField::CreationDate: // fall through + case MetaDataField::ModificationDate: // convert a "D:YYYYMMDDHHmmSSOHH'mm'" into "YYYY-MM-DDTHH:mm:ss+HH:mm" if (text.startsWith(QLatin1String("D:"))) text = text.mid(2); @@ -635,20 +750,40 @@ QVariant QPdfDocument::metaData(MetaDataField field) const return QVariant(); } -QPdfDocument::DocumentError QPdfDocument::error() const +/*! + \enum QPdfDocument::Error + + This enum describes the error while attempting the last operation on the document. + + \value None No error occurred. + \value Unknown Unknown type of error. + \value DataNotYetAvailable The document is still loading, it's too early to attempt the operation. + \value FileNotFound The file given to load() was not found. + \value InvalidFileFormat The file given to load() is not a valid PDF file. + \value IncorrectPassword The password given to setPassword() is not correct for this file. + \value UnsupportedSecurityScheme QPdfDocument is not able to unlock this kind of PDF file. + + \sa QPdfDocument::error() +*/ + +/*! + Returns the type of error if \l status is \c Error, or \c NoError if there + is no error. +*/ +QPdfDocument::Error QPdfDocument::error() const { return d->lastError; } /*! - Closes the document. + Closes the document. */ void QPdfDocument::close() { if (!d->doc) return; - d->setStatus(Unloading); + d->setStatus(Status::Unloading); d->clear(); @@ -657,19 +792,24 @@ void QPdfDocument::close() emit passwordChanged(); } - d->setStatus(Null); + d->setStatus(Status::Null); } /*! - Returns the amount of pages for the loaded document or \c 0 if - no document is loaded. + \property QPdfDocument::pageCount + + This property holds the number of pages in the loaded document or \c 0 if + no document is loaded. */ int QPdfDocument::pageCount() const { return d->pageCount; } -QSizeF QPdfDocument::pageSize(int page) const +/*! + Returns the size of page \a page in points (1/72 of an inch). +*/ +QSizeF QPdfDocument::pagePointSize(int page) const { QSizeF result; if (!d->doc || !d->checkPageComplete(page)) @@ -682,6 +822,74 @@ QSizeF QPdfDocument::pageSize(int page) const } /*! + \enum QPdfDocument::PageModelRole + + Roles in pageModel(). + + \value Label The page number to be used for display purposes (QString). + \value PointSize The page size in points (1/72 of an inch) (QSizeF). + \omitvalue NRoles +*/ + +/*! + \property QPdfDocument::pageModel + + This property holds an instance of QAbstractListModel to provide + page-specific metadata, containing one row for each page in the document. + + \sa QPdfDocument::PageModelRole +*/ +QAbstractListModel *QPdfDocument::pageModel() +{ + if (!d->pageModel) + d->pageModel = new QPdfPageModel(this); + return d->pageModel; +} + +/*! + Returns the \a page number to be used for display purposes. + + For example, a document may have multiple sections with different numbering. + Perhaps the preface uses roman numerals, the body starts on page 1, and the + appendix starts at A1. Whenever a PDF viewer shows a page number, to avoid + confusing the user it should be the same "number" as is printed on the + corner of the page, rather than the zero-based page index that we use in + APIs (assuming the document author has made the page labels match the + printed numbers). + + If the document does not have custom page numbering, this function returns + \c {page + 1}. + + \sa pageIndexForLabel() +*/ +QString QPdfDocument::pageLabel(int page) +{ + const unsigned long len = FPDF_GetPageLabel(d->doc, page, nullptr, 0); + if (len == 0) + return QString::number(page + 1); + QList<char16_t> buf(len); + QPdfMutexLocker lock; + FPDF_GetPageLabel(d->doc, page, buf.data(), len); + lock.unlock(); + return QString::fromUtf16(buf.constData()); +} + +/*! + Returns the index of the page that has the \a label, or \c -1 if not found. + + \sa pageLabel() + \since 6.6 +*/ +int QPdfDocument::pageIndexForLabel(const QString &label) +{ + for (int i = 0; i < d->pageCount; ++i) { + if (pageLabel(i) == label) + return i; + } + return -1; +} + +/*! Renders the \a page into a QImage of size \a imageSize according to the provided \a renderOptions. @@ -709,37 +917,21 @@ QImage QPdfDocument::render(int page, QSize imageSize, QPdfDocumentRenderOptions result.fill(Qt::transparent); FPDF_BITMAP bitmap = FPDFBitmap_CreateEx(result.width(), result.height(), FPDFBitmap_BGRA, result.bits(), result.bytesPerLine()); - int rotation = 0; - switch (renderOptions.rotation()) { - case QPdf::Rotate0: - rotation = 0; - break; - case QPdf::Rotate90: - rotation = 1; - break; - case QPdf::Rotate180: - rotation = 2; - break; - case QPdf::Rotate270: - rotation = 3; - break; - } - - const QPdf::RenderFlags renderFlags = renderOptions.renderFlags(); + const QPdfDocumentRenderOptions::RenderFlags renderFlags = renderOptions.renderFlags(); int flags = 0; - if (renderFlags & QPdf::RenderAnnotations) + if (renderFlags & QPdfDocumentRenderOptions::RenderFlag::Annotations) flags |= FPDF_ANNOT; - if (renderFlags & QPdf::RenderOptimizedForLcd) + if (renderFlags & QPdfDocumentRenderOptions::RenderFlag::OptimizedForLcd) flags |= FPDF_LCD_TEXT; - if (renderFlags & QPdf::RenderGrayscale) + if (renderFlags & QPdfDocumentRenderOptions::RenderFlag::Grayscale) flags |= FPDF_GRAYSCALE; - if (renderFlags & QPdf::RenderForceHalftone) + if (renderFlags & QPdfDocumentRenderOptions::RenderFlag::ForceHalftone) flags |= FPDF_RENDER_FORCEHALFTONE; - if (renderFlags & QPdf::RenderTextAliased) + if (renderFlags & QPdfDocumentRenderOptions::RenderFlag::TextAliased) flags |= FPDF_RENDER_NO_SMOOTHTEXT; - if (renderFlags & QPdf::RenderImageAliased) + if (renderFlags & QPdfDocumentRenderOptions::RenderFlag::ImageAliased) flags |= FPDF_RENDER_NO_SMOOTHIMAGE; - if (renderFlags & QPdf::RenderPathAliased) + if (renderFlags & QPdfDocumentRenderOptions::RenderFlag::PathAliased) flags |= FPDF_RENDER_NO_SMOOTHPATH; if (renderOptions.scaledClipRect().isValid()) { @@ -752,7 +944,7 @@ QImage QPdfDocument::render(int page, QSize imageSize, QPdfDocumentRenderOptions float y1 = clipRect.bottom(); float x2 = clipRect.right(); float y2 = clipRect.top(); - QSizeF origSize = pageSize(page); + QSizeF origSize = pagePointSize(page); QVector2D pageScale(1, 1); if (!renderOptions.scaledSize().isNull()) { pageScale = QVector2D(renderOptions.scaledSize().width() / float(origSize.width()), @@ -770,6 +962,7 @@ QImage QPdfDocument::render(int page, QSize imageSize, QPdfDocumentRenderOptions qCDebug(qLcDoc) << "page" << page << "region" << renderOptions.scaledClipRect() << "size" << imageSize << "took" << timer.elapsed() << "ms"; } else { + const auto rotation = QPdfDocumentPrivate::toFPDFRotation(renderOptions.rotation()); FPDF_RenderPageBitmap(bitmap, pdfPage, 0, 0, result.width(), result.height(), rotation, flags); qCDebug(qLcDoc) << "page" << page << "size" << imageSize << "took" << timer.elapsed() << "ms"; } @@ -788,11 +981,12 @@ QPdfSelection QPdfDocument::getSelection(int page, QPointF start, QPointF end) { const QPdfMutexLocker lock; FPDF_PAGE pdfPage = FPDF_LoadPage(d->doc, page); - double pageHeight = FPDF_GetPageHeight(pdfPage); + const QPointF pageStart = d->mapViewToPage(pdfPage, start); + const QPointF pageEnd = d->mapViewToPage(pdfPage, end); FPDF_TEXTPAGE textPage = FPDFText_LoadPage(pdfPage); - int startIndex = FPDFText_GetCharIndexAtPos(textPage, start.x(), pageHeight - start.y(), + int startIndex = FPDFText_GetCharIndexAtPos(textPage, pageStart.x(), pageStart.y(), CharacterHitTolerance, CharacterHitTolerance); - int endIndex = FPDFText_GetCharIndexAtPos(textPage, end.x(), pageHeight - end.y(), + int endIndex = FPDFText_GetCharIndexAtPos(textPage, pageEnd.x(), pageEnd.y(), CharacterHitTolerance, CharacterHitTolerance); QPdfSelection result; @@ -803,7 +997,7 @@ QPdfSelection QPdfDocument::getSelection(int page, QPointF start, QPointF end) // If the given end position is past the end of the line, i.e. if the right edge of the last character's // bounding box is closer to it than the left edge is, then extend the char range by one - QRectF endCharBox = d->getCharBox(textPage, pageHeight, endIndex); + QRectF endCharBox = d->getCharBox(pdfPage, textPage, endIndex); if (qAbs(endCharBox.right() - end.x()) < qAbs(endCharBox.x() - end.x())) ++endIndex; @@ -815,7 +1009,7 @@ QPdfSelection QPdfDocument::getSelection(int page, QPointF start, QPointF end) for (int i = 0; i < rectCount; ++i) { double l, r, b, t; FPDFText_GetRect(textPage, i, &l, &t, &r, &b); - QRectF rect(l, pageHeight - t, r - l, t - b); + const QRectF rect = d->mapPageToView(pdfPage, l, t, r, b); if (hull.isNull()) hull = rect; else @@ -836,7 +1030,7 @@ QPdfSelection QPdfDocument::getSelection(int page, QPointF start, QPointF end) /*! Returns information about the text on the given \a page that can be found - beginning at the given \a startIndex with at most \l maxLength characters. + beginning at the given \a startIndex with at most \a maxLength characters. */ QPdfSelection QPdfDocument::getSelectionAtIndex(int page, int startIndex, int maxLength) { @@ -845,7 +1039,6 @@ QPdfSelection QPdfDocument::getSelectionAtIndex(int page, int startIndex, int ma return {}; const QPdfMutexLocker lock; FPDF_PAGE pdfPage = FPDF_LoadPage(d->doc, page); - double pageHeight = FPDF_GetPageHeight(pdfPage); FPDF_TEXTPAGE textPage = FPDFText_LoadPage(pdfPage); int pageCount = FPDFText_CountChars(textPage); if (startIndex >= pageCount) @@ -856,11 +1049,11 @@ QPdfSelection QPdfDocument::getSelectionAtIndex(int page, int startIndex, int ma QString text; if (maxLength > 0) { text = d->getText(textPage, startIndex, maxLength); - rectCount = FPDFText_CountRects(textPage, startIndex, text.length()); + rectCount = FPDFText_CountRects(textPage, startIndex, text.size()); for (int i = 0; i < rectCount; ++i) { double l, r, b, t; FPDFText_GetRect(textPage, i, &l, &t, &r, &b); - QRectF rect(l, pageHeight - t, r - l, t - b); + const QRectF rect = d->mapPageToView(pdfPage, l, t, r, b); if (hull.isNull()) hull = rect; else @@ -869,14 +1062,14 @@ QPdfSelection QPdfDocument::getSelectionAtIndex(int page, int startIndex, int ma } } if (bounds.isEmpty()) - hull = QRectF(d->getCharPosition(textPage, pageHeight, startIndex), QSizeF()); + hull = QRectF(d->getCharPosition(pdfPage, textPage, startIndex), QSizeF()); qCDebug(qLcDoc) << "on page" << page << "at index" << startIndex << "maxLength" << maxLength - << "got" << text.length() << "chars," << rectCount << "rects within" << hull; + << "got" << text.size() << "chars," << rectCount << "rects within" << hull; FPDFText_ClosePage(textPage); FPDF_ClosePage(pdfPage); - return QPdfSelection(text, bounds, hull, startIndex, startIndex + text.length()); + return QPdfSelection(text, bounds, hull, startIndex, startIndex + text.size()); } /*! @@ -886,7 +1079,6 @@ QPdfSelection QPdfDocument::getAllText(int page) { const QPdfMutexLocker lock; FPDF_PAGE pdfPage = FPDF_LoadPage(d->doc, page); - double pageHeight = FPDF_GetPageHeight(pdfPage); FPDF_TEXTPAGE textPage = FPDFText_LoadPage(pdfPage); int count = FPDFText_CountChars(textPage); if (count < 1) @@ -898,7 +1090,7 @@ QPdfSelection QPdfDocument::getAllText(int page) for (int i = 0; i < rectCount; ++i) { double l, r, b, t; FPDFText_GetRect(textPage, i, &l, &t, &r, &b); - QRectF rect(l, pageHeight - t, r - l, t - b); + const QRectF rect = d->mapPageToView(pdfPage, l, t, r, b); if (hull.isNull()) hull = rect; else @@ -915,4 +1107,5 @@ QPdfSelection QPdfDocument::getAllText(int page) QT_END_NAMESPACE +#include "qpdfdocument.moc" #include "moc_qpdfdocument.cpp" diff --git a/src/pdf/qpdfdocument.h b/src/pdf/qpdfdocument.h index 54ca687fa..8355246ae 100644 --- a/src/pdf/qpdfdocument.h +++ b/src/pdf/qpdfdocument.h @@ -1,38 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtPDF module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL3$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPLv3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QPDFDOCUMENT_H #define QPDFDOCUMENT_H @@ -40,6 +7,7 @@ #include <QtPdf/qtpdfglobal.h> #include <QtCore/qobject.h> +#include <QtCore/QAbstractListModel> #include <QtGui/qimage.h> #include <QtPdf/qpdfdocumentrenderoptions.h> #include <QtPdf/qpdfselection.h> @@ -56,9 +24,10 @@ class Q_PDF_EXPORT QPdfDocument : public QObject Q_PROPERTY(int pageCount READ pageCount NOTIFY pageCountChanged FINAL) Q_PROPERTY(QString password READ password WRITE setPassword NOTIFY passwordChanged FINAL) Q_PROPERTY(Status status READ status NOTIFY statusChanged FINAL) + Q_PROPERTY(QAbstractListModel* pageModel READ pageModel NOTIFY pageModelChanged FINAL) public: - enum Status { + enum class Status { Null, Loading, Ready, @@ -67,18 +36,18 @@ public: }; Q_ENUM(Status) - enum DocumentError { - NoError, - UnknownError, - DataNotYetAvailableError, - FileNotFoundError, - InvalidFileFormatError, - IncorrectPasswordError, - UnsupportedSecuritySchemeError + enum class Error { + None, + Unknown, + DataNotYetAvailable, + FileNotFound, + InvalidFileFormat, + IncorrectPassword, + UnsupportedSecurityScheme }; - Q_ENUM(DocumentError) + Q_ENUM(Error) - enum MetaDataField { + enum class MetaDataField { Title, Subject, Author, @@ -90,10 +59,18 @@ public: }; Q_ENUM(MetaDataField) - explicit QPdfDocument(QObject *parent = nullptr); - ~QPdfDocument(); + enum class PageModelRole { + Label = Qt::UserRole, + PointSize, + NRoles + }; + Q_ENUM(PageModelRole) + + QPdfDocument() : QPdfDocument(nullptr) {} + explicit QPdfDocument(QObject *parent); + ~QPdfDocument() override; - DocumentError load(const QString &fileName); + Error load(const QString &fileName); Status status() const; @@ -103,13 +80,18 @@ public: QVariant metaData(MetaDataField field) const; - DocumentError error() const; + Error error() const; void close(); int pageCount() const; - QSizeF pageSize(int page) const; + Q_INVOKABLE QSizeF pagePointSize(int page) const; + + Q_INVOKABLE QString pageLabel(int page); + Q_INVOKABLE int pageIndexForLabel(const QString &label); + + QAbstractListModel *pageModel(); QImage render(int page, QSize imageSize, QPdfDocumentRenderOptions options = QPdfDocumentRenderOptions()); @@ -122,14 +104,19 @@ Q_SIGNALS: void passwordRequired(); void statusChanged(QPdfDocument::Status status); void pageCountChanged(int pageCount); + void pageModelChanged(); private: - friend class QPdfBookmarkModelPrivate; + friend struct QPdfBookmarkModelPrivate; + friend class QPdfFile; friend class QPdfLinkModelPrivate; + friend class QPdfPageModel; friend class QPdfSearchModel; friend class QPdfSearchModelPrivate; friend class QQuickPdfSelection; + QString fileName() const; + Q_PRIVATE_SLOT(d, void _q_tryLoadingWithSizeFromContentHeader()) Q_PRIVATE_SLOT(d, void _q_copyFromSequentialSourceDevice()) QScopedPointer<QPdfDocumentPrivate> d; diff --git a/src/pdf/qpdfdocument_p.h b/src/pdf/qpdfdocument_p.h index b6ee2dfd0..cdb76d16f 100644 --- a/src/pdf/qpdfdocument_p.h +++ b/src/pdf/qpdfdocument_p.h @@ -1,38 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtPDF module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL3$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPLv3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QPDFDOCUMENT_P_H #define QPDFDOCUMENT_P_H @@ -49,6 +16,7 @@ // #include "qpdfdocument.h" +#include "qtpdfexports.h" #include "third_party/pdfium/public/fpdfview.h" #include "third_party/pdfium/public/fpdf_dataavail.h" @@ -68,13 +36,16 @@ public: QPdfMutexLocker(); }; -class Q_PDF_PRIVATE_EXPORT QPdfDocumentPrivate: public FPDF_FILEACCESS, public FX_FILEAVAIL, public FX_DOWNLOADHINTS +class QPdfPageModel; + +class Q_PDF_EXPORT QPdfDocumentPrivate: public FPDF_FILEACCESS, public FX_FILEAVAIL, public FX_DOWNLOADHINTS { public: QPdfDocumentPrivate(); ~QPdfDocumentPrivate(); QPdfDocument *q; + QPdfPageModel *pageModel = nullptr; FPDF_AVAIL avail; FPDF_DOCUMENT doc; @@ -87,7 +58,7 @@ public: QByteArray password; QPdfDocument::Status status; - QPdfDocument::DocumentError lastError; + QPdfDocument::Error lastError; int pageCount; void clear(); @@ -107,9 +78,37 @@ public: static int fpdf_GetBlock(void* param, unsigned long position, unsigned char* pBuf, unsigned long size); static void fpdf_AddSegment(struct _FX_DOWNLOADHINTS* pThis, size_t offset, size_t size); void updateLastError(); - QString getText(FPDF_TEXTPAGE textPage, int startIndex, int count); - QPointF getCharPosition(FPDF_TEXTPAGE textPage, double pageHeight, int charIndex); - QRectF getCharBox(FPDF_TEXTPAGE textPage, double pageHeight, int charIndex); + QString getText(FPDF_TEXTPAGE textPage, int startIndex, int count) const; + QPointF getCharPosition(FPDF_PAGE pdfPage, FPDF_TEXTPAGE textPage, int charIndex) const; + QRectF getCharBox(FPDF_PAGE pdfPage, FPDF_TEXTPAGE textPage, int charIndex) const; + QPointF mapPageToView(FPDF_PAGE pdfPage, double x, double y) const; + QRectF mapPageToView(FPDF_PAGE pdfPage, double left, double top, double right, double bottom) const; + QPointF mapViewToPage(FPDF_PAGE pdfPage, QPointF position) const; + + // FPDF takes the rotation parameter as an int. + // This enum is mapping the int values defined in fpdfview.h:956. + // (not using enum class to ensure int convertability) + enum QFPDFRotation { + Normal = 0, + ClockWise90 = 1, + ClockWise180 = 2, + CounterClockWise90 = 3 + }; + + static constexpr QFPDFRotation toFPDFRotation(QPdfDocumentRenderOptions::Rotation rotation) + { + switch (rotation) { + case QPdfDocumentRenderOptions::Rotation::None: + return QFPDFRotation::Normal; + case QPdfDocumentRenderOptions::Rotation::Clockwise90: + return QFPDFRotation::ClockWise90; + case QPdfDocumentRenderOptions::Rotation::Clockwise180: + return QFPDFRotation::ClockWise180; + case QPdfDocumentRenderOptions::Rotation::Clockwise270: + return QFPDFRotation::CounterClockWise90; + } + Q_UNREACHABLE(); + } struct TextPosition { QPointF position; diff --git a/src/pdf/qpdfdocumentrenderoptions.h b/src/pdf/qpdfdocumentrenderoptions.h index cafb4716f..af074d976 100644 --- a/src/pdf/qpdfdocumentrenderoptions.h +++ b/src/pdf/qpdfdocumentrenderoptions.h @@ -1,44 +1,11 @@ -/**************************************************************************** -** -** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com> -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtPDF module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL3$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPLv3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com> +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QPDFDOCUMENTRENDEROPTIONS_H #define QPDFDOCUMENTRENDEROPTIONS_H -#include <QtPdf/qpdfnamespace.h> +#include <QtPdf/qtpdfglobal.h> #include <QtCore/qobject.h> #include <QtCore/qrect.h> @@ -47,22 +14,41 @@ QT_BEGIN_NAMESPACE class QPdfDocumentRenderOptions { public: - Q_DECL_CONSTEXPR QPdfDocumentRenderOptions() noexcept : m_renderFlags(0), m_rotation(0), m_reserved(0) {} - - Q_DECL_CONSTEXPR QPdf::Rotation rotation() const noexcept { return static_cast<QPdf::Rotation>(m_rotation); } - Q_DECL_RELAXED_CONSTEXPR void setRotation(QPdf::Rotation r) noexcept { m_rotation = r; } - - Q_DECL_CONSTEXPR QPdf::RenderFlags renderFlags() const noexcept { return static_cast<QPdf::RenderFlags>(m_renderFlags); } - Q_DECL_RELAXED_CONSTEXPR void setRenderFlags(QPdf::RenderFlags r) noexcept { m_renderFlags = r; } - - Q_DECL_CONSTEXPR QRect scaledClipRect() const noexcept { return m_clipRect; } - Q_DECL_RELAXED_CONSTEXPR void setScaledClipRect(const QRect &r) noexcept { m_clipRect = r; } - - Q_DECL_CONSTEXPR QSize scaledSize() const noexcept { return m_scaledSize; } - Q_DECL_RELAXED_CONSTEXPR void setScaledSize(const QSize &s) noexcept { m_scaledSize = s; } + enum class Rotation { + None, + Clockwise90, + Clockwise180, + Clockwise270 + }; + + enum class RenderFlag { + None = 0x000, + Annotations = 0x001, + OptimizedForLcd = 0x002, + Grayscale = 0x004, + ForceHalftone = 0x008, + TextAliased = 0x010, + ImageAliased = 0x020, + PathAliased = 0x040 + }; + Q_DECLARE_FLAGS(RenderFlags, RenderFlag) + + constexpr QPdfDocumentRenderOptions() noexcept : m_renderFlags(0), m_rotation(0), m_reserved(0) {} + + constexpr Rotation rotation() const noexcept { return static_cast<Rotation>(m_rotation); } + constexpr void setRotation(Rotation r) noexcept { m_rotation = quint32(r); } + + constexpr RenderFlags renderFlags() const noexcept { return static_cast<RenderFlags>(m_renderFlags); } + constexpr void setRenderFlags(RenderFlags r) noexcept { m_renderFlags = quint32(r.toInt()); } + + constexpr QRect scaledClipRect() const noexcept { return m_clipRect; } + constexpr void setScaledClipRect(const QRect &r) noexcept { m_clipRect = r; } + + constexpr QSize scaledSize() const noexcept { return m_scaledSize; } + constexpr void setScaledSize(const QSize &s) noexcept { m_scaledSize = s; } private: - friend Q_DECL_CONSTEXPR inline bool operator==(QPdfDocumentRenderOptions lhs, QPdfDocumentRenderOptions rhs) noexcept; + friend constexpr inline bool operator==(const QPdfDocumentRenderOptions &lhs, const QPdfDocumentRenderOptions &rhs) noexcept; QRect m_clipRect; QSize m_scaledSize; @@ -74,15 +60,16 @@ private: }; Q_DECLARE_TYPEINFO(QPdfDocumentRenderOptions, Q_PRIMITIVE_TYPE); +Q_DECLARE_OPERATORS_FOR_FLAGS(QPdfDocumentRenderOptions::RenderFlags) -Q_DECL_CONSTEXPR inline bool operator==(QPdfDocumentRenderOptions lhs, QPdfDocumentRenderOptions rhs) noexcept +constexpr inline bool operator==(const QPdfDocumentRenderOptions &lhs, const QPdfDocumentRenderOptions &rhs) noexcept { return lhs.m_clipRect == rhs.m_clipRect && lhs.m_scaledSize == rhs.m_scaledSize && lhs.m_renderFlags == rhs.m_renderFlags && lhs.m_rotation == rhs.m_rotation && lhs.m_reserved == rhs.m_reserved && lhs.m_reserved2 == rhs.m_reserved2; // fix -Wunused-private-field } -Q_DECL_CONSTEXPR inline bool operator!=(QPdfDocumentRenderOptions lhs, QPdfDocumentRenderOptions rhs) noexcept +constexpr inline bool operator!=(const QPdfDocumentRenderOptions &lhs, const QPdfDocumentRenderOptions &rhs) noexcept { return !operator==(lhs, rhs); } diff --git a/src/pdf/qpdfdocumentrenderoptions.qdoc b/src/pdf/qpdfdocumentrenderoptions.qdoc index cc5083f9d..ad8e7bfdb 100644 --- a/src/pdf/qpdfdocumentrenderoptions.qdoc +++ b/src/pdf/qpdfdocumentrenderoptions.qdoc @@ -1,38 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com> -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtPDF module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL3$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPLv3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qpdfdocumentrenderoptions.h" @@ -49,13 +16,42 @@ QT_BEGIN_NAMESPACE */ /*! + \enum QPdfDocumentRenderOptions::Rotation + + This enum describes the rotation of the page for rendering. + + \value None Do not rotate (the default) + \value Clockwise90 Rotate 90 degrees clockwise + \value Clockwise180 Rotate 180 degrees + \value Clockwise270 Rotate 270 degrees clockwise + + \sa QPdfDocument::render() +*/ +/*! + \enum QPdfDocumentRenderOptions::RenderFlag + + This enum is used to describe how a page should be rendered. + + \value None The default value, representing no flags. + \value Annotations The page is rendered with annotations. + \value OptimizedForLcd The text of the page is rendered optimized for LCD display. + \value Grayscale The page is rendered grayscale. + \value ForceHalftone Always use halftones for rendering if the output image is stretched. + \value TextAliased Anti-aliasing is disabled for rendering text. + \value ImageAliased Anti-aliasing is disabled for rendering images. + \value PathAliased Anti-aliasing is disabled for rendering paths. + + \sa QPdfDocument::render() +*/ + +/*! \fn QPdfDocumentRenderOptions::QPdfDocumentRenderOptions() Constructs a QPdfDocumentRenderOptions object. */ /*! - \fn QPdf::Rotation QPdfDocumentRenderOptions::rotation() const + \fn QPdfDocumentRenderOptions::Rotation QPdfDocumentRenderOptions::rotation() const Returns the rotation used for rendering a page from a PDF document. @@ -63,7 +59,7 @@ QT_BEGIN_NAMESPACE */ /*! - \fn void QPdfDocumentRenderOptions::setRotation(QPdf::Rotation rotation) + \fn void QPdfDocumentRenderOptions::setRotation(QPdfDocumentRenderOptions::Rotation rotation) Sets the \a rotation used for rendering a page from a PDF document. @@ -71,7 +67,7 @@ QT_BEGIN_NAMESPACE */ /*! - \fn QPdf::RenderFlags QPdfDocumentRenderOptions::renderFlags() const + \fn QPdfDocumentRenderOptions::RenderFlags QPdfDocumentRenderOptions::renderFlags() const Returns the special flags used for rendering a page from a PDF document. @@ -79,7 +75,7 @@ QT_BEGIN_NAMESPACE */ /*! - \fn void QPdfDocumentRenderOptions::setRenderFlags(QPdf::RenderFlags flags) + \fn void QPdfDocumentRenderOptions::setRenderFlags(QPdfDocumentRenderOptions::RenderFlags flags) Sets the special \a flags used for rendering a page from a PDF document. @@ -96,10 +92,10 @@ QT_BEGIN_NAMESPACE */ /*! - \fn void QPdfDocumentRenderOptions::setScaledClipRect(QRect rect) + \fn void QPdfDocumentRenderOptions::setScaledClipRect(const QRect &r) - Sets the region \a rect to be clipped from the page after having been - scaled to \l scaledSize(). + Sets the rectangle region (\a r) to be clipped from the page after having + been scaled to \l scaledSize(). \sa scaledClipRect() */ @@ -107,15 +103,15 @@ QT_BEGIN_NAMESPACE /*! \fn QRect QPdfDocumentRenderOptions::scaledSize() const - Returns the \a size of the page to be rendered, in pixels. + Returns the size of the page to be rendered, in pixels. \sa setScaledSize() */ /*! - \fn void QPdfDocumentRenderOptions::setScaledSize(QSize size) + \fn void QPdfDocumentRenderOptions::setScaledSize(const QSize &s) - Sets the \a size of the page to be rendered, in pixels. + Sets the size (\a s) of the page to be rendered, in pixels. \sa scaledSize() */ diff --git a/src/pdf/qpdffile.cpp b/src/pdf/qpdffile.cpp new file mode 100644 index 000000000..a54f6a568 --- /dev/null +++ b/src/pdf/qpdffile.cpp @@ -0,0 +1,28 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qpdffile_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \internal + \class QPdfFile + \inmodule QtPdf + + QPdfFile is a means of passing a PDF file along with the associated + QPdfDocument together into QPdfIOHandler::load(QIODevice *device) so that + QPdfIOHandler does not need to construct its own redundant QPdfDocument + instance. If it succeeds in casting the QIODevice to a QPdfFile, it is + expected to use the QPdfDocument operations for all I/O, and thus the + normal QFile I/O functions are not needed for that use case. +*/ + +QPdfFile::QPdfFile(QPdfDocument *doc) + : QFile(doc->fileName()), m_document(doc) +{ +} + +QT_END_NAMESPACE + +//#include "moc_qpdffile_p.cpp" diff --git a/src/pdf/qpdffile_p.h b/src/pdf/qpdffile_p.h new file mode 100644 index 000000000..f678cdcdc --- /dev/null +++ b/src/pdf/qpdffile_p.h @@ -0,0 +1,37 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QPDFFILE_P_H +#define QPDFFILE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qpdfdocument.h" + +#include <QtCore/qfile.h> + +QT_BEGIN_NAMESPACE + +class Q_PDF_EXPORT QPdfFile : public QFile +{ + Q_OBJECT +public: + QPdfFile(QPdfDocument *doc); + QPdfDocument *document() { return m_document; } + +private: + QPdfDocument *m_document; +}; + +QT_END_NAMESPACE + +#endif // QPDFFILE_P_H diff --git a/src/pdf/qpdflink.cpp b/src/pdf/qpdflink.cpp new file mode 100644 index 000000000..0c2867086 --- /dev/null +++ b/src/pdf/qpdflink.cpp @@ -0,0 +1,189 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qpdflink.h" +#include "qpdflink_p.h" +#include "qpdflinkmodel_p.h" +#include <QGuiApplication> +#include <QDebug> + +QT_BEGIN_NAMESPACE + +/*! + \class QPdfLink + \since 6.4 + \inmodule QtPdf + + \brief The QPdfLink class defines a link between a region on a page + (such as a hyperlink or a search result) and a destination + (page, location on the page, and zoom level at which to view it). +*/ + +/*! + Constructs an invalid Destination. + + \sa valid +*/ +QPdfLink::QPdfLink() : + QPdfLink(new QPdfLinkPrivate()) { } + +QPdfLink::QPdfLink(int page, QPointF location, qreal zoom) + : QPdfLink(new QPdfLinkPrivate(page, location, zoom)) +{ +} + +QPdfLink::QPdfLink(int page, QList<QRectF> rects, + QString contextBefore, QString contextAfter) + : QPdfLink(new QPdfLinkPrivate(page, std::move(rects), + std::move(contextBefore), + std::move(contextAfter))) +{ +} + +QPdfLink::QPdfLink(QPdfLinkPrivate *d) : d(d) {} + +QPdfLink::~QPdfLink() = default; +QPdfLink::QPdfLink(const QPdfLink &other) noexcept = default; +QPdfLink::QPdfLink(QPdfLink &&other) noexcept = default; +QPdfLink &QPdfLink::operator=(const QPdfLink &other) = default; + +/*! + \property QPdfLink::valid + + This property holds whether the link is valid. +*/ +bool QPdfLink::isValid() const +{ + return d->page >= 0; +} + +/*! + \property QPdfLink::page + + This property holds the page number. + If the link is a search result, it is the page number on which the result is found; + if the link is a hyperlink, it is the destination page number. +*/ +int QPdfLink::page() const +{ + return d->page; +} + +/*! + \property QPdfLink::location + + This property holds the location on the \l page, in units of points. + If the link is a search result, it is the location where the result is found; + if the link is a hyperlink, it is the destination location. +*/ +QPointF QPdfLink::location() const +{ + return d->location; +} + +/*! + \property QPdfLink::zoom + + This property holds the suggested magnification level, where 1.0 means default scale + (1 pixel = 1 point). If the link is a search result, this value is not used. +*/ +qreal QPdfLink::zoom() const +{ + return d->zoom; +} + +/*! + \property QPdfLink::url + + This property holds the destination URL if the link is an external hyperlink; + otherwise, it's empty. +*/ +QUrl QPdfLink::url() const +{ + return d->url; +} + +/*! + \property QPdfLink::contextBefore + + This property holds adjacent text found on the page before the search string. + If the link is a hyperlink, this string is empty. + + \sa QPdfSearchModel::resultsOnPage(), QPdfSearchModel::resultAtIndex() +*/ +QString QPdfLink::contextBefore() const +{ + return d->contextBefore; +} + +/*! + \property QPdfLink::contextAfter + + This property holds adjacent text found on the page after the search string. + If the link is a hyperlink, this string is empty. + + \sa QPdfSearchModel::resultsOnPage(), QPdfSearchModel::resultAtIndex() +*/ +QString QPdfLink::contextAfter() const +{ + return d->contextAfter; +} + +/*! + \property QPdfLink::rectangles + + This property holds the region (set of rectangles) occupied by the link or + search result on the page where it was found. If the text wraps around to + multiple lines on the page, there may be multiple rectangles: + + \image wrapping-search-result.png + + \sa QPdfSearchModel::resultsOnPage(), QPdfSearchModel::resultAtIndex() +*/ +QList<QRectF> QPdfLink::rectangles() const +{ + return d->rects; +} + +/*! + Returns a translated representation for display. + + \sa copyToClipboard() +*/ +QString QPdfLink::toString() const +{ + if (d->page <= 0) + return d->url.toString(); + return QPdfLinkModel::tr("Page %1 location %2, %3 zoom %4") + .arg(d->page).arg(d->location.x(), 0, 'f', 1).arg(d->location.y(), 0, 'f', 1) + .arg(d->zoom, 0, 'f', 0); +} + +/*! + Copies the toString() representation of the link to the + \l {QGuiApplication::clipboard()}{system clipboard} depending on the \a mode given. +*/ +void QPdfLink::copyToClipboard(QClipboard::Mode mode) const +{ + QGuiApplication::clipboard()->setText(toString(), mode); +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QPdfLink &link) +{ + QDebugStateSaver saver(dbg); + dbg.nospace(); + dbg << "QPdfLink(page=" << link.page() + << " location=" << link.location() + << " zoom=" << link.zoom() + << " contextBefore=" << link.contextBefore() + << " contextAfter=" << link.contextAfter() + << " rects=" << link.rectangles(); + dbg << ')'; + return dbg; +} +#endif + +QT_END_NAMESPACE + +#include "moc_qpdflink.cpp" diff --git a/src/pdf/qpdflink.h b/src/pdf/qpdflink.h new file mode 100644 index 000000000..63389afe6 --- /dev/null +++ b/src/pdf/qpdflink.h @@ -0,0 +1,78 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QPDFLINK_H +#define QPDFLINK_H + +#include <QtPdf/qtpdfglobal.h> +#include <QtCore/qlist.h> +#include <QtCore/qobject.h> +#include <QtCore/qpoint.h> +#include <QtCore/qrect.h> +#include <QtCore/qshareddata.h> +#include <QtGui/qclipboard.h> + +QT_BEGIN_NAMESPACE + +class QDebug; +class QPdfLinkPrivate; + +class QPdfLink +{ + Q_GADGET_EXPORT(Q_PDF_EXPORT) + Q_PROPERTY(bool valid READ isValid) + Q_PROPERTY(int page READ page) + Q_PROPERTY(QPointF location READ location) + Q_PROPERTY(qreal zoom READ zoom) + Q_PROPERTY(QUrl url READ url) + Q_PROPERTY(QString contextBefore READ contextBefore) + Q_PROPERTY(QString contextAfter READ contextAfter) + Q_PROPERTY(QList<QRectF> rectangles READ rectangles) + +public: + Q_PDF_EXPORT QPdfLink(); + Q_PDF_EXPORT ~QPdfLink(); + Q_PDF_EXPORT QPdfLink &operator=(const QPdfLink &other); + + Q_PDF_EXPORT QPdfLink(const QPdfLink &other) noexcept; + Q_PDF_EXPORT QPdfLink(QPdfLink &&other) noexcept; + QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QPdfLink) + + void swap(QPdfLink &other) noexcept { d.swap(other.d); } + + Q_PDF_EXPORT bool isValid() const; + Q_PDF_EXPORT int page() const; + Q_PDF_EXPORT QPointF location() const; + Q_PDF_EXPORT qreal zoom() const; + Q_PDF_EXPORT QUrl url() const; + Q_PDF_EXPORT QString contextBefore() const; + Q_PDF_EXPORT QString contextAfter() const; + Q_PDF_EXPORT QList<QRectF> rectangles() const; + Q_PDF_EXPORT Q_INVOKABLE QString toString() const; + Q_PDF_EXPORT Q_INVOKABLE void copyToClipboard(QClipboard::Mode mode = QClipboard::Clipboard) const; + +private: // methods + QPdfLink(int page, QPointF location, qreal zoom); + QPdfLink(int page, QList<QRectF> rects, QString contextBefore, QString contextAfter); + QPdfLink(QPdfLinkPrivate *d); + friend class QPdfDocument; + friend class QPdfLinkModelPrivate; + friend class QPdfSearchModelPrivate; + friend class QPdfPageNavigator; + friend class QQuickPdfPageNavigator; + +private: // storage + QExplicitlySharedDataPointer<QPdfLinkPrivate> d; + +}; +Q_DECLARE_SHARED(QPdfLink) + +#ifndef QT_NO_DEBUG_STREAM +Q_PDF_EXPORT QDebug operator<<(QDebug, const QPdfLink &); +#endif + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QPdfLink) + +#endif // QPDFLINK_H diff --git a/src/pdf/qpdflink_p.h b/src/pdf/qpdflink_p.h new file mode 100644 index 000000000..fa82f47c3 --- /dev/null +++ b/src/pdf/qpdflink_p.h @@ -0,0 +1,53 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QPDFLINK_P_H +#define QPDFLINK_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qpdflink.h" + +#include <QPointF> +#include <QRectF> +#include <QUrl> + +QT_BEGIN_NAMESPACE + +class QPdfLinkPrivate : public QSharedData +{ +public: + QPdfLinkPrivate() = default; + QPdfLinkPrivate(int page, QPointF location, qreal zoom) + : page(page), + location(location), + zoom(zoom) { } + QPdfLinkPrivate(int page, QList<QRectF> rects, QString contextBefore, QString contextAfter) + : page(page), + location(rects.first().topLeft()), + zoom(0), + contextBefore{std::move(contextBefore)}, + contextAfter{std::move(contextAfter)}, + rects{std::move(rects)} {} + + int page = -1; + QPointF location; + qreal zoom = 1; + QString contextBefore; + QString contextAfter; + QUrl url; + QList<QRectF> rects; +}; + +QT_END_NAMESPACE + +#endif // QPDFLINK_P_H diff --git a/src/pdf/qpdflinkmodel.cpp b/src/pdf/qpdflinkmodel.cpp index 5c2596bb9..0a8b1e812 100644 --- a/src/pdf/qpdflinkmodel.cpp +++ b/src/pdf/qpdflinkmodel.cpp @@ -1,41 +1,9 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtPDF module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL3$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPLv3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +#include "qpdflink_p.h" +#include "qpdflinkmodel.h" #include "qpdflinkmodel_p.h" -#include "qpdflinkmodel_p_p.h" #include "qpdfdocument_p.h" #include "third_party/pdfium/public/fpdf_doc.h" @@ -48,44 +16,85 @@ QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(qLcLink, "qt.pdf.links") +/*! + \class QPdfLinkModel + \since 6.6 + \inmodule QtPdf + \inherits QAbstractListModel + + \brief The QPdfLinkModel class holds the geometry and the destination for + each link that the specified \l page contains. + + This is used in PDF viewers to implement the hyperlink mechanism. +*/ + +/*! + \enum QPdfLinkModel::Role + + \value Link A QPdfLink object. + \value Rectangle Bounding rectangle around the link. + \value Url If the link is a web link, the URL for that; otherwise an empty URL. + \value Page If the link is an internal link, the page number to which the link should jump; otherwise \c {-1}. + \value Location If the link is an internal link, the location on the page to which the link should jump. + \value Zoom If the link is an internal link, the suggested zoom level on the destination page. + \omitvalue NRoles +*/ + +/*! + Constructs a new link model with parent object \a parent. +*/ QPdfLinkModel::QPdfLinkModel(QObject *parent) - : QAbstractListModel(*(new QPdfLinkModelPrivate()), parent) + : QAbstractListModel(parent), + d_ptr{std::make_unique<QPdfLinkModelPrivate>(this)} { + Q_D(QPdfLinkModel); QMetaEnum rolesMetaEnum = metaObject()->enumerator(metaObject()->indexOfEnumerator("Role")); - for (int r = Qt::UserRole; r < int(Role::_Count); ++r) - m_roleNames.insert(r, QByteArray(rolesMetaEnum.valueToKey(r)).toLower()); + for (int r = Qt::UserRole; r < int(Role::NRoles); ++r) + d->roleNames.insert(r, QByteArray(rolesMetaEnum.valueToKey(r)).toLower()); } +/*! + Destroys the model. +*/ QPdfLinkModel::~QPdfLinkModel() {} QHash<int, QByteArray> QPdfLinkModel::roleNames() const { - return m_roleNames; + Q_D(const QPdfLinkModel); + return d->roleNames; } +/*! + \reimp +*/ int QPdfLinkModel::rowCount(const QModelIndex &parent) const { Q_D(const QPdfLinkModel); Q_UNUSED(parent); - return d->links.count(); + return d->links.size(); } +/*! + \reimp +*/ QVariant QPdfLinkModel::data(const QModelIndex &index, int role) const { Q_D(const QPdfLinkModel); - const QPdfLinkModelPrivate::Link &link = d->links.at(index.row()); + const auto &link = d->links.at(index.row()); switch (Role(role)) { - case Role::Rect: - return link.rect; + case Role::Link: + return QVariant::fromValue(link); + case Role::Rectangle: + return link.rectangles().empty() ? QVariant() : link.rectangles().constFirst(); case Role::Url: - return link.url; + return link.url(); case Role::Page: - return link.page; + return link.page(); case Role::Location: - return link.location; + return link.location(); case Role::Zoom: - return link.zoom; - case Role::_Count: + return link.zoom(); + case Role::NRoles: break; } if (role == Qt::DisplayRole) @@ -93,6 +102,10 @@ QVariant QPdfLinkModel::data(const QModelIndex &index, int role) const return QVariant(); } +/*! + \property QPdfLinkModel::document + \brief The document to load links from. +*/ QPdfDocument *QPdfLinkModel::document() const { Q_D(const QPdfLinkModel); @@ -115,6 +128,10 @@ void QPdfLinkModel::setDocument(QPdfDocument *document) d->update(); } +/*! + \property QPdfLinkModel::page + \brief The page to load links from. +*/ int QPdfLinkModel::page() const { Q_D(const QPdfLinkModel); @@ -132,8 +149,22 @@ void QPdfLinkModel::setPage(int page) d->update(); } -QPdfLinkModelPrivate::QPdfLinkModelPrivate() : QAbstractItemModelPrivate() +/*! + Returns a \l {QPdfLink::isValid()}{valid} link if found under the \a point + (given in units of points, 1/72 of an inch), or an invalid link if it is + not found. In other words, this function is useful for picking, to handle + mouse click or hover. +*/ +QPdfLink QPdfLinkModel::linkAt(QPointF point) const { + Q_D(const QPdfLinkModel); + for (const auto &link : std::as_const(d->links)) { + for (const auto &rect : link.rectangles()) { + if (rect.contains(point)) + return link; + } + } + return {}; } void QPdfLinkModelPrivate::update() @@ -148,7 +179,6 @@ void QPdfLinkModelPrivate::update() qCWarning(qLcLink) << "failed to load page" << page; return; } - double pageHeight = FPDF_GetPageHeight(pdfPage); q->beginResetModel(); links.clear(); @@ -166,42 +196,68 @@ void QPdfLinkModelPrivate::update() qCWarning(qLcLink) << "skipping link with invalid bounding box"; continue; // while enumerating links } - Link linkData; - linkData.rect = QRectF(rect.left, pageHeight - rect.top, - rect.right - rect.left, rect.top - rect.bottom); + // In case horizontal/vertical coordinates are flipped, swap them. + if (rect.right < rect.left) + std::swap(rect.right, rect.left); + if (rect.bottom > rect.top) + std::swap(rect.bottom, rect.top); + + QPdfLink linkData; + // Use quad points if present; otherwise use the rect. + if (int quadPointsCount = FPDFLink_CountQuadPoints(linkAnnot) > 0) { + for (int i = 0; i < quadPointsCount; ++i) { + FS_QUADPOINTSF point; + if (FPDFLink_GetQuadPoints(linkAnnot, i, &point)) { + // Quadpoints are counter clockwise from bottom left (x1, y1) + QPolygonF poly; + poly << QPointF(point.x1, point.y1); + poly << QPointF(point.x2, point.y2); + poly << QPointF(point.x3, point.y3); + poly << QPointF(point.x4, point.y4); + QRectF bounds = poly.boundingRect(); + bounds = document->d->mapPageToView(pdfPage, bounds.left(), bounds.top(), bounds.right(), bounds.bottom()); + qCDebug(qLcLink) << "quadpoints" << i << "of" << quadPointsCount << ":" << poly << "mapped bounds" << bounds; + linkData.d->rects << bounds; + // QPdfLink could store polygons rather than rects, to get the benefit of quadpoints; + // so far we didn't bother. It would be an API change, and we'd need to use Shapes in PdfLinkDelegate.qml + } + } + } else { + linkData.d->rects << document->d->mapPageToView(pdfPage, rect.left, rect.top, rect.right, rect.bottom); + } FPDF_DEST dest = FPDFLink_GetDest(doc, linkAnnot); FPDF_ACTION action = FPDFLink_GetAction(linkAnnot); switch (FPDFAction_GetType(action)) { case PDFACTION_UNSUPPORTED: // this happens with valid links in some PDFs case PDFACTION_GOTO: { - linkData.page = FPDFDest_GetDestPageIndex(doc, dest); - if (linkData.page < 0) { - qCWarning(qLcLink) << "skipping link with invalid page number"; + linkData.d->page = FPDFDest_GetDestPageIndex(doc, dest); + if (linkData.d->page < 0) { + qCWarning(qLcLink) << "skipping link with invalid page number" << linkData.d->page; continue; // while enumerating links } FPDF_BOOL hasX, hasY, hasZoom; FS_FLOAT x, y, zoom; ok = FPDFDest_GetLocationInPage(dest, &hasX, &hasY, &hasZoom, &x, &y, &zoom); if (!ok) { - qCWarning(qLcLink) << "link with invalid location and/or zoom @" << linkData.rect; + qCWarning(qLcLink) << "link with invalid location and/or zoom @" << linkData.d->rects; break; // at least we got a page number, so the link will jump there } if (hasX && hasY) - linkData.location = QPointF(x, pageHeight - y); + linkData.d->location = document->d->mapPageToView(pdfPage, x, y); if (hasZoom) - linkData.zoom = zoom; + linkData.d->zoom = zoom; break; } case PDFACTION_URI: { unsigned long len = FPDFAction_GetURIPath(doc, action, nullptr, 0); if (len < 1) { - qCWarning(qLcLink) << "skipping link with empty URI @" << linkData.rect; + qCWarning(qLcLink) << "skipping link with empty URI @" << linkData.d->rects; continue; // while enumerating links } else { QByteArray buf(len, 0); unsigned long got = FPDFAction_GetURIPath(doc, action, buf.data(), len); Q_ASSERT(got == len); - linkData.url = QString::fromLatin1(buf.data(), got - 1); + linkData.d->url = QString::fromLatin1(buf.data(), got - 1); } break; } @@ -209,13 +265,13 @@ void QPdfLinkModelPrivate::update() case PDFACTION_REMOTEGOTO: { unsigned long len = FPDFAction_GetFilePath(action, nullptr, 0); if (len < 1) { - qCWarning(qLcLink) << "skipping link with empty file path @" << linkData.rect; + qCWarning(qLcLink) << "skipping link with empty file path @" << linkData.d->rects; continue; // while enumerating links } else { QByteArray buf(len, 0); unsigned long got = FPDFAction_GetFilePath(action, buf.data(), len); Q_ASSERT(got == len); - linkData.url = QUrl::fromLocalFile(QString::fromLatin1(buf.data(), got - 1)).toString(); + linkData.d->url = QUrl::fromLocalFile(QString::fromLatin1(buf.data(), got - 1)).toString(); // Unfortunately, according to comments in fpdf_doc.h, if it's PDFACTION_REMOTEGOTO, // we can't get the page and location without first opening the linked document @@ -234,7 +290,7 @@ void QPdfLinkModelPrivate::update() if (webLinks) { int count = FPDFLink_CountWebLinks(webLinks); for (int i = 0; i < count; ++i) { - Link linkData; + QPdfLink linkData; int len = FPDFLink_GetURL(webLinks, i, nullptr, 0); if (len < 1) { qCWarning(qLcLink) << "skipping link" << i << "with empty URL"; @@ -242,16 +298,15 @@ void QPdfLinkModelPrivate::update() QList<unsigned short> buf(len); int got = FPDFLink_GetURL(webLinks, i, buf.data(), len); Q_ASSERT(got == len); - linkData.url = QString::fromUtf16( + linkData.d->url = QString::fromUtf16( reinterpret_cast<const char16_t *>(buf.data()), got - 1); } - FPDFLink_GetTextRange(webLinks, i, &linkData.textStart, &linkData.textCharCount); len = FPDFLink_CountRects(webLinks, i); for (int r = 0; r < len; ++r) { double left, top, right, bottom; bool success = FPDFLink_GetRect(webLinks, i, r, &left, &top, &right, &bottom); if (success) { - linkData.rect = QRectF(left, pageHeight - top, right - left, top - bottom); + linkData.d->rects << document->d->mapPageToView(pdfPage, left, top, right, bottom); links << linkData; } } @@ -264,8 +319,8 @@ void QPdfLinkModelPrivate::update() // All done FPDF_ClosePage(pdfPage); if (Q_UNLIKELY(qLcLink().isDebugEnabled())) { - for (const Link &l : links) - qCDebug(qLcLink) << l.rect << l.toString(); + for (const auto &l : links) + qCDebug(qLcLink) << l; } q->endResetModel(); } @@ -274,21 +329,10 @@ void QPdfLinkModel::onStatusChanged(QPdfDocument::Status status) { Q_D(QPdfLinkModel); qCDebug(qLcLink) << "sees document statusChanged" << status; - if (status == QPdfDocument::Ready) + if (status == QPdfDocument::Status::Ready) d->update(); } -QString QPdfLinkModelPrivate::Link::toString() const -{ - QString ret; - if (page >= 0) - return QLatin1String("page ") + QString::number(page) + - QLatin1String(" location ") + QString::number(location.x()) + QLatin1Char(',') + QString::number(location.y()) + - QLatin1String(" zoom ") + QString::number(zoom); - else - return url.toString(); -} - QT_END_NAMESPACE #include "moc_qpdflinkmodel_p.cpp" diff --git a/src/pdf/qpdflinkmodel.h b/src/pdf/qpdflinkmodel.h new file mode 100644 index 000000000..be2ce890c --- /dev/null +++ b/src/pdf/qpdflinkmodel.h @@ -0,0 +1,67 @@ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QPDFLINKMODEL_H +#define QPDFLINKMODEL_H + +#include <QtPdf/qtpdfglobal.h> +#include <QtPdf/qpdfdocument.h> +#include <QtPdf/qpdflink.h> + +#include <QtCore/QAbstractListModel> + +#include <memory> + +QT_BEGIN_NAMESPACE + +class QPdfLinkModelPrivate; + +class Q_PDF_EXPORT QPdfLinkModel : public QAbstractListModel +{ + Q_OBJECT + Q_PROPERTY(QPdfDocument *document READ document WRITE setDocument NOTIFY documentChanged) + Q_PROPERTY(int page READ page WRITE setPage NOTIFY pageChanged) + +public: + enum class Role { + Link = Qt::UserRole, + Rectangle, + Url, + Page, + Location, + Zoom, + NRoles + }; + Q_ENUM(Role) + explicit QPdfLinkModel(QObject *parent = nullptr); + ~QPdfLinkModel() override; + + QPdfDocument *document() const; + + QHash<int, QByteArray> roleNames() const override; + int rowCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role) const override; + + int page() const; + + QPdfLink linkAt(QPointF point) const; + +public Q_SLOTS: + void setDocument(QPdfDocument *document); + void setPage(int page); + +Q_SIGNALS: + void documentChanged(); + void pageChanged(int page); + +private Q_SLOTS: + void onStatusChanged(QPdfDocument::Status status); + +private: + Q_DECLARE_PRIVATE(QPdfLinkModel) + const std::unique_ptr<QPdfLinkModelPrivate> d_ptr; +}; + +QT_END_NAMESPACE + +#endif // QPDFLINKMODEL_H diff --git a/src/pdf/qpdflinkmodel_p.h b/src/pdf/qpdflinkmodel_p.h index cf9c0aad4..ba46a6e00 100644 --- a/src/pdf/qpdflinkmodel_p.h +++ b/src/pdf/qpdflinkmodel_p.h @@ -1,38 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtPDF module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL3$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPLv3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QPDFLINKMODEL_P_H #define QPDFLINKMODEL_P_H @@ -48,57 +15,26 @@ // We mean it. // -#include "qtpdfglobal.h" -#include "qpdfdocument.h" - -#include <QObject> -#include <QAbstractListModel> +#include "qpdflinkmodel.h" +#include <private/qabstractitemmodel_p.h> QT_BEGIN_NAMESPACE -class QPdfLinkModelPrivate; - -class Q_PDF_EXPORT QPdfLinkModel : public QAbstractListModel +class QPdfLinkModelPrivate { - Q_OBJECT - Q_PROPERTY(QPdfDocument *document READ document WRITE setDocument NOTIFY documentChanged) - Q_PROPERTY(int page READ page WRITE setPage NOTIFY pageChanged) + QPdfLinkModel *q_ptr; + Q_DECLARE_PUBLIC(QPdfLinkModel) public: - enum class Role : int { - Rect = Qt::UserRole, - Url, - Page, - Location, - Zoom, - _Count - }; - Q_ENUM(Role) - explicit QPdfLinkModel(QObject *parent = nullptr); - ~QPdfLinkModel(); - - QPdfDocument *document() const; - - QHash<int, QByteArray> roleNames() const override; - int rowCount(const QModelIndex &parent) const override; - QVariant data(const QModelIndex &index, int role) const override; - - int page() const; - -public Q_SLOTS: - void setDocument(QPdfDocument *document); - void setPage(int page); - -Q_SIGNALS: - void documentChanged(); - void pageChanged(int page); + explicit QPdfLinkModelPrivate(QPdfLinkModel *qq) + : q_ptr(qq) {} -private Q_SLOTS: - void onStatusChanged(QPdfDocument::Status status); + void update(); -private: - QHash<int, QByteArray> m_roleNames; - Q_DECLARE_PRIVATE(QPdfLinkModel) + QHash<int, QByteArray> roleNames; + QPdfDocument *document = nullptr; + QList<QPdfLink> links; + int page = 0; }; QT_END_NAMESPACE diff --git a/src/pdf/qpdflinkmodel_p_p.h b/src/pdf/qpdflinkmodel_p_p.h deleted file mode 100644 index 0606b4746..000000000 --- a/src/pdf/qpdflinkmodel_p_p.h +++ /dev/null @@ -1,91 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtPDF module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL3$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPLv3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QPDFLINKMODEL_P_P_H -#define QPDFLINKMODEL_P_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qpdflinkmodel_p.h" -#include <private/qabstractitemmodel_p.h> - -#include "third_party/pdfium/public/fpdfview.h" - -#include <QUrl> - -QT_BEGIN_NAMESPACE - -class QPdfLinkModelPrivate: public QAbstractItemModelPrivate -{ - Q_DECLARE_PUBLIC(QPdfLinkModel) - -public: - QPdfLinkModelPrivate(); - - void update(); - - struct Link { - // where it is on the current page - QRectF rect; - int textStart = -1; - int textCharCount = 0; - // destination inside PDF - int page = -1; // -1 means look at the url instead - QPointF location; - qreal zoom = 0; // 0 means no specified zoom: don't change when clicking - // web destination - QUrl url; - - QString toString() const; - }; - - QPdfDocument *document = nullptr; - QList<Link> links; - int page = 0; -}; - -QT_END_NAMESPACE - -#endif // QPDFLINKMODEL_P_P_H diff --git a/src/pdf/qpdfnamespace.h b/src/pdf/qpdfnamespace.h deleted file mode 100644 index e76d0abd9..000000000 --- a/src/pdf/qpdfnamespace.h +++ /dev/null @@ -1,73 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com> -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtPDF module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL3$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPLv3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QPDFNAMESPACE_H -#define QPDFNAMESPACE_H - -#include <QtCore/qobject.h> - -QT_BEGIN_NAMESPACE - -namespace QPdf { - Q_NAMESPACE - - enum Rotation { - Rotate0, - Rotate90, - Rotate180, - Rotate270 - }; - Q_ENUM_NS(Rotation) - - enum RenderFlag { - NoRenderFlags = 0x000, - RenderAnnotations = 0x001, - RenderOptimizedForLcd = 0x002, - RenderGrayscale = 0x004, - RenderForceHalftone = 0x008, - RenderTextAliased = 0x010, - RenderImageAliased = 0x020, - RenderPathAliased = 0x040 - }; - Q_FLAG_NS(RenderFlag) - Q_DECLARE_FLAGS(RenderFlags, RenderFlag) -} - -Q_DECLARE_OPERATORS_FOR_FLAGS(QPdf::RenderFlags) - -QT_END_NAMESPACE -#endif diff --git a/src/pdf/qpdfnamespace.qdoc b/src/pdf/qpdfnamespace.qdoc deleted file mode 100644 index 96bb090e9..000000000 --- a/src/pdf/qpdfnamespace.qdoc +++ /dev/null @@ -1,74 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com> -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtPDF module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL3$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPLv3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -/*! - \namespace QPdf - \inmodule QtPdf - \keyword QPdf Namespace - - \brief The QPdf namespace contains miscellaneous identifiers - used throughout the QtPdf module. -*/ - -/*! - \enum QPdf::Rotation - - This enum describes the rotation of the page for rendering. - - \value Rotate0 Do not rotate (the default) - \value Rotate90 Rotate 90 degrees clockwise - \value Rotate180 Rotate 180 degrees - \value Rotate270 Rotate 270 degrees clockwise - - \sa QPdfDocument::render() -*/ -/*! - \enum QPdf::RenderFlag - - This enum is used to describe how a page should be rendered. - - \value NoRenderFlags The default value, representing no flags. - \value RenderAnnotations The page is rendered with annotations. - \value RenderOptimizedForLcd The text of the page is rendered optimized for LCD display. - \value RenderGrayscale The page is rendered grayscale. - \value RenderForceHalftone Always use halftones for rendering if the output image is stretched. - \value RenderTextAliased Anti-aliasing is disabled for rendering text. - \value RenderImageAliased Anti-aliasing is disabled for rendering images. - \value RenderPathAliased Anti-aliasing is disabled for rendering paths. - - \sa QPdfDocument::render() -*/ - diff --git a/src/pdf/qpdfpagenavigation.cpp b/src/pdf/qpdfpagenavigation.cpp deleted file mode 100644 index 031b38962..000000000 --- a/src/pdf/qpdfpagenavigation.cpp +++ /dev/null @@ -1,290 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com> -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtPDF module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL3$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPLv3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qpdfpagenavigation.h" - -#include "qpdfdocument.h" - -#include <private/qobject_p.h> - -#include <QPointer> - -QT_BEGIN_NAMESPACE - -class QPdfPageNavigationPrivate -{ -public: - QPdfPageNavigationPrivate(QPdfPageNavigation *q) : q_ptr(q) { } - - void update() - { - const bool documentAvailable = m_document && m_document->status() == QPdfDocument::Ready; - - if (documentAvailable) { - const int newPageCount = m_document->pageCount(); - if (m_pageCount != newPageCount) { - m_pageCount = newPageCount; - emit q_ptr->pageCountChanged(m_pageCount); - } - } else { - if (m_pageCount != 0) { - m_pageCount = 0; - emit q_ptr->pageCountChanged(m_pageCount); - } - } - - if (m_currentPage != 0) { - m_currentPage = 0; - emit q_ptr->currentPageChanged(m_currentPage); - } - - updatePrevNext(); - } - - void updatePrevNext() - { - const bool hasPreviousPage = m_currentPage > 0; - const bool hasNextPage = m_currentPage < (m_pageCount - 1); - - if (m_canGoToPreviousPage != hasPreviousPage) { - m_canGoToPreviousPage = hasPreviousPage; - emit q_ptr->canGoToPreviousPageChanged(m_canGoToPreviousPage); - } - - if (m_canGoToNextPage != hasNextPage) { - m_canGoToNextPage = hasNextPage; - emit q_ptr->canGoToNextPageChanged(m_canGoToNextPage); - } - } - - void documentStatusChanged() - { - update(); - } - - QPdfPageNavigation *q_ptr = nullptr; - QPointer<QPdfDocument> m_document = nullptr; - int m_currentPage = 0; - int m_pageCount = 0; - bool m_canGoToPreviousPage = false; - bool m_canGoToNextPage = false; - - QMetaObject::Connection m_documentStatusChangedConnection; -}; - -/*! - \class QPdfPageNavigation - \since 5.10 - \inmodule QtPdf - - \brief The QPdfPageNavigation class handles the navigation through a PDF document. - - \sa QPdfDocument -*/ - - -/*! - Constructs a page navigation object with parent object \a parent. -*/ -QPdfPageNavigation::QPdfPageNavigation(QObject *parent) - : QObject(parent), d_ptr(new QPdfPageNavigationPrivate(this)) -{ -} - -/*! - Destroys the page navigation object. -*/ -QPdfPageNavigation::~QPdfPageNavigation() -{ -} - -/*! - \property QPdfPageNavigation::document - \brief The document instance on which this object navigates. - - By default, this property is \c nullptr. - - \sa document(), setDocument(), QPdfDocument -*/ - -/*! - Returns the document on which this object navigates, or a \c nullptr - if none has set before. - - \sa QPdfDocument -*/ -QPdfDocument* QPdfPageNavigation::document() const -{ - return d_ptr->m_document; -} - -/*! - Sets the \a document this object navigates on. - - After a new document has been set, the currentPage will be \c 0. - - \sa QPdfDocument -*/ -void QPdfPageNavigation::setDocument(QPdfDocument *document) -{ - if (d_ptr->m_document == document) - return; - - if (d_ptr->m_document) - disconnect(d_ptr->m_documentStatusChangedConnection); - - d_ptr->m_document = document; - emit documentChanged(d_ptr->m_document); - - if (d_ptr->m_document) - d_ptr->m_documentStatusChangedConnection = - connect(d_ptr->m_document.data(), &QPdfDocument::statusChanged, this, - [this]() { d_ptr->documentStatusChanged(); }); - - d_ptr->update(); -} - -/*! - \property QPdfPageNavigation::currentPage - \brief The current page number in the document. - - \sa currentPage(), setCurrentPage() -*/ - -/*! - Returns the current page number or \c 0 if there is no document set. - - After a document has been loaded, the currentPage will always be \c 0. -*/ -int QPdfPageNavigation::currentPage() const -{ - return d_ptr->m_currentPage; -} - -/*! - \fn void QPdfPageNavigation::setCurrentPage(int page) - - Sets the current \a page number. -*/ -void QPdfPageNavigation::setCurrentPage(int newPage) -{ - if (newPage < 0 || newPage >= d_ptr->m_pageCount) - return; - - if (d_ptr->m_currentPage == newPage) - return; - - d_ptr->m_currentPage = newPage; - emit currentPageChanged(d_ptr->m_currentPage); - - d_ptr->updatePrevNext(); -} - -/*! - \property QPdfPageNavigation::pageCount - \brief The number of pages in the document. - - \sa pageCount() -*/ - -/*! - Returns the number of pages in the document or \c 0 if there - is no document set. -*/ -int QPdfPageNavigation::pageCount() const -{ - return d_ptr->m_pageCount; -} - -/*! - \property QPdfPageNavigation::canGoToPreviousPage - \brief Indicates whether there is a page before the current page. - - \sa canGoToPreviousPage(), goToPreviousPage() -*/ - -/*! - Returns whether there is a page before the current one. -*/ -bool QPdfPageNavigation::canGoToPreviousPage() const -{ - return d_ptr->m_canGoToPreviousPage; -} - -/*! - \property QPdfPageNavigation::canGoToNextPage - \brief Indicates whether there is a page after the current page. - - \sa canGoToNextPage(), goToNextPage() -*/ - -/*! - Returns whether there is a page after the current one. -*/ -bool QPdfPageNavigation::canGoToNextPage() const -{ - return d_ptr->m_canGoToNextPage; -} - -/*! - Changes the current page to the previous page. - - If there is no previous page in the document, nothing happens. - - \sa canGoToPreviousPage -*/ -void QPdfPageNavigation::goToPreviousPage() -{ - if (d_ptr->m_currentPage > 0) - setCurrentPage(d_ptr->m_currentPage - 1); -} - -/*! - Changes the current page to the next page. - - If there is no next page in the document, nothing happens. - - \sa canGoToNextPage -*/ -void QPdfPageNavigation::goToNextPage() -{ - if (d_ptr->m_currentPage < d_ptr->m_pageCount - 1) - setCurrentPage(d_ptr->m_currentPage + 1); -} - -QT_END_NAMESPACE - -#include "moc_qpdfpagenavigation.cpp" diff --git a/src/pdf/qpdfpagenavigation.h b/src/pdf/qpdfpagenavigation.h deleted file mode 100644 index dc412c33b..000000000 --- a/src/pdf/qpdfpagenavigation.h +++ /dev/null @@ -1,92 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com> -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtPDF module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL3$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPLv3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QPDFPAGENAVIGATION_H -#define QPDFPAGENAVIGATION_H - -#include <QtPdf/qtpdfglobal.h> -#include <QtCore/qobject.h> - -QT_BEGIN_NAMESPACE - -class QPdfDocument; -class QPdfPageNavigationPrivate; - -class Q_PDF_EXPORT QPdfPageNavigation : public QObject -{ - Q_OBJECT - - Q_PROPERTY(QPdfDocument* document READ document WRITE setDocument NOTIFY documentChanged) - - Q_PROPERTY(int currentPage READ currentPage WRITE setCurrentPage NOTIFY currentPageChanged) - Q_PROPERTY(int pageCount READ pageCount NOTIFY pageCountChanged) - Q_PROPERTY(bool canGoToPreviousPage READ canGoToPreviousPage NOTIFY canGoToPreviousPageChanged) - Q_PROPERTY(bool canGoToNextPage READ canGoToNextPage NOTIFY canGoToNextPageChanged) - -public: - explicit QPdfPageNavigation(QObject *parent = nullptr); - ~QPdfPageNavigation(); - - QPdfDocument* document() const; - void setDocument(QPdfDocument *document); - - int currentPage() const; - void setCurrentPage(int currentPage); - - int pageCount() const; - - bool canGoToPreviousPage() const; - bool canGoToNextPage() const; - -public Q_SLOTS: - void goToPreviousPage(); - void goToNextPage(); - -Q_SIGNALS: - void documentChanged(QPdfDocument *document); - void currentPageChanged(int currentPage); - void pageCountChanged(int pageCount); - void canGoToPreviousPageChanged(bool canGo); - void canGoToNextPageChanged(bool canGo); - -private: - QScopedPointer<QPdfPageNavigationPrivate> d_ptr; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/pdf/qpdfpagenavigator.cpp b/src/pdf/qpdfpagenavigator.cpp new file mode 100644 index 000000000..e077e2184 --- /dev/null +++ b/src/pdf/qpdfpagenavigator.cpp @@ -0,0 +1,362 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qpdfpagenavigator.h" +#include "qpdfdocument.h" +#include "qpdflink_p.h" + +#include <QtCore/qloggingcategory.h> +#include <QtCore/qpointer.h> + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(qLcNav, "qt.pdf.pagenavigator") + +struct QPdfPageNavigatorPrivate +{ + QPdfPageNavigator *q = nullptr; + + QList<QExplicitlySharedDataPointer<QPdfLinkPrivate>> pageHistory; + int currentHistoryIndex = 0; + bool changing = false; +}; + +/*! + \class QPdfPageNavigator + \since 6.4 + \inmodule QtPdf + \brief Navigation history within a PDF document. + + The QPdfPageNavigator class remembers which destinations the user + has visited in a PDF document, and provides the ability to traverse + backward and forward. It is used to implement back and forward actions + similar to the back and forward buttons in a web browser. + + \sa QPdfDocument +*/ + +/*! + Constructs a page navigation stack with parent object \a parent. +*/ +QPdfPageNavigator::QPdfPageNavigator(QObject *parent) + : QObject(parent), d(new QPdfPageNavigatorPrivate) +{ + d->q = this; + clear(); +} + +/*! + Destroys the page navigation stack. +*/ +QPdfPageNavigator::~QPdfPageNavigator() +{ +} + +/*! + Goes back to the page, location and zoom level that was being viewed before + back() was called, and then emits the \l jumped() signal. + + If a new destination was pushed since the last time \l back() was called, + the forward() function does nothing, because there is a branch in the + timeline which causes the "future" to be lost. +*/ +void QPdfPageNavigator::forward() +{ + if (d->currentHistoryIndex >= d->pageHistory.size() - 1) + return; + const bool backAvailableWas = backAvailable(); + const bool forwardAvailableWas = forwardAvailable(); + QPointF currentLocationWas = currentLocation(); + qreal currentZoomWas = currentZoom(); + ++d->currentHistoryIndex; + d->changing = true; + emit jumped(currentLink()); + if (currentZoomWas != currentZoom()) + emit currentZoomChanged(currentZoom()); + emit currentPageChanged(currentPage()); + if (currentLocationWas != currentLocation()) + emit currentLocationChanged(currentLocation()); + if (!backAvailableWas) + emit backAvailableChanged(backAvailable()); + if (forwardAvailableWas != forwardAvailable()) + emit forwardAvailableChanged(forwardAvailable()); + d->changing = false; + qCDebug(qLcNav) << "forward: index" << d->currentHistoryIndex << "page" << currentPage() + << "@" << currentLocation() << "zoom" << currentZoom(); +} + +/*! + Pops the stack, updates the \l currentPage, \l currentLocation and + \l currentZoom properties to the most-recently-viewed destination, and then + emits the \l jumped() signal. +*/ +void QPdfPageNavigator::back() +{ + if (d->currentHistoryIndex <= 0) + return; + const bool backAvailableWas = backAvailable(); + const bool forwardAvailableWas = forwardAvailable(); + QPointF currentLocationWas = currentLocation(); + qreal currentZoomWas = currentZoom(); + --d->currentHistoryIndex; + d->changing = true; + emit jumped(currentLink()); + if (currentZoomWas != currentZoom()) + emit currentZoomChanged(currentZoom()); + emit currentPageChanged(currentPage()); + if (currentLocationWas != currentLocation()) + emit currentLocationChanged(currentLocation()); + if (backAvailableWas != backAvailable()) + emit backAvailableChanged(backAvailable()); + if (!forwardAvailableWas) + emit forwardAvailableChanged(forwardAvailable()); + d->changing = false; + qCDebug(qLcNav) << "back: index" << d->currentHistoryIndex << "page" << currentPage() + << "@" << currentLocation() << "zoom" << currentZoom(); +} +/*! + \property QPdfPageNavigator::currentPage + + This property holds the current page that is being viewed. + The default is \c 0. +*/ +int QPdfPageNavigator::currentPage() const +{ + if (d->currentHistoryIndex < 0 || d->currentHistoryIndex >= d->pageHistory.size()) + return -1; // only until ctor or clear() runs + return d->pageHistory.at(d->currentHistoryIndex)->page; +} + +/*! + \property QPdfPageNavigator::currentLocation + + This property holds the current location on the page that is being viewed + (the location that was last given to jump() or update()). The default is + \c {0, 0}. +*/ +QPointF QPdfPageNavigator::currentLocation() const +{ + if (d->currentHistoryIndex < 0 || d->currentHistoryIndex >= d->pageHistory.size()) + return QPointF(); + return d->pageHistory.at(d->currentHistoryIndex)->location; +} + +/*! + \property QPdfPageNavigator::currentZoom + + This property holds the magnification scale (1 logical pixel = 1 point) + on the page that is being viewed. The default is \c 1. +*/ +qreal QPdfPageNavigator::currentZoom() const +{ + if (d->currentHistoryIndex < 0 || d->currentHistoryIndex >= d->pageHistory.size()) + return 1; + return d->pageHistory.at(d->currentHistoryIndex)->zoom; +} + +QPdfLink QPdfPageNavigator::currentLink() const +{ + if (d->currentHistoryIndex < 0 || d->currentHistoryIndex >= d->pageHistory.size()) + return QPdfLink(); + return QPdfLink(d->pageHistory.at(d->currentHistoryIndex).data()); +} + +/*! + Clear the history and restore \l currentPage, \l currentLocation and + \l currentZoom to their default values. +*/ +void QPdfPageNavigator::clear() +{ + d->pageHistory.clear(); + d->currentHistoryIndex = 0; + // Begin with an implicit jump to page 0, so that + // backAvailable() will become true after jump() is called one more time. + d->pageHistory.append(QExplicitlySharedDataPointer<QPdfLinkPrivate>(new QPdfLinkPrivate(0, {}, 1))); +} + +/*! + Adds the given \a destination to the history of visited locations. + + In this case, PDF views respond to the \l jumped signal by scrolling to + place \c destination.rectangles in the viewport, as opposed to placing + \c destination.location in the viewport. So it's appropriate to call this + method to jump to a search result from QPdfSearchModel (because the + rectangles cover the region of text found). To jump to a hyperlink + destination, call jump(page, location, zoom) instead, because in that + case the QPdfLink object's \c rectangles cover the hyperlink origin + location rather than the destination. +*/ +void QPdfPageNavigator::jump(QPdfLink destination) +{ + const bool zoomChange = !qFuzzyCompare(destination.zoom(), currentZoom()); + const bool pageChange = (destination.page() != currentPage()); + const bool locationChange = (destination.location() != currentLocation()); + const bool backAvailableWas = backAvailable(); + const bool forwardAvailableWas = forwardAvailable(); + if (!d->changing) { + if (d->currentHistoryIndex >= 0 && forwardAvailableWas) + d->pageHistory.remove(d->currentHistoryIndex + 1, d->pageHistory.size() - d->currentHistoryIndex - 1); + d->pageHistory.append(destination.d); + d->currentHistoryIndex = d->pageHistory.size() - 1; + } + if (zoomChange) + emit currentZoomChanged(currentZoom()); + if (pageChange) + emit currentPageChanged(currentPage()); + if (locationChange) + emit currentLocationChanged(currentLocation()); + if (d->changing) + return; + if (backAvailableWas != backAvailable()) + emit backAvailableChanged(backAvailable()); + if (forwardAvailableWas != forwardAvailable()) + emit forwardAvailableChanged(forwardAvailable()); + emit jumped(currentLink()); + qCDebug(qLcNav) << "push: index" << d->currentHistoryIndex << destination << "-> history" << + [this]() { + QStringList ret; + for (auto d : d->pageHistory) + ret << QString::number(d->page); + return ret.join(QLatin1Char(',')); + }(); +} + +/*! + Adds the given destination, consisting of \a page, \a location, and \a zoom, + to the history of visited locations. + + The \a zoom argument represents magnification (where \c 1 is the default + scale, 1 logical pixel = 1 point). If \a zoom is not given or is \c 0, + currentZoom keeps its existing value, and currentZoomChanged is not emitted. + + The \a location should be the same as QPdfLink::location() if the user is + following a link; and since that is specified as the upper-left corner of + the destination, it is best for consistency to always use the location + visible in the upper-left corner of the viewport, in points. + + If forwardAvailable is \c true, calling this function represents a branch + in the timeline which causes the "future" to be lost, and therefore + forwardAvailable will change to \c false. +*/ +void QPdfPageNavigator::jump(int page, const QPointF &location, qreal zoom) +{ + if (page == currentPage() && location == currentLocation() && zoom == currentZoom()) + return; + if (qFuzzyIsNull(zoom)) + zoom = currentZoom(); + const bool zoomChange = !qFuzzyCompare(zoom, currentZoom()); + const bool pageChange = (page != currentPage()); + const bool locationChange = (location != currentLocation()); + const bool backAvailableWas = backAvailable(); + const bool forwardAvailableWas = forwardAvailable(); + if (!d->changing) { + if (d->currentHistoryIndex >= 0 && forwardAvailableWas) + d->pageHistory.remove(d->currentHistoryIndex + 1, d->pageHistory.size() - d->currentHistoryIndex - 1); + d->pageHistory.append(QExplicitlySharedDataPointer<QPdfLinkPrivate>(new QPdfLinkPrivate(page, location, zoom))); + d->currentHistoryIndex = d->pageHistory.size() - 1; + } + if (zoomChange) + emit currentZoomChanged(currentZoom()); + if (pageChange) + emit currentPageChanged(currentPage()); + if (locationChange) + emit currentLocationChanged(currentLocation()); + if (d->changing) + return; + if (backAvailableWas != backAvailable()) + emit backAvailableChanged(backAvailable()); + if (forwardAvailableWas != forwardAvailable()) + emit forwardAvailableChanged(forwardAvailable()); + emit jumped(currentLink()); + qCDebug(qLcNav) << "push: index" << d->currentHistoryIndex << "page" << page + << "@" << location << "zoom" << zoom << "-> history" << + [this]() { + QStringList ret; + for (auto d : d->pageHistory) + ret << QString::number(d->page); + return ret.join(QLatin1Char(',')); + }(); +} + +/*! + Modifies the current destination, consisting of \a page, \a location and \a zoom. + + This can be called periodically while the user is manually moving around + the document, so that after back() is called, forward() will jump back to + the most-recently-viewed destination rather than the destination that was + last specified by push(). + + The \c currentZoomChanged, \c currentPageChanged and \c currentLocationChanged + signals will be emitted if the respective properties are actually changed. + The \l jumped signal is not emitted, because this operation represents + smooth movement rather than a navigational jump. +*/ +void QPdfPageNavigator::update(int page, const QPointF &location, qreal zoom) +{ + if (d->currentHistoryIndex < 0 || d->currentHistoryIndex >= d->pageHistory.size()) + return; + int currentPageWas = currentPage(); + QPointF currentLocationWas = currentLocation(); + qreal currentZoomWas = currentZoom(); + if (page == currentPageWas && location == currentLocationWas && zoom == currentZoomWas) + return; + d->pageHistory[d->currentHistoryIndex]->page = page; + d->pageHistory[d->currentHistoryIndex]->location = location; + d->pageHistory[d->currentHistoryIndex]->zoom = zoom; + if (currentZoomWas != zoom) + emit currentZoomChanged(currentZoom()); + if (currentPageWas != page) + emit currentPageChanged(currentPage()); + if (currentLocationWas != location) + emit currentLocationChanged(currentLocation()); + qCDebug(qLcNav) << "update: index" << d->currentHistoryIndex << "page" << page + << "@" << location << "zoom" << zoom << "-> history" << + [this]() { + QStringList ret; + for (auto d : d->pageHistory) + ret << QString::number(d->page); + return ret.join(QLatin1Char(',')); + }(); +} + +/*! + \property QPdfPageNavigator::backAvailable + \readonly + + Holds \c true if a \e back destination is available in the history: + that is, if push() or forward() has been called. +*/ +bool QPdfPageNavigator::backAvailable() const +{ + return d->currentHistoryIndex > 0; +} + +/*! + \property QPdfPageNavigator::forwardAvailable + \readonly + + Holds \c true if a \e forward destination is available in the history: + that is, if back() has been previously called. +*/ +bool QPdfPageNavigator::forwardAvailable() const +{ + return d->currentHistoryIndex < d->pageHistory.size() - 1; +} + +/*! + \fn void QPdfPageNavigator::jumped(QPdfLink current) + + This signal is emitted when an abrupt jump occurs, to the \a current + page index, location on the page, and zoom level; but \e not when simply + scrolling through the document one page at a time. That is, jump(), + forward() and back() emit this signal, but update() does not. + + If \c {current.rectangles.length > 0}, they are rectangles that cover + a specific destination area: a search result that should be made + visible; otherwise, \c {current.location} is the destination location on + the \c page (a hyperlink destination, or during forward/back navigation). +*/ + +QT_END_NAMESPACE + +#include "moc_qpdfpagenavigator.cpp" diff --git a/src/pdf/qpdfpagenavigator.h b/src/pdf/qpdfpagenavigator.h new file mode 100644 index 000000000..cec89ef5a --- /dev/null +++ b/src/pdf/qpdfpagenavigator.h @@ -0,0 +1,62 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QPDFPAGENAVIGATOR_H +#define QPDFPAGENAVIGATOR_H + +#include <QtPdf/qtpdfglobal.h> +#include <QtPdf/qpdflink.h> +#include <QtCore/qobject.h> + +QT_BEGIN_NAMESPACE + +struct QPdfPageNavigatorPrivate; + +class Q_PDF_EXPORT QPdfPageNavigator : public QObject +{ + Q_OBJECT + + Q_PROPERTY(int currentPage READ currentPage NOTIFY currentPageChanged) + Q_PROPERTY(QPointF currentLocation READ currentLocation NOTIFY currentLocationChanged) + Q_PROPERTY(qreal currentZoom READ currentZoom NOTIFY currentZoomChanged) + Q_PROPERTY(bool backAvailable READ backAvailable NOTIFY backAvailableChanged) + Q_PROPERTY(bool forwardAvailable READ forwardAvailable NOTIFY forwardAvailableChanged) + +public: + QPdfPageNavigator() : QPdfPageNavigator(nullptr) {} + explicit QPdfPageNavigator(QObject *parent); + ~QPdfPageNavigator() override; + + int currentPage() const; + QPointF currentLocation() const; + qreal currentZoom() const; + + bool backAvailable() const; + bool forwardAvailable() const; + +public Q_SLOTS: + void clear(); + void jump(QPdfLink destination); + void jump(int page, const QPointF &location, qreal zoom = 0); + void update(int page, const QPointF &location, qreal zoom); + void forward(); + void back(); + +Q_SIGNALS: + void currentPageChanged(int page); + void currentLocationChanged(QPointF location); + void currentZoomChanged(qreal zoom); + void backAvailableChanged(bool available); + void forwardAvailableChanged(bool available); + void jumped(QPdfLink current); + +protected: + QPdfLink currentLink() const; + +private: + QScopedPointer<QPdfPageNavigatorPrivate> d; +}; + +QT_END_NAMESPACE + +#endif // QPDFPAGENAVIGATOR_H diff --git a/src/pdf/qpdfpagerenderer.cpp b/src/pdf/qpdfpagerenderer.cpp index 8be0cfa21..771fc67ef 100644 --- a/src/pdf/qpdfpagerenderer.cpp +++ b/src/pdf/qpdfpagerenderer.cpp @@ -1,38 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com> -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtPDF module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL3$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPLv3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qpdfpagerenderer.h" @@ -122,7 +89,7 @@ void RenderWorker::requestPage(quint64 requestId, int pageNumber, QSize imageSiz { const QMutexLocker locker(&m_mutex); - if (!m_document || m_document->status() != QPdfDocument::Ready) + if (!m_document || m_document->status() != QPdfDocument::Status::Ready) return; const QImage image = m_document->render(pageNumber, imageSize, options); @@ -312,10 +279,10 @@ void QPdfPageRenderer::setDocument(QPdfDocument *document) quint64 QPdfPageRenderer::requestPage(int pageNumber, QSize imageSize, QPdfDocumentRenderOptions options) { - if (!d_ptr->m_document || d_ptr->m_document->status() != QPdfDocument::Ready) + if (!d_ptr->m_document || d_ptr->m_document->status() != QPdfDocument::Status::Ready) return 0; - for (const auto &request : qAsConst(d_ptr->m_pendingRequests)) { + for (const auto &request : std::as_const(d_ptr->m_pendingRequests)) { if (request.pageNumber == pageNumber && request.imageSize == imageSize && request.options == options) @@ -340,3 +307,4 @@ quint64 QPdfPageRenderer::requestPage(int pageNumber, QSize imageSize, QT_END_NAMESPACE #include "qpdfpagerenderer.moc" +#include "moc_qpdfpagerenderer.cpp" diff --git a/src/pdf/qpdfpagerenderer.h b/src/pdf/qpdfpagerenderer.h index faeaef8af..cb9be06fe 100644 --- a/src/pdf/qpdfpagerenderer.h +++ b/src/pdf/qpdfpagerenderer.h @@ -1,39 +1,6 @@ -/**************************************************************************** -** -** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com> -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtPDF module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL3$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPLv3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com> +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QPDFPAGERENDERER_H #define QPDFPAGERENDERER_H @@ -64,7 +31,8 @@ public: }; Q_ENUM(RenderMode) - explicit QPdfPageRenderer(QObject *parent = nullptr); + QPdfPageRenderer() : QPdfPageRenderer(nullptr) {} + explicit QPdfPageRenderer(QObject *parent); ~QPdfPageRenderer() override; RenderMode renderMode() const; @@ -78,7 +46,7 @@ public: Q_SIGNALS: void documentChanged(QPdfDocument *document); - void renderModeChanged(RenderMode renderMode); + void renderModeChanged(QPdfPageRenderer::RenderMode renderMode); void pageRendered(int pageNumber, QSize imageSize, const QImage &image, QPdfDocumentRenderOptions options, quint64 requestId); diff --git a/src/pdf/qpdfsearchmodel.cpp b/src/pdf/qpdfsearchmodel.cpp index 4d4e86cee..a81ae77dc 100644 --- a/src/pdf/qpdfsearchmodel.cpp +++ b/src/pdf/qpdfsearchmodel.cpp @@ -1,47 +1,13 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtPDF module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL3$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPLv3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qpdfdestination.h" +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + #include "qpdfdocument_p.h" +#include "qpdflink.h" #include "qpdfsearchmodel.h" #include "qpdfsearchmodel_p.h" -#include "qpdfsearchresult_p.h" -#include "third_party/pdfium/public/fpdf_doc.h" #include "third_party/pdfium/public/fpdf_text.h" +#include "third_party/pdfium/public/fpdfview.h" #include <QtCore/qelapsedtimer.h> #include <QtCore/qloggingcategory.h> @@ -53,28 +19,75 @@ Q_LOGGING_CATEGORY(qLcS, "qt.pdf.search") static const int UpdateTimerInterval = 100; static const int ContextChars = 64; -static const double CharacterHitTolerance = 6.0; +/*! + \class QPdfSearchModel + \since 5.15 + \inmodule QtPdf + \inherits QAbstractListModel + + \brief The QPdfSearchModel class searches for a string in a PDF document + and holds the results. + + This is used in the \l {Model/View Programming} paradigm to display + a list of search results, to highlight them on the rendered PDF pages, + and to iterate through them using the "search forward" / "search backward" + buttons and shortcuts that would be found in a typical document-viewing UI: + + \image search-results.png +*/ + +/*! + \enum QPdfSearchModel::Role + + \value Page The page number where the search result is found (int). + \value IndexOnPage The index of the search result on the page (int). + \value Location The position of the search result on the page (QPointF). + \value ContextBefore The adjacent text on the page, before the search string (QString). + \value ContextAfter The adjacent text on the page, after the search string (QString). + \omitvalue NRoles + + \sa QPdfLink +*/ + +/*! + Constructs a new search model with parent object \a parent. +*/ QPdfSearchModel::QPdfSearchModel(QObject *parent) : QAbstractListModel(*(new QPdfSearchModelPrivate()), parent) { QMetaEnum rolesMetaEnum = metaObject()->enumerator(metaObject()->indexOfEnumerator("Role")); - for (int r = Qt::UserRole; r < int(Role::_Count); ++r) { + for (int r = Qt::UserRole; r < int(Role::NRoles); ++r) { QByteArray roleName = QByteArray(rolesMetaEnum.valueToKey(r)); if (roleName.isEmpty()) continue; roleName[0] = QChar::toLower(roleName[0]); m_roleNames.insert(r, roleName); } + connect(this, &QAbstractListModel::dataChanged, this, &QPdfSearchModel::countChanged); + connect(this, &QAbstractListModel::modelReset, this, &QPdfSearchModel::countChanged); + connect(this, &QAbstractListModel::rowsRemoved, this, &QPdfSearchModel::countChanged); + connect(this, &QAbstractListModel::rowsInserted, this, &QPdfSearchModel::countChanged); } +/*! + Destroys the model. +*/ QPdfSearchModel::~QPdfSearchModel() {} +/*! + \reimp +*/ QHash<int, QByteArray> QPdfSearchModel::roleNames() const { return m_roleNames; } +/*! + \reimp + + The number of rows in the model is equal to the number of search results found. +*/ int QPdfSearchModel::rowCount(const QModelIndex &parent) const { Q_D(const QPdfSearchModel); @@ -82,6 +95,9 @@ int QPdfSearchModel::rowCount(const QModelIndex &parent) const return d->rowCountSoFar; } +/*! + \reimp +*/ QVariant QPdfSearchModel::data(const QModelIndex &index, int role) const { Q_D(const QPdfSearchModel); @@ -99,7 +115,7 @@ QVariant QPdfSearchModel::data(const QModelIndex &index, int role) const return d->searchResults[pi.page][pi.index].contextBefore(); case Role::ContextAfter: return d->searchResults[pi.page][pi.index].contextAfter(); - case Role::_Count: + case Role::NRoles: break; } if (role == Qt::DisplayRole) { @@ -111,19 +127,33 @@ QVariant QPdfSearchModel::data(const QModelIndex &index, int role) const return QVariant(); } +/*! + \since 6.8 + \property QPdfSearchModel::count + \brief the number of search results found +*/ +int QPdfSearchModel::count() const +{ + return rowCount(QModelIndex()); +} + void QPdfSearchModel::updatePage(int page) { Q_D(QPdfSearchModel); d->doSearch(page); } +/*! + \property QPdfSearchModel::searchString + \brief the string to search for +*/ QString QPdfSearchModel::searchString() const { Q_D(const QPdfSearchModel); return d->searchString; } -void QPdfSearchModel::setSearchString(QString searchString) +void QPdfSearchModel::setSearchString(const QString &searchString) { Q_D(QPdfSearchModel); if (d->searchString == searchString) @@ -136,24 +166,35 @@ void QPdfSearchModel::setSearchString(QString searchString) endResetModel(); } -QList<QPdfSearchResult> QPdfSearchModel::resultsOnPage(int page) const +/*! + Returns the list of all results found on the given \a page. +*/ +QList<QPdfLink> QPdfSearchModel::resultsOnPage(int page) const { Q_D(const QPdfSearchModel); const_cast<QPdfSearchModelPrivate *>(d)->doSearch(page); - if (d->searchResults.count() <= page) + if (d->searchResults.size() <= page) return {}; return d->searchResults[page]; } -QPdfSearchResult QPdfSearchModel::resultAtIndex(int index) const +/*! + Returns a result found by \a index in the \l document, regardless of the + page on which it was found. \a index must be less than \l rowCount. +*/ +QPdfLink QPdfSearchModel::resultAtIndex(int index) const { Q_D(const QPdfSearchModel); const auto pi = const_cast<QPdfSearchModelPrivate*>(d)->pageAndIndexForResult(index); - if (pi.page < 0) - return QPdfSearchResult(); + if (pi.page < 0 || index < 0) + return {}; return d->searchResults[pi.page][pi.index]; } +/*! + \property QPdfSearchModel::document + \brief the document to search +*/ QPdfDocument *QPdfSearchModel::document() const { Q_D(const QPdfSearchModel); @@ -166,6 +207,10 @@ void QPdfSearchModel::setDocument(QPdfDocument *document) if (d->document == document) return; + disconnect(d->documentConnection); + d->documentConnection = connect(document, &QPdfDocument::pageCountChanged, this, + [this]() { d_func()->clearResults(); }); + d->document = document; d->clearResults(); emit documentChanged(); @@ -178,7 +223,7 @@ void QPdfSearchModel::timerEvent(QTimerEvent *event) return; if (!d->document || d->nextPageToUpdate >= d->document->pageCount()) { if (d->document) - qCDebug(qLcS) << "done updating search results on" << d->searchResults.count() << "pages"; + qCDebug(qLcS) << "done updating search results on" << d->searchResults.size() << "pages"; killTimer(d->updateTimerId); d->updateTimerId = -1; } @@ -205,7 +250,7 @@ void QPdfSearchModelPrivate::clearResults() bool QPdfSearchModelPrivate::doSearch(int page) { - if (page < 0 || page >= pagesSearched.count() || searchString.isEmpty()) + if (page < 0 || page >= pagesSearched.size() || searchString.isEmpty()) return false; if (pagesSearched[page]) return true; @@ -219,7 +264,6 @@ bool QPdfSearchModelPrivate::doSearch(int page) qWarning() << "failed to load page" << page; return false; } - double pageHeight = FPDF_GetPageHeight(pdfPage); FPDF_TEXTPAGE textPage = FPDFText_LoadPage(pdfPage); if (!textPage) { qWarning() << "failed to load text of page" << page; @@ -227,7 +271,8 @@ bool QPdfSearchModelPrivate::doSearch(int page) return false; } FPDF_SCHHANDLE sh = FPDFText_FindStart(textPage, searchString.utf16(), 0, 0); - QList<QPdfSearchResult> newSearchResults; + QList<QPdfLink> newSearchResults; + constexpr double CharacterHitTolerance = 6.0; while (FPDFText_FindNext(sh)) { int idx = FPDFText_GetSchResultIndex(sh); int count = FPDFText_GetSchCount(sh); @@ -236,9 +281,12 @@ bool QPdfSearchModelPrivate::doSearch(int page) int startIndex = -1; int endIndex = -1; for (int r = 0; r < rectCount; ++r) { + // get bounding box of search result in page coordinates double left, top, right, bottom; FPDFText_GetRect(textPage, r, &left, &top, &right, &bottom); - rects << QRectF(left, pageHeight - top, right - left, top - bottom); + // deal with any internal PDF transforms and + // convert to the 1x (pixels = points) 4th-quadrant coordinate system + rects << document->d->mapPageToView(pdfPage, left, top, right, bottom); if (r == 0) { startIndex = FPDFText_GetCharIndexAtPos(textPage, left, top, CharacterHitTolerance, CharacterHitTolerance); @@ -247,7 +295,8 @@ bool QPdfSearchModelPrivate::doSearch(int page) endIndex = FPDFText_GetCharIndexAtPos(textPage, right, top, CharacterHitTolerance, CharacterHitTolerance); } - qCDebug(qLcS) << rects.last() << "char idx" << startIndex << "->" << endIndex; + qCDebug(qLcS) << rects.last() << "char idx" << startIndex << "->" << endIndex + << "from page rect" << left << top << right << bottom; } QString contextBefore, contextAfter; if (startIndex >= 0 || endIndex >= 0) { @@ -269,25 +318,25 @@ bool QPdfSearchModelPrivate::doSearch(int page) if (si < 0) qWarning() << "search string" << searchString << "not found in context" << context; contextBefore = context.mid(0, si); - contextAfter = context.mid(si + searchString.length()); + contextAfter = context.mid(si + searchString.size()); } } if (!rects.isEmpty()) - newSearchResults << QPdfSearchResult(page, rects, contextBefore, contextAfter); + newSearchResults << QPdfLink(page, rects, contextBefore, contextAfter); } FPDFText_FindClose(sh); FPDFText_ClosePage(textPage); FPDF_ClosePage(pdfPage); qCDebug(qLcS) << searchString << "took" << timer.elapsed() << "ms to find" - << newSearchResults.count() << "results on page" << page; + << newSearchResults.size() << "results on page" << page; pagesSearched[page] = true; searchResults[page] = newSearchResults; - if (newSearchResults.count() > 0) { + if (newSearchResults.size() > 0) { int rowsBefore = rowsBeforePage(page); - qCDebug(qLcS) << "from row" << rowsBefore << "rowCount" << rowCountSoFar << "increasing by" << newSearchResults.count(); - rowCountSoFar += newSearchResults.count(); - q->beginInsertRows(QModelIndex(), rowsBefore, rowsBefore + newSearchResults.count() - 1); + qCDebug(qLcS) << "from row" << rowsBefore << "rowCount" << rowCountSoFar << "increasing by" << newSearchResults.size(); + rowCountSoFar += newSearchResults.size(); + q->beginInsertRows(QModelIndex(), rowsBefore, rowsBefore + newSearchResults.size() - 1); q->endInsertRows(); } return true; @@ -295,13 +344,15 @@ bool QPdfSearchModelPrivate::doSearch(int page) QPdfSearchModelPrivate::PageAndIndex QPdfSearchModelPrivate::pageAndIndexForResult(int resultIndex) { + if (pagesSearched.isEmpty()) + return {-1, -1}; const int pageCount = document->pageCount(); int totalSoFar = 0; int previousTotalSoFar = 0; for (int page = 0; page < pageCount; ++page) { if (!pagesSearched[page]) doSearch(page); - totalSoFar += searchResults[page].count(); + totalSoFar += searchResults[page].size(); if (totalSoFar > resultIndex) return {page, resultIndex - previousTotalSoFar}; previousTotalSoFar = totalSoFar; @@ -313,7 +364,7 @@ int QPdfSearchModelPrivate::rowsBeforePage(int page) { int ret = 0; for (int i = 0; i < page; ++i) - ret += searchResults[i].count(); + ret += searchResults[i].size(); return ret; } diff --git a/src/pdf/qpdfsearchmodel.h b/src/pdf/qpdfsearchmodel.h index 1a413c763..04f8b9140 100644 --- a/src/pdf/qpdfsearchmodel.h +++ b/src/pdf/qpdfsearchmodel.h @@ -1,38 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtPDF module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL3$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPLv3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QPDFSEARCHMODEL_H #define QPDFSEARCHMODEL_H @@ -41,7 +8,7 @@ #include <QtCore/qabstractitemmodel.h> #include <QtPdf/qpdfdocument.h> -#include <QtPdf/qpdfsearchresult.h> +#include <QtPdf/qpdflink.h> QT_BEGIN_NAMESPACE @@ -52,6 +19,7 @@ class Q_PDF_EXPORT QPdfSearchModel : public QAbstractListModel Q_OBJECT Q_PROPERTY(QPdfDocument *document READ document WRITE setDocument NOTIFY documentChanged) Q_PROPERTY(QString searchString READ searchString WRITE setSearchString NOTIFY searchStringChanged) + Q_PROPERTY(int count READ count NOTIFY countChanged REVISION(6, 8) FINAL) public: enum class Role : int { @@ -60,14 +28,15 @@ public: Location, ContextBefore, ContextAfter, - _Count + NRoles }; Q_ENUM(Role) - explicit QPdfSearchModel(QObject *parent = nullptr); - ~QPdfSearchModel(); + QPdfSearchModel() : QPdfSearchModel(nullptr) {} + explicit QPdfSearchModel(QObject *parent); + ~QPdfSearchModel() override; - QList<QPdfSearchResult> resultsOnPage(int page) const; - QPdfSearchResult resultAtIndex(int index) const; + QList<QPdfLink> resultsOnPage(int page) const; + QPdfLink resultAtIndex(int index) const; QPdfDocument *document() const; QString searchString() const; @@ -76,13 +45,16 @@ public: int rowCount(const QModelIndex &parent) const override; QVariant data(const QModelIndex &index, int role) const override; + int count() const; + public Q_SLOTS: - void setSearchString(QString searchString); + void setSearchString(const QString &searchString); void setDocument(QPdfDocument *document); Q_SIGNALS: void documentChanged(); void searchStringChanged(); + Q_REVISION(6, 8) void countChanged(); protected: void updatePage(int page); diff --git a/src/pdf/qpdfsearchmodel_p.h b/src/pdf/qpdfsearchmodel_p.h index 551eff47c..5ffa08f5d 100644 --- a/src/pdf/qpdfsearchmodel_p.h +++ b/src/pdf/qpdfsearchmodel_p.h @@ -1,38 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtPDF module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL3$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPLv3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QPDFSEARCHMODEL_P_H #define QPDFSEARCHMODEL_P_H @@ -49,7 +16,6 @@ // #include "qpdfsearchmodel.h" -#include "qpdfsearchresult_p.h" #include <private/qabstractitemmodel_p.h> #include "third_party/pdfium/public/fpdfview.h" @@ -75,10 +41,12 @@ public: QPdfDocument *document = nullptr; QString searchString; QList<bool> pagesSearched; - QList<QList<QPdfSearchResult>> searchResults; + QList<QList<QPdfLink>> searchResults; int rowCountSoFar = 0; int updateTimerId = -1; int nextPageToUpdate = 0; + + QMetaObject::Connection documentConnection; }; QT_END_NAMESPACE diff --git a/src/pdf/qpdfsearchresult.cpp b/src/pdf/qpdfsearchresult.cpp deleted file mode 100644 index 629a8765f..000000000 --- a/src/pdf/qpdfsearchresult.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtPDF module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL3$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPLv3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qpdfsearchresult.h" -#include "qpdfsearchresult_p.h" - -QT_BEGIN_NAMESPACE - -QPdfSearchResult::QPdfSearchResult() : - QPdfSearchResult(new QPdfSearchResultPrivate()) { } - -QPdfSearchResult::QPdfSearchResult(int page, QList<QRectF> rects, QString contextBefore, QString contextAfter) : - QPdfSearchResult(new QPdfSearchResultPrivate(page, rects, contextBefore, contextAfter)) { } - -QPdfSearchResult::QPdfSearchResult(QPdfSearchResultPrivate *d) : - QPdfDestination(static_cast<QPdfDestinationPrivate *>(d)) { } - -QString QPdfSearchResult::contextBefore() const -{ - return static_cast<QPdfSearchResultPrivate *>(d.data())->contextBefore; -} - -QString QPdfSearchResult::contextAfter() const -{ - return static_cast<QPdfSearchResultPrivate *>(d.data())->contextAfter; -} - -QList<QRectF> QPdfSearchResult::rectangles() const -{ - return static_cast<QPdfSearchResultPrivate *>(d.data())->rects; -} - -QDebug operator<<(QDebug dbg, const QPdfSearchResult &searchResult) -{ - QDebugStateSaver saver(dbg); - dbg.nospace(); - dbg << "QPdfSearchResult(page=" << searchResult.page() - << " contextBefore=" << searchResult.contextBefore() - << " contextAfter=" << searchResult.contextAfter() - << " rects=" << searchResult.rectangles(); - dbg << ')'; - return dbg; -} - -QT_END_NAMESPACE - -#include "moc_qpdfsearchresult.cpp" diff --git a/src/pdf/qpdfsearchresult.h b/src/pdf/qpdfsearchresult.h deleted file mode 100644 index 0acf03d52..000000000 --- a/src/pdf/qpdfsearchresult.h +++ /dev/null @@ -1,76 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtPDF module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL3$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPLv3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QPDFSEARCHRESULT_H -#define QPDFSEARCHRESULT_H - -#include <QtCore/qdebug.h> -#include <QtCore/qlist.h> -#include <QtCore/qrect.h> -#include <QtPdf/qpdfdestination.h> - -QT_BEGIN_NAMESPACE - -class QPdfSearchResultPrivate; - -class Q_PDF_EXPORT QPdfSearchResult : public QPdfDestination -{ - Q_GADGET - Q_PROPERTY(QString contextBefore READ contextBefore) - Q_PROPERTY(QString contextAfter READ contextAfter) - Q_PROPERTY(QList<QRectF> rectangles READ rectangles) - -public: - QPdfSearchResult(); - ~QPdfSearchResult() {} - - QString contextBefore() const; - QString contextAfter() const; - QList<QRectF> rectangles() const; - -private: - QPdfSearchResult(int page, QList<QRectF> rects, QString contextBefore, QString contextAfter); - QPdfSearchResult(QPdfSearchResultPrivate *d); - friend class QPdfDocument; - friend class QPdfSearchModelPrivate; - friend class QQuickPdfNavigationStack; -}; - -Q_PDF_EXPORT QDebug operator<<(QDebug, const QPdfSearchResult &); - -QT_END_NAMESPACE - -#endif // QPDFSEARCHRESULT_H diff --git a/src/pdf/qpdfsearchresult_p.h b/src/pdf/qpdfsearchresult_p.h deleted file mode 100644 index eca37890e..000000000 --- a/src/pdf/qpdfsearchresult_p.h +++ /dev/null @@ -1,72 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtPDF module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL3$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPLv3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QPDFSEARCHRESULT_P_H -#define QPDFSEARCHRESULT_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qpdfdestination_p.h" - -QT_BEGIN_NAMESPACE - -class QPdfSearchResultPrivate : public QPdfDestinationPrivate -{ -public: - QPdfSearchResultPrivate() = default; - QPdfSearchResultPrivate(int page, QList<QRectF> rects, QString contextBefore, QString contextAfter) : - QPdfDestinationPrivate(page, rects.first().topLeft(), 0), - contextBefore(contextBefore), - contextAfter(contextAfter), - rects(rects) {} - - QString contextBefore; - QString contextAfter; - QList<QRectF> rects; -}; - -QT_END_NAMESPACE - -#endif // QPDFSEARCHRESULT_P_H diff --git a/src/pdf/qpdfselection.cpp b/src/pdf/qpdfselection.cpp index b8ad02969..df30eb353 100644 --- a/src/pdf/qpdfselection.cpp +++ b/src/pdf/qpdfselection.cpp @@ -1,38 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtPDF module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL3$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPLv3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qpdfselection.h" #include "qpdfselection_p.h" @@ -77,25 +44,10 @@ QPdfSelection::QPdfSelection(QPdfSelectionPrivate *d) { } -QPdfSelection::QPdfSelection(const QPdfSelection &other) - : d(other.d) -{ -} - -QPdfSelection::QPdfSelection(QPdfSelection &&other) noexcept - : d(std::move(other.d)) -{ -} - -QPdfSelection::~QPdfSelection() -{ -} - -QPdfSelection &QPdfSelection::operator=(const QPdfSelection &other) -{ - d = other.d; - return *this; -} +QPdfSelection::~QPdfSelection() = default; +QPdfSelection::QPdfSelection(const QPdfSelection &other) = default; +QPdfSelection::QPdfSelection(QPdfSelection &&other) noexcept = default; +QPdfSelection &QPdfSelection::operator=(const QPdfSelection &other) = default; /*! \property QPdfSelection::valid @@ -135,7 +87,7 @@ QString QPdfSelection::text() const } /*! - \property rect QPdfSelection::boundingRectangle + \property QPdfSelection::boundingRectangle This property holds the overall bounding rectangle (convex hull) around \l bounds. */ @@ -145,7 +97,7 @@ QRectF QPdfSelection::boundingRectangle() const } /*! - \property int QPdfSelection::startIndex + \property QPdfSelection::startIndex This property holds the index at the beginning of \l text within the full text on the page. */ @@ -155,7 +107,7 @@ int QPdfSelection::startIndex() const } /*! - \property int QPdfSelection::endIndex + \property QPdfSelection::endIndex This property holds the index at the end of \l text within the full text on the page. */ @@ -166,7 +118,8 @@ int QPdfSelection::endIndex() const #if QT_CONFIG(clipboard) /*! - Copies \l text to the \l {QGuiApplication::clipboard()}{system clipboard}. + Copies \l text to the \l {QGuiApplication::clipboard()}{system clipboard} + depending on the \a mode selected. */ void QPdfSelection::copyToClipboard(QClipboard::Mode mode) const { diff --git a/src/pdf/qpdfselection.h b/src/pdf/qpdfselection.h index 70077fdb0..8fcfaf2d8 100644 --- a/src/pdf/qpdfselection.h +++ b/src/pdf/qpdfselection.h @@ -1,38 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtPDF module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL3$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPLv3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QPDFSELECTION_H #define QPDFSELECTION_H @@ -48,9 +15,9 @@ QT_BEGIN_NAMESPACE class QPdfSelectionPrivate; -class Q_PDF_EXPORT QPdfSelection +class QPdfSelection { - Q_GADGET + Q_GADGET_EXPORT(Q_PDF_EXPORT) Q_PROPERTY(bool valid READ isValid) Q_PROPERTY(QList<QPolygonF> bounds READ bounds) Q_PROPERTY(QRectF boundingRectangle READ boundingRectangle) @@ -59,20 +26,23 @@ class Q_PDF_EXPORT QPdfSelection Q_PROPERTY(int endIndex READ endIndex) public: - ~QPdfSelection(); - QPdfSelection(const QPdfSelection &other); - QPdfSelection &operator=(const QPdfSelection &other); - QPdfSelection(QPdfSelection &&other) noexcept; - QPdfSelection &operator=(QPdfSelection &&other) noexcept { swap(other); return *this; } + Q_PDF_EXPORT ~QPdfSelection(); + Q_PDF_EXPORT QPdfSelection(const QPdfSelection &other); + Q_PDF_EXPORT QPdfSelection &operator=(const QPdfSelection &other); + + Q_PDF_EXPORT QPdfSelection(QPdfSelection &&other) noexcept; + QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QPdfSelection) + void swap(QPdfSelection &other) noexcept { d.swap(other.d); } - bool isValid() const; - QList<QPolygonF> bounds() const; - QString text() const; - QRectF boundingRectangle() const; - int startIndex() const; - int endIndex() const; + + Q_PDF_EXPORT bool isValid() const; + Q_PDF_EXPORT QList<QPolygonF> bounds() const; + Q_PDF_EXPORT QString text() const; + Q_PDF_EXPORT QRectF boundingRectangle() const; + Q_PDF_EXPORT int startIndex() const; + Q_PDF_EXPORT int endIndex() const; #if QT_CONFIG(clipboard) - void copyToClipboard(QClipboard::Mode mode = QClipboard::Clipboard) const; + Q_PDF_EXPORT void copyToClipboard(QClipboard::Mode mode = QClipboard::Clipboard) const; #endif private: @@ -85,6 +55,7 @@ private: private: QExplicitlySharedDataPointer<QPdfSelectionPrivate> d; }; +Q_DECLARE_SHARED(QPdfSelection) QT_END_NAMESPACE diff --git a/src/pdf/qpdfselection_p.h b/src/pdf/qpdfselection_p.h index 0c176b324..541480746 100644 --- a/src/pdf/qpdfselection_p.h +++ b/src/pdf/qpdfselection_p.h @@ -1,38 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtPDF module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL3$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPLv3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QPDFSELECTION_P_H #define QPDFSELECTION_P_H @@ -48,6 +15,8 @@ // We mean it. // +#include "qpdfselection.h" + #include <QList> #include <QPolygonF> diff --git a/src/pdf/qtpdfglobal.h b/src/pdf/qtpdfglobal.h index 8b4b0c206..2d0900029 100644 --- a/src/pdf/qtpdfglobal.h +++ b/src/pdf/qtpdfglobal.h @@ -1,61 +1,11 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtPDF module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL3$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPLv3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QTPDFGLOBAL_H #define QTPDFGLOBAL_H #include <QtCore/qglobal.h> - -QT_BEGIN_NAMESPACE - -#ifndef Q_PDF_EXPORT -# ifndef QT_STATIC -# if defined(QT_BUILD_PDF_LIB) -# define Q_PDF_EXPORT Q_DECL_EXPORT -# else -# define Q_PDF_EXPORT Q_DECL_IMPORT -# endif -# else -# define Q_PDF_EXPORT -# endif -#endif - -#define Q_PDF_PRIVATE_EXPORT Q_PDF_EXPORT - -QT_END_NAMESPACE +#include <QtPdf/qtpdfexports.h> #endif // QTPDFGLOBAL_H diff --git a/src/pdf/quick/quick.pro b/src/pdf/quick/quick.pro deleted file mode 100644 index 47c559091..000000000 --- a/src/pdf/quick/quick.pro +++ /dev/null @@ -1,40 +0,0 @@ -CXX_MODULE = qml -TARGET = pdfplugin -TARGETPATH = QtQuick/Pdf -IMPORT_VERSION = 1.0 - -# qpdfdocument_p.h includes pdfium headers which we must find in order to use private API -CHROMIUM_SRC_DIR = $$QTWEBENGINE_ROOT/$$getChromiumSrcDir() -INCLUDEPATH += $$CHROMIUM_SRC_DIR - -#QMAKE_DOCS = $$PWD/doc/qtquickpdf.qdocconf - -PDF_QML_FILES = \ - qml/PdfMultiPageView.qml \ - qml/PdfPageView.qml \ - qml/PdfScrollablePageView.qml \ - -QML_FILES += $$PDF_QML_FILES qmldir - -RESOURCES += resources.qrc - -SOURCES += \ - plugin.cpp \ - qquickpdfdocument.cpp \ - qquickpdflinkmodel.cpp \ - qquickpdfnavigationstack.cpp \ - qquickpdfsearchmodel.cpp \ - qquickpdfselection.cpp \ - qquicktableviewextra.cpp \ - -HEADERS += \ - qquickpdfdocument_p.h \ - qquickpdflinkmodel_p.h \ - qquickpdfnavigationstack_p.h \ - qquickpdfsearchmodel_p.h \ - qquickpdfselection_p.h \ - qquicktableviewextra_p.h \ - -QT += pdf pdf-private gui core qml quick quick-private -include($${OUT_PWD}/../$$getConfigDir()/QtPdf_static_dep.pri) -load(qml_plugin) diff --git a/src/pdf/quick/resources.qrc b/src/pdf/quick/resources.qrc deleted file mode 100644 index 8270a2028..000000000 --- a/src/pdf/quick/resources.qrc +++ /dev/null @@ -1,10 +0,0 @@ -<RCC> - <qresource prefix="/qt-project.org/qtpdf"> - <file>qml/+material/PdfStyle.qml</file> - <file>qml/+universal/PdfStyle.qml</file> - <file>qml/PdfStyle.qml</file> - <file>qml/PdfMultiPageView.qml</file> - <file>qml/PdfPageView.qml</file> - <file>qml/PdfScrollablePageView.qml</file> - </qresource> -</RCC> |