init
This commit is contained in:
BIN
venv/lib/python3.12/site-packages/PyQt5/Qt5/qml/QtQuick/Pdf/libpdfplugin.dylib
Executable file
BIN
venv/lib/python3.12/site-packages/PyQt5/Qt5/qml/QtQuick/Pdf/libpdfplugin.dylib
Executable file
Binary file not shown.
@@ -0,0 +1,52 @@
|
||||
import QtQuick.tooling 1.2
|
||||
|
||||
// This file describes the plugin-supplied types contained in the library.
|
||||
// It is used for QML tooling purposes only.
|
||||
//
|
||||
// This file was auto-generated by:
|
||||
// 'qmlplugindump -nonrelocatable QtQuick.Pdf 5.14'
|
||||
|
||||
Module {
|
||||
dependencies: [
|
||||
"QtGraphicalEffects 1.12",
|
||||
"QtQuick 2.14",
|
||||
"QtQuick.Controls 2.14",
|
||||
"QtQuick.Controls.Fusion 2.14",
|
||||
"QtQuick.Controls.Fusion.impl 2.14",
|
||||
"QtQuick.Controls.Imagine 2.14",
|
||||
"QtQuick.Controls.Imagine.impl 2.14",
|
||||
"QtQuick.Controls.Material 2.14",
|
||||
"QtQuick.Controls.Material.impl 2.14",
|
||||
"QtQuick.Controls.Universal 2.14",
|
||||
"QtQuick.Controls.Universal.impl 2.12",
|
||||
"QtQuick.Controls.impl 2.14",
|
||||
"QtQuick.Shapes 1.14",
|
||||
"QtQuick.Templates 2.14",
|
||||
"QtQuick.Window 2.2"
|
||||
]
|
||||
Component {
|
||||
name: "QQuickPdfDocument"
|
||||
prototype: "QObject"
|
||||
exports: ["QtQuick.Pdf/PdfDocument 5.14"]
|
||||
exportMetaObjectRevisions: [0]
|
||||
Property { name: "source"; type: "QUrl" }
|
||||
Property { name: "pageCount"; type: "int"; isReadonly: true }
|
||||
Property { name: "password"; type: "string" }
|
||||
Property { name: "status"; type: "QPdfDocument::Status"; isReadonly: true }
|
||||
Property { name: "title"; type: "string"; isReadonly: true }
|
||||
Property { name: "subject"; type: "string"; isReadonly: true }
|
||||
Property { name: "author"; type: "string"; isReadonly: true }
|
||||
Property { name: "keywords"; type: "string"; isReadonly: true }
|
||||
Property { name: "producer"; type: "string"; isReadonly: true }
|
||||
Property { name: "creator"; type: "string"; isReadonly: true }
|
||||
Property { name: "creationDate"; type: "QDateTime"; isReadonly: true }
|
||||
Property { name: "modificationDate"; type: "QDateTime"; isReadonly: true }
|
||||
Signal { name: "passwordRequired" }
|
||||
Signal { name: "metaDataLoaded" }
|
||||
Method {
|
||||
name: "pagePointSize"
|
||||
type: "QSizeF"
|
||||
Parameter { name: "page"; type: "int" }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,434 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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.Layouts 1.14
|
||||
import QtQuick.Pdf 5.15
|
||||
import QtQuick.Shapes 1.14
|
||||
import QtQuick.Window 2.14
|
||||
|
||||
Item {
|
||||
// public API
|
||||
// TODO 5.15: required property
|
||||
property var document: undefined
|
||||
property bool debug: false
|
||||
|
||||
property string selectedText
|
||||
function selectAll() {
|
||||
var currentItem = tableHelper.itemAtCell(tableHelper.cellAtPos(root.width / 2, root.height / 2))
|
||||
if (currentItem)
|
||||
currentItem.selection.selectAll()
|
||||
}
|
||||
function copySelectionToClipboard() {
|
||||
var currentItem = tableHelper.itemAtCell(tableHelper.cellAtPos(root.width / 2, root.height / 2))
|
||||
if (debug)
|
||||
console.log("currentItem", currentItem, "sel", currentItem.selection.text)
|
||||
if (currentItem)
|
||||
currentItem.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) {
|
||||
if (page === navigationStack.currentPage)
|
||||
return
|
||||
goToLocation(page, Qt.point(-1, -1), 0)
|
||||
}
|
||||
function goToLocation(page, location, zoom) {
|
||||
if (zoom > 0) {
|
||||
navigationStack.jumping = true // don't call navigationStack.update() because we will push() instead
|
||||
root.renderScale = zoom
|
||||
tableView.forceLayout() // but do ensure that the table layout is correct before we try to jump
|
||||
navigationStack.jumping = false
|
||||
}
|
||||
navigationStack.push(page, location, zoom) // actually jump
|
||||
}
|
||||
property vector2d jumpLocationMargin: Qt.vector2d(10, 10) // px from top-left corner
|
||||
property int currentPageRenderingStatus: Image.Null
|
||||
|
||||
// page scaling
|
||||
property real renderScale: 1
|
||||
property real pageRotation: 0
|
||||
function resetScale() { root.renderScale = 1 }
|
||||
function scaleToWidth(width, height) {
|
||||
root.renderScale = width / (tableView.rot90 ? tableView.firstPagePointSize.height : tableView.firstPagePointSize.width)
|
||||
}
|
||||
function scaleToPage(width, height) {
|
||||
var windowAspect = width / height
|
||||
var pageAspect = tableView.firstPagePointSize.width / tableView.firstPagePointSize.height
|
||||
if (tableView.rot90) {
|
||||
if (windowAspect > pageAspect) {
|
||||
root.renderScale = height / tableView.firstPagePointSize.width
|
||||
} else {
|
||||
root.renderScale = width / tableView.firstPagePointSize.height
|
||||
}
|
||||
} else {
|
||||
if (windowAspect > pageAspect) {
|
||||
root.renderScale = height / tableView.firstPagePointSize.height
|
||||
} else {
|
||||
root.renderScale = width / tableView.firstPagePointSize.width
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// text search
|
||||
property alias searchModel: searchModel
|
||||
property alias searchString: searchModel.searchString
|
||||
function searchBack() { --searchModel.currentResult }
|
||||
function searchForward() { ++searchModel.currentResult }
|
||||
|
||||
id: root
|
||||
PdfStyle { id: style }
|
||||
TableView {
|
||||
id: tableView
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 2
|
||||
model: modelInUse && root.document !== undefined ? root.document.pageCount : 0
|
||||
// workaround to make TableView do scheduleRebuildTable(RebuildOption::All) in cases when forceLayout() doesn't
|
||||
property bool modelInUse: true
|
||||
function rebuild() {
|
||||
modelInUse = false
|
||||
modelInUse = true
|
||||
}
|
||||
// end workaround
|
||||
rowSpacing: 6
|
||||
property real rotationNorm: Math.round((360 + (root.pageRotation % 360)) % 360)
|
||||
property bool rot90: rotationNorm == 90 || rotationNorm == 270
|
||||
onRot90Changed: forceLayout()
|
||||
property size firstPagePointSize: document === undefined ? Qt.size(0, 0) : document.pagePointSize(0)
|
||||
property real pageHolderWidth: Math.max(root.width, document === undefined ? 0 :
|
||||
(rot90 ? document.maxPageHeight : document.maxPageWidth) * root.renderScale)
|
||||
contentWidth: document === undefined ? 0 : pageHolderWidth + vscroll.width + 2
|
||||
rowHeightProvider: function(row) { return (rot90 ? document.pagePointSize(row).width : document.pagePointSize(row).height) * root.renderScale }
|
||||
TableViewExtra {
|
||||
id: tableHelper
|
||||
tableView: tableView
|
||||
}
|
||||
delegate: Rectangle {
|
||||
id: pageHolder
|
||||
color: root.debug ? "beige" : "transparent"
|
||||
Text {
|
||||
visible: root.debug
|
||||
anchors { right: parent.right; verticalCenter: parent.verticalCenter }
|
||||
rotation: -90; text: pageHolder.width.toFixed(1) + "x" + pageHolder.height.toFixed(1) + "\n" +
|
||||
image.width.toFixed(1) + "x" + image.height.toFixed(1)
|
||||
}
|
||||
implicitWidth: tableView.pageHolderWidth
|
||||
implicitHeight: tableView.rot90 ? image.width : image.height
|
||||
property alias selection: selection
|
||||
Rectangle {
|
||||
id: paper
|
||||
width: image.width
|
||||
height: image.height
|
||||
rotation: root.pageRotation
|
||||
anchors.centerIn: pinch.active ? undefined : parent
|
||||
property size pagePointSize: document.pagePointSize(index)
|
||||
property real pageScale: image.paintedWidth / pagePointSize.width
|
||||
Image {
|
||||
id: image
|
||||
source: document.source
|
||||
currentFrame: index
|
||||
asynchronous: true
|
||||
fillMode: Image.PreserveAspectFit
|
||||
width: paper.pagePointSize.width * root.renderScale
|
||||
height: paper.pagePointSize.height * root.renderScale
|
||||
property real renderScale: root.renderScale
|
||||
property real oldRenderScale: 1
|
||||
onRenderScaleChanged: {
|
||||
image.sourceSize.width = paper.pagePointSize.width * renderScale
|
||||
image.sourceSize.height = 0
|
||||
paper.scale = 1
|
||||
searchHighlights.update()
|
||||
}
|
||||
onStatusChanged: {
|
||||
if (index === navigationStack.currentPage)
|
||||
root.currentPageRenderingStatus = status
|
||||
}
|
||||
}
|
||||
Shape {
|
||||
anchors.fill: parent
|
||||
visible: image.status === Image.Ready
|
||||
onVisibleChanged: searchHighlights.update()
|
||||
ShapePath {
|
||||
strokeWidth: -1
|
||||
fillColor: style.pageSearchResultsColor
|
||||
scale: Qt.size(paper.pageScale, paper.pageScale)
|
||||
PathMultiline {
|
||||
id: searchHighlights
|
||||
function update() {
|
||||
// paths could be a binding, but we need to be able to "kick" it sometimes
|
||||
paths = searchModel.boundingPolygonsOnPage(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
Connections {
|
||||
target: searchModel
|
||||
// whenever the highlights on the _current_ page change, they actually need to change on _all_ pages
|
||||
// (usually because the search string has changed)
|
||||
function onCurrentPageBoundingPolygonsChanged() { searchHighlights.update() }
|
||||
}
|
||||
ShapePath {
|
||||
strokeWidth: -1
|
||||
fillColor: style.selectionColor
|
||||
scale: Qt.size(paper.pageScale, paper.pageScale)
|
||||
PathMultiline {
|
||||
paths: selection.geometry
|
||||
}
|
||||
}
|
||||
}
|
||||
Shape {
|
||||
anchors.fill: parent
|
||||
visible: image.status === Image.Ready && searchModel.currentPage === index
|
||||
ShapePath {
|
||||
strokeWidth: style.currentSearchResultStrokeWidth
|
||||
strokeColor: style.currentSearchResultStrokeColor
|
||||
fillColor: "transparent"
|
||||
scale: Qt.size(paper.pageScale, paper.pageScale)
|
||||
PathMultiline {
|
||||
paths: searchModel.currentResultBoundingPolygons
|
||||
}
|
||||
}
|
||||
}
|
||||
PinchHandler {
|
||||
id: pinch
|
||||
minimumScale: 0.1
|
||||
maximumScale: root.renderScale < 4 ? 2 : 1
|
||||
minimumRotation: root.pageRotation
|
||||
maximumRotation: root.pageRotation
|
||||
enabled: image.sourceSize.width < 5000
|
||||
onActiveChanged:
|
||||
if (active) {
|
||||
paper.z = 10
|
||||
} else {
|
||||
paper.z = 0
|
||||
var centroidInPoints = Qt.point(pinch.centroid.position.x / root.renderScale,
|
||||
pinch.centroid.position.y / root.renderScale)
|
||||
var centroidInFlickable = tableView.mapFromItem(paper, pinch.centroid.position.x, pinch.centroid.position.y)
|
||||
var newSourceWidth = image.sourceSize.width * paper.scale
|
||||
var ratio = newSourceWidth / image.sourceSize.width
|
||||
if (root.debug)
|
||||
console.log("pinch ended on page", index, "with centroid", pinch.centroid.position, centroidInPoints, "wrt flickable", centroidInFlickable,
|
||||
"page at", pageHolder.x.toFixed(2), pageHolder.y.toFixed(2),
|
||||
"contentX/Y were", tableView.contentX.toFixed(2), tableView.contentY.toFixed(2))
|
||||
if (ratio > 1.1 || ratio < 0.9) {
|
||||
var centroidOnPage = Qt.point(centroidInPoints.x * root.renderScale * ratio, centroidInPoints.y * root.renderScale * ratio)
|
||||
paper.scale = 1
|
||||
paper.x = 0
|
||||
paper.y = 0
|
||||
root.renderScale *= ratio
|
||||
tableView.forceLayout()
|
||||
if (tableView.rotationNorm == 0) {
|
||||
tableView.contentX = pageHolder.x + tableView.originX + centroidOnPage.x - centroidInFlickable.x
|
||||
tableView.contentY = pageHolder.y + tableView.originY + centroidOnPage.y - centroidInFlickable.y
|
||||
} else if (tableView.rotationNorm == 90) {
|
||||
tableView.contentX = pageHolder.x + tableView.originX + image.height - centroidOnPage.y - centroidInFlickable.x
|
||||
tableView.contentY = pageHolder.y + tableView.originY + centroidOnPage.x - centroidInFlickable.y
|
||||
} else if (tableView.rotationNorm == 180) {
|
||||
tableView.contentX = pageHolder.x + tableView.originX + image.width - centroidOnPage.x - centroidInFlickable.x
|
||||
tableView.contentY = pageHolder.y + tableView.originY + image.height - centroidOnPage.y - centroidInFlickable.y
|
||||
} else if (tableView.rotationNorm == 270) {
|
||||
tableView.contentX = pageHolder.x + tableView.originX + centroidOnPage.y - centroidInFlickable.x
|
||||
tableView.contentY = pageHolder.y + tableView.originY + image.width - centroidOnPage.x - centroidInFlickable.y
|
||||
}
|
||||
if (root.debug)
|
||||
console.log("contentX/Y adjusted to", tableView.contentX.toFixed(2), tableView.contentY.toFixed(2), "y @top", pageHolder.y)
|
||||
tableView.returnToBounds()
|
||||
}
|
||||
}
|
||||
grabPermissions: PointerHandler.CanTakeOverFromAnything
|
||||
}
|
||||
DragHandler {
|
||||
id: textSelectionDrag
|
||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus
|
||||
target: null
|
||||
}
|
||||
TapHandler {
|
||||
id: mouseClickHandler
|
||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus
|
||||
}
|
||||
TapHandler {
|
||||
id: touchTapHandler
|
||||
acceptedDevices: PointerDevice.TouchScreen
|
||||
onTapped: {
|
||||
selection.clear()
|
||||
selection.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
Repeater {
|
||||
model: PdfLinkModel {
|
||||
id: linkModel
|
||||
document: root.document
|
||||
page: image.currentFrame
|
||||
}
|
||||
delegate: Shape {
|
||||
x: rect.x * paper.pageScale
|
||||
y: rect.y * paper.pageScale
|
||||
width: rect.width * paper.pageScale
|
||||
height: rect.height * paper.pageScale
|
||||
visible: image.status === Image.Ready
|
||||
ShapePath {
|
||||
strokeWidth: style.linkUnderscoreStrokeWidth
|
||||
strokeColor: style.linkUnderscoreColor
|
||||
strokeStyle: style.linkUnderscoreStrokeStyle
|
||||
dashPattern: style.linkUnderscoreDashPattern
|
||||
startX: 0; startY: height
|
||||
PathLine { x: width; y: height }
|
||||
}
|
||||
MouseArea { // TODO switch to TapHandler / HoverHandler in 5.15
|
||||
id: linkMA
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
hoverEnabled: true
|
||||
onClicked: {
|
||||
if (page >= 0)
|
||||
root.goToLocation(page, location, zoom)
|
||||
else
|
||||
Qt.openUrlExternally(url)
|
||||
}
|
||||
}
|
||||
ToolTip {
|
||||
visible: linkMA.containsMouse
|
||||
delay: 1000
|
||||
text: page >= 0 ?
|
||||
("page " + (page + 1) +
|
||||
" location " + location.x.toFixed(1) + ", " + location.y.toFixed(1) +
|
||||
" zoom " + zoom) : url
|
||||
}
|
||||
}
|
||||
}
|
||||
PdfSelection {
|
||||
id: selection
|
||||
anchors.fill: parent
|
||||
document: root.document
|
||||
page: image.currentFrame
|
||||
renderScale: image.renderScale
|
||||
fromPoint: textSelectionDrag.centroid.pressPosition
|
||||
toPoint: textSelectionDrag.centroid.position
|
||||
hold: !textSelectionDrag.active && !mouseClickHandler.pressed
|
||||
onTextChanged: root.selectedText = text
|
||||
focus: true
|
||||
}
|
||||
}
|
||||
}
|
||||
ScrollBar.vertical: ScrollBar {
|
||||
id: vscroll
|
||||
property bool moved: false
|
||||
onPositionChanged: moved = true
|
||||
onActiveChanged: {
|
||||
var cell = tableHelper.cellAtPos(root.width / 2, root.height / 2)
|
||||
var currentItem = tableHelper.itemAtCell(cell)
|
||||
var currentLocation = Qt.point(0, 0)
|
||||
if (currentItem) { // maybe the delegate wasn't loaded yet
|
||||
currentLocation = Qt.point((tableView.contentX - currentItem.x + jumpLocationMargin.x) / root.renderScale,
|
||||
(tableView.contentY - currentItem.y + jumpLocationMargin.y) / root.renderScale)
|
||||
}
|
||||
if (active) {
|
||||
moved = false
|
||||
// emitJumped false to avoid interrupting a pinch if TableView thinks it should scroll at the same time
|
||||
navigationStack.push(cell.y, currentLocation, root.renderScale, false)
|
||||
} else if (moved) {
|
||||
navigationStack.update(cell.y, currentLocation, root.renderScale)
|
||||
}
|
||||
}
|
||||
}
|
||||
ScrollBar.horizontal: ScrollBar { }
|
||||
}
|
||||
onRenderScaleChanged: {
|
||||
// if navigationStack.jumped changes the scale, don't turn around and update the stack again;
|
||||
// and don't force layout either, because positionViewAtCell() will do that
|
||||
if (navigationStack.jumping)
|
||||
return
|
||||
// make TableView rebuild from scratch, because otherwise it doesn't know the delegates are changing size
|
||||
tableView.rebuild()
|
||||
var cell = tableHelper.cellAtPos(root.width / 2, root.height / 2)
|
||||
var currentItem = tableHelper.itemAtCell(cell)
|
||||
if (currentItem) {
|
||||
var currentLocation = Qt.point((tableView.contentX - currentItem.x + jumpLocationMargin.x) / root.renderScale,
|
||||
(tableView.contentY - currentItem.y + jumpLocationMargin.y) / root.renderScale)
|
||||
navigationStack.update(cell.y, currentLocation, renderScale)
|
||||
}
|
||||
}
|
||||
PdfNavigationStack {
|
||||
id: navigationStack
|
||||
property bool jumping: false
|
||||
property int previousPage: 0
|
||||
onJumped: {
|
||||
jumping = true
|
||||
root.renderScale = zoom
|
||||
if (location.y < 0) {
|
||||
// invalid to indicate that a specific location was not needed,
|
||||
// so attempt to position the new page just as the current page is
|
||||
var currentYOffset = 0
|
||||
var previousPageDelegate = tableHelper.itemAtCell(0, previousPage)
|
||||
if (previousPageDelegate)
|
||||
currentYOffset = tableView.contentY - previousPageDelegate.y
|
||||
tableHelper.positionViewAtRow(page, Qt.AlignTop, currentYOffset)
|
||||
if (root.debug) {
|
||||
console.log("going from page", previousPage, "to", page, "offset", currentYOffset,
|
||||
"ended up @", tableView.contentX.toFixed(1) + ", " + tableView.contentY.toFixed(1))
|
||||
}
|
||||
} else {
|
||||
// jump to a page and position the given location relative to the top-left corner of the viewport
|
||||
var pageSize = root.document.pagePointSize(page)
|
||||
pageSize.width *= root.renderScale
|
||||
pageSize.height *= root.renderScale
|
||||
var xOffsetLimit = Math.max(0, pageSize.width - root.width) / 2
|
||||
var offset = Qt.point(Math.max(-xOffsetLimit, Math.min(xOffsetLimit,
|
||||
location.x * root.renderScale - jumpLocationMargin.x)),
|
||||
Math.max(0, location.y * root.renderScale - jumpLocationMargin.y))
|
||||
tableHelper.positionViewAtCell(0, page, Qt.AlignLeft | Qt.AlignTop, offset)
|
||||
if (root.debug) {
|
||||
console.log("going to zoom", zoom, "loc", location, "on page", page,
|
||||
"ended up @", tableView.contentX.toFixed(1) + ", " + tableView.contentY.toFixed(1))
|
||||
}
|
||||
}
|
||||
jumping = false
|
||||
previousPage = page
|
||||
}
|
||||
onCurrentPageChanged: searchModel.currentPage = currentPage
|
||||
}
|
||||
PdfSearchModel {
|
||||
id: searchModel
|
||||
document: root.document === undefined ? null : root.document
|
||||
// TODO maybe avoid jumping if the result is already fully visible in the viewport
|
||||
onCurrentResultBoundingRectChanged: root.goToLocation(currentPage,
|
||||
Qt.point(currentResultBoundingRect.x, currentResultBoundingRect.y), 0)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,276 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,307 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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
|
||||
|
||||
Flickable {
|
||||
// public API
|
||||
// TODO 5.15: required property
|
||||
property var document: undefined
|
||||
property bool debug: false
|
||||
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) {
|
||||
if (page === navigationStack.currentPage)
|
||||
return
|
||||
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 real pageRotation: 0
|
||||
property alias sourceSize: image.sourceSize
|
||||
function resetScale() {
|
||||
paper.scale = 1
|
||||
root.renderScale = 1
|
||||
}
|
||||
function scaleToWidth(width, height) {
|
||||
var pagePointSize = document.pagePointSize(navigationStack.currentPage)
|
||||
root.renderScale = root.width / (paper.rot90 ? pagePointSize.height : pagePointSize.width)
|
||||
if (debug)
|
||||
console.log("scaling", pagePointSize, "to fit", root.width, "rotated?", paper.rot90, "scale", root.renderScale)
|
||||
root.contentX = 0
|
||||
root.contentY = 0
|
||||
}
|
||||
function scaleToPage(width, height) {
|
||||
var pagePointSize = document.pagePointSize(navigationStack.currentPage)
|
||||
root.renderScale = Math.min(
|
||||
root.width / (paper.rot90 ? pagePointSize.height : pagePointSize.width),
|
||||
root.height / (paper.rot90 ? pagePointSize.width : pagePointSize.height) )
|
||||
root.contentX = 0
|
||||
root.contentY = 0
|
||||
}
|
||||
|
||||
// text search
|
||||
property alias searchModel: searchModel
|
||||
property alias searchString: searchModel.searchString
|
||||
function searchBack() { --searchModel.currentResult }
|
||||
function searchForward() { ++searchModel.currentResult }
|
||||
|
||||
// implementation
|
||||
id: root
|
||||
PdfStyle { id: style }
|
||||
contentWidth: paper.width
|
||||
contentHeight: paper.height
|
||||
ScrollBar.vertical: ScrollBar {
|
||||
onActiveChanged:
|
||||
if (!active ) {
|
||||
var currentLocation = Qt.point((root.contentX + root.width / 2) / root.renderScale,
|
||||
(root.contentY + root.height / 2) / root.renderScale)
|
||||
navigationStack.update(navigationStack.currentPage, currentLocation, root.renderScale)
|
||||
}
|
||||
}
|
||||
ScrollBar.horizontal: ScrollBar {
|
||||
onActiveChanged:
|
||||
if (!active ) {
|
||||
var currentLocation = Qt.point((root.contentX + root.width / 2) / root.renderScale,
|
||||
(root.contentY + root.height / 2) / root.renderScale)
|
||||
navigationStack.update(navigationStack.currentPage, currentLocation, root.renderScale)
|
||||
}
|
||||
}
|
||||
|
||||
onRenderScaleChanged: {
|
||||
image.sourceSize.width = document.pagePointSize(navigationStack.currentPage).width * renderScale
|
||||
image.sourceSize.height = 0
|
||||
paper.scale = 1
|
||||
var currentLocation = Qt.point((root.contentX + root.width / 2) / root.renderScale,
|
||||
(root.contentY + root.height / 2) / root.renderScale)
|
||||
navigationStack.update(navigationStack.currentPage, currentLocation, root.renderScale)
|
||||
}
|
||||
|
||||
PdfSearchModel {
|
||||
id: searchModel
|
||||
document: root.document === undefined ? null : root.document
|
||||
// TODO maybe avoid jumping if the result is already fully visible in the viewport
|
||||
onCurrentResultBoundingRectChanged: root.goToLocation(currentPage,
|
||||
Qt.point(currentResultBoundingRect.x, currentResultBoundingRect.y), 0)
|
||||
}
|
||||
|
||||
PdfNavigationStack {
|
||||
id: navigationStack
|
||||
onJumped: {
|
||||
root.renderScale = zoom
|
||||
var dx = Math.max(0, location.x * root.renderScale - root.width / 2) - root.contentX
|
||||
var dy = Math.max(0, location.y * root.renderScale - root.height / 2) - root.contentY
|
||||
// don't jump if location is in the viewport already, i.e. if the "error" between desired and actual contentX/Y is small
|
||||
if (Math.abs(dx) > root.width / 3)
|
||||
root.contentX += dx
|
||||
if (Math.abs(dy) > root.height / 3)
|
||||
root.contentY += dy
|
||||
if (root.debug) {
|
||||
console.log("going to zoom", zoom, "loc", location,
|
||||
"on page", page, "ended up @", root.contentX + ", " + root.contentY)
|
||||
}
|
||||
}
|
||||
onCurrentPageChanged: searchModel.currentPage = currentPage
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: paper
|
||||
width: rot90 ? image.height : image.width
|
||||
height: rot90 ? image.width : image.height
|
||||
property real rotationModulus: Math.abs(root.pageRotation % 180)
|
||||
property bool rot90: rotationModulus > 45 && rotationModulus < 135
|
||||
|
||||
Image {
|
||||
id: image
|
||||
currentFrame: navigationStack.currentPage
|
||||
source: document.status === PdfDocument.Ready ? document.source : ""
|
||||
asynchronous: true
|
||||
fillMode: Image.PreserveAspectFit
|
||||
rotation: root.pageRotation
|
||||
anchors.centerIn: parent
|
||||
property real pageScale: image.paintedWidth / document.pagePointSize(navigationStack.currentPage).width
|
||||
|
||||
Shape {
|
||||
anchors.fill: parent
|
||||
visible: image.status === Image.Ready
|
||||
ShapePath {
|
||||
strokeWidth: -1
|
||||
fillColor: style.pageSearchResultsColor
|
||||
scale: Qt.size(image.pageScale, image.pageScale)
|
||||
PathMultiline {
|
||||
paths: searchModel.currentPageBoundingPolygons
|
||||
}
|
||||
}
|
||||
ShapePath {
|
||||
strokeWidth: style.currentSearchResultStrokeWidth
|
||||
strokeColor: style.currentSearchResultStrokeColor
|
||||
fillColor: "transparent"
|
||||
scale: Qt.size(image.pageScale, image.pageScale)
|
||||
PathMultiline {
|
||||
paths: searchModel.currentResultBoundingPolygons
|
||||
}
|
||||
}
|
||||
ShapePath {
|
||||
fillColor: style.selectionColor
|
||||
scale: Qt.size(image.pageScale, image.pageScale)
|
||||
PathMultiline {
|
||||
paths: selection.geometry
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: PdfLinkModel {
|
||||
id: linkModel
|
||||
document: root.document
|
||||
page: navigationStack.currentPage
|
||||
}
|
||||
delegate: Shape {
|
||||
x: rect.x * image.pageScale
|
||||
y: rect.y * image.pageScale
|
||||
width: rect.width * image.pageScale
|
||||
height: rect.height * image.pageScale
|
||||
ShapePath {
|
||||
strokeWidth: style.linkUnderscoreStrokeWidth
|
||||
strokeColor: style.linkUnderscoreColor
|
||||
strokeStyle: style.linkUnderscoreStrokeStyle
|
||||
dashPattern: style.linkUnderscoreDashPattern
|
||||
startX: 0; startY: height
|
||||
PathLine { x: width; y: height }
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
DragHandler {
|
||||
id: textSelectionDrag
|
||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus
|
||||
target: null
|
||||
}
|
||||
TapHandler {
|
||||
id: mouseClickHandler
|
||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus
|
||||
}
|
||||
TapHandler {
|
||||
id: touchTapHandler
|
||||
acceptedDevices: PointerDevice.TouchScreen
|
||||
onTapped: {
|
||||
selection.clear()
|
||||
selection.focus = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PdfSelection {
|
||||
id: selection
|
||||
anchors.fill: parent
|
||||
document: root.document
|
||||
page: navigationStack.currentPage
|
||||
renderScale: image.pageScale
|
||||
fromPoint: textSelectionDrag.centroid.pressPosition
|
||||
toPoint: textSelectionDrag.centroid.position
|
||||
hold: !textSelectionDrag.active && !mouseClickHandler.pressed
|
||||
focus: true
|
||||
}
|
||||
|
||||
PinchHandler {
|
||||
id: pinch
|
||||
minimumScale: 0.1
|
||||
maximumScale: root.renderScale < 4 ? 2 : 1
|
||||
minimumRotation: 0
|
||||
maximumRotation: 0
|
||||
enabled: image.sourceSize.width < 5000
|
||||
onActiveChanged:
|
||||
if (!active) {
|
||||
var centroidInPoints = Qt.point(pinch.centroid.position.x / root.renderScale,
|
||||
pinch.centroid.position.y / root.renderScale)
|
||||
var centroidInFlickable = root.mapFromItem(paper, pinch.centroid.position.x, pinch.centroid.position.y)
|
||||
var newSourceWidth = image.sourceSize.width * paper.scale
|
||||
var ratio = newSourceWidth / image.sourceSize.width
|
||||
if (root.debug)
|
||||
console.log("pinch ended with centroid", pinch.centroid.position, centroidInPoints, "wrt flickable", centroidInFlickable,
|
||||
"page at", paper.x.toFixed(2), paper.y.toFixed(2),
|
||||
"contentX/Y were", root.contentX.toFixed(2), root.contentY.toFixed(2))
|
||||
if (ratio > 1.1 || ratio < 0.9) {
|
||||
var centroidOnPage = Qt.point(centroidInPoints.x * root.renderScale * ratio, centroidInPoints.y * root.renderScale * ratio)
|
||||
paper.scale = 1
|
||||
paper.x = 0
|
||||
paper.y = 0
|
||||
root.contentX = centroidOnPage.x - centroidInFlickable.x
|
||||
root.contentY = centroidOnPage.y - centroidInFlickable.y
|
||||
root.renderScale *= ratio // onRenderScaleChanged calls navigationStack.update() so we don't need to here
|
||||
if (root.debug)
|
||||
console.log("contentX/Y adjusted to", root.contentX.toFixed(2), root.contentY.toFixed(2))
|
||||
} else {
|
||||
paper.x = 0
|
||||
paper.y = 0
|
||||
}
|
||||
}
|
||||
grabPermissions: PointerHandler.CanTakeOverFromAnything
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
module QtQuick.Pdf
|
||||
plugin pdfplugin
|
||||
classname QtQuick2PdfPlugin
|
||||
typeinfo plugins.qmltypes
|
||||
Reference in New Issue
Block a user