HEX
Server: Apache/2.4.46 (Win64) OpenSSL/1.1.1j PHP/8.4.25
System: Windows NT DESKTOP-4TAV2RJ 10.0 build 19045 (Windows 10) AMD64
User: fred (0)
PHP: 8.4.25
Disabled: NONE
Upload Files
File: C:/Users/fred/anaconda3/Library/qml/QtQuick/Pdf/qml/PdfPageView.qml
/****************************************************************************
**
** 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$
**
****************************************************************************/
import QtQuick 2.14
import QtQuick.Controls 2.14
import QtQuick.Pdf 5.15
import QtQuick.Shapes 1.14
import Qt.labs.animation 1.0

Rectangle {
    // public API
    // TODO 5.15: required property
    property var document: undefined
    property alias status: image.status

    property alias selectedText: selection.text
    function selectAll() {
        selection.selectAll()
    }
    function copySelectionToClipboard() {
        selection.copyToClipboard()
    }

    // page navigation
    property alias currentPage: navigationStack.currentPage
    property alias backEnabled: navigationStack.backAvailable
    property alias forwardEnabled: navigationStack.forwardAvailable
    function back() { navigationStack.back() }
    function forward() { navigationStack.forward() }
    function goToPage(page) { goToLocation(page, Qt.point(0, 0), 0) }
    function goToLocation(page, location, zoom) {
        if (zoom > 0)
            root.renderScale = zoom
        navigationStack.push(page, location, zoom)
    }

    // page scaling
    property real renderScale: 1
    property alias sourceSize: image.sourceSize
    function resetScale() {
        image.sourceSize.width = 0
        image.sourceSize.height = 0
        root.x = 0
        root.y = 0
        root.scale = 1
    }
    function scaleToWidth(width, height) {
        var halfRotation = Math.abs(root.rotation % 180)
        image.sourceSize = Qt.size((halfRotation > 45 && halfRotation < 135) ? height : width, 0)
        root.x = 0
        root.y = 0
        image.centerInSize = Qt.size(width, height)
        image.centerOnLoad = true
        image.vCenterOnLoad = (halfRotation > 45 && halfRotation < 135)
        root.scale = 1
    }
    function scaleToPage(width, height) {
        var windowAspect = width / height
        var halfRotation = Math.abs(root.rotation % 180)
        var pagePointSize = document.pagePointSize(navigationStack.currentPage)
        if (halfRotation > 45 && halfRotation < 135) {
            // rotated 90 or 270ยบ
            var pageAspect = pagePointSize.height / pagePointSize.width
            if (windowAspect > pageAspect) {
                image.sourceSize = Qt.size(height, 0)
            } else {
                image.sourceSize = Qt.size(0, width)
            }
        } else {
            var pageAspect = pagePointSize.width / pagePointSize.height
            if (windowAspect > pageAspect) {
                image.sourceSize = Qt.size(0, height)
            } else {
                image.sourceSize = Qt.size(width, 0)
            }
        }
        image.centerInSize = Qt.size(width, height)
        image.centerOnLoad = true
        image.vCenterOnLoad = true
        root.scale = 1
    }

    // text search
    property alias searchModel: searchModel
    property alias searchString: searchModel.searchString
    function searchBack() { --searchModel.currentResult }
    function searchForward() { ++searchModel.currentResult }

    // implementation
    id: root
    width: image.width
    height: image.height

    PdfSelection {
        id: selection
        document: root.document
        page: navigationStack.currentPage
        fromPoint: Qt.point(textSelectionDrag.centroid.pressPosition.x / image.pageScale, textSelectionDrag.centroid.pressPosition.y / image.pageScale)
        toPoint: Qt.point(textSelectionDrag.centroid.position.x / image.pageScale, textSelectionDrag.centroid.position.y / image.pageScale)
        hold: !textSelectionDrag.active && !tapHandler.pressed
    }

    PdfSearchModel {
        id: searchModel
        document: root.document === undefined ? null : root.document
        onCurrentPageChanged: root.goToPage(currentPage)
    }

    PdfNavigationStack {
        id: navigationStack
        onCurrentPageChanged: searchModel.currentPage = currentPage
        // TODO onCurrentLocationChanged: position currentLocation.x and .y in middle // currentPageChanged() MUST occur first!
        onCurrentZoomChanged: root.renderScale = currentZoom
        // TODO deal with horizontal location (need WheelHandler or Flickable probably)
    }

    Image {
        id: image
        currentFrame: navigationStack.currentPage
        source: document.status === PdfDocument.Ready ? document.source : ""
        asynchronous: true
        fillMode: Image.PreserveAspectFit
        property bool centerOnLoad: false
        property bool vCenterOnLoad: false
        property size centerInSize
        property real pageScale: image.paintedWidth / document.pagePointSize(navigationStack.currentPage).width
        function reRenderIfNecessary() {
            var newSourceWidth = image.sourceSize.width * root.scale
            var ratio = newSourceWidth / image.sourceSize.width
            if (ratio > 1.1 || ratio < 0.9) {
                image.sourceSize.width = newSourceWidth
                image.sourceSize.height = 0
                root.scale = 1
            }
        }
        onStatusChanged:
            if (status == Image.Ready && centerOnLoad) {
                root.x = (centerInSize.width - image.implicitWidth) / 2
                root.y = vCenterOnLoad ? (centerInSize.height - image.implicitHeight) / 2 : 0
                centerOnLoad = false
                vCenterOnLoad = false
            }
    }
    onRenderScaleChanged: {
        image.sourceSize.width = document.pagePointSize(navigationStack.currentPage).width * renderScale
        image.sourceSize.height = 0
        root.scale = 1
    }

    Shape {
        anchors.fill: parent
        opacity: 0.25
        visible: image.status === Image.Ready
        ShapePath {
            strokeWidth: 1
            strokeColor: "cyan"
            fillColor: "steelblue"
            scale: Qt.size(image.pageScale, image.pageScale)
            PathMultiline {
                paths: searchModel.currentPageBoundingPolygons
            }
        }
        ShapePath {
            strokeWidth: 1
            strokeColor: "orange"
            fillColor: "cyan"
            scale: Qt.size(image.pageScale, image.pageScale)
            PathMultiline {
                paths: searchModel.currentResultBoundingPolygons
            }
        }
        ShapePath {
            fillColor: "orange"
            scale: Qt.size(image.pageScale, image.pageScale)
            PathMultiline {
                paths: selection.geometry
            }
        }
    }

    Repeater {
        model: PdfLinkModel {
            id: linkModel
            document: root.document
            page: navigationStack.currentPage
        }
        delegate: Rectangle {
            color: "transparent"
            border.color: "lightgrey"
            x: rect.x * image.pageScale
            y: rect.y * image.pageScale
            width: rect.width * image.pageScale
            height: rect.height * image.pageScale
            MouseArea { // TODO switch to TapHandler / HoverHandler in 5.15
                anchors.fill: parent
                cursorShape: Qt.PointingHandCursor
                onClicked: {
                    if (page >= 0)
                        navigationStack.push(page, Qt.point(0, 0), root.renderScale)
                    else
                        Qt.openUrlExternally(url)
                }
            }
        }
    }

    PinchHandler {
        id: pinch
        minimumScale: 0.1
        maximumScale: 10
        minimumRotation: 0
        maximumRotation: 0
        onActiveChanged: if (!active) image.reRenderIfNecessary()
        grabPermissions: PinchHandler.TakeOverForbidden // don't allow takeover if pinch has started
    }
    DragHandler {
        id: pageMovingTouchDrag
        acceptedDevices: PointerDevice.TouchScreen
    }
    DragHandler {
        id: pageMovingMiddleMouseDrag
        acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus
        acceptedButtons: Qt.MiddleButton
        snapMode: DragHandler.NoSnap
    }
    DragHandler {
        id: textSelectionDrag
        acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus
        target: null
    }
    TapHandler {
        id: tapHandler
        acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus
    }
    // prevent it from being scrolled out of view
    BoundaryRule on x {
        minimum: 100 - root.width
        maximum: root.parent.width - 100
    }
    BoundaryRule on y {
        minimum: 100 - root.height
        maximum: root.parent.height - 100
    }
}