Issue #118: First part of splitting

This commit is contained in:
Jakub Melka
2023-12-03 19:11:17 +01:00
parent c01a1da4ec
commit bc8be2198b
391 changed files with 126 additions and 67 deletions

View File

@ -0,0 +1,82 @@
# Copyright (C) 2023 Jakub Melka
#
# This file is part of PDF4QT.
#
# PDF4QT is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# with the written consent of the copyright owner, any later version.
#
# PDF4QT is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
add_library(Pdf4QtLibWidgets SHARED
sources/pdfadvancedtools.cpp
sources/pdfadvancedtools.h
sources/pdfwidgettool.cpp
sources/pdfwidgetutils.cpp
sources/pdfwidgettool.h
sources/pdfwidgetutils.h
sources/pdfcertificatemanagerdialog.cpp
sources/pdfcertificatemanagerdialog.h
sources/pdfcertificatemanagerdialog.ui
sources/pdfcreatecertificatedialog.cpp
sources/pdfcreatecertificatedialog.h
sources/pdfcreatecertificatedialog.ui
sources/pdfdrawwidget.cpp
sources/pdfdrawwidget.h
sources/pdfobjecteditorwidget.h
sources/pdfobjecteditorwidget_impl.h
sources/pdfobjecteditorwidget.cpp
sources/pdfpagecontenteditorstylesettings.h
sources/pdfpagecontenteditorstylesettings.cpp
sources/pdfpagecontenteditorstylesettings.ui
sources/pdfpagecontenteditortools.cpp
sources/pdfpagecontenteditorwidget.cpp
sources/pdfpagecontenteditortools.h
sources/pdfpagecontenteditorwidget.h
sources/pdfpagecontenteditorwidget.ui
sources/pdfrenderingerrorswidget.h
sources/pdfrenderingerrorswidget.cpp
sources/pdfrenderingerrorswidget.ui
sources/pdfselectpagesdialog.h
sources/pdfselectpagesdialog.cpp
sources/pdfselectpagesdialog.ui
)
include(GenerateExportHeader)
GENERATE_EXPORT_HEADER(Pdf4QtLibWidgets
EXPORT_MACRO_NAME
PDF4QTLIBSHARED_EXPORT
EXPORT_FILE_NAME "${CMAKE_BINARY_DIR}/${INSTALL_INCLUDEDIR}/pdf4qtlibwidgets_export.h")
if(PDF4QT_ENABLE_OPENGL)
target_link_libraries(Pdf4QtLibWidgets PRIVATE Qt6::OpenGLWidgets)
endif()
target_link_libraries(Pdf4QtLibWidgets PRIVATE Pdf4QtLibCore Qt6::Core Qt6::Gui Qt6::Xml Qt6::Svg Qt6::Widgets)
if(LINUX_GCC)
target_link_libraries(Pdf4QtLibWidgets PUBLIC TBB::tbb)
endif()
if(MINGW)
target_link_libraries(Pdf4QtLibWidgets PRIVATE Secur32 Mscms Gdi32 User32 crypt32)
endif()
target_include_directories(Pdf4QtLibWidgets INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/sources)
target_include_directories(Pdf4QtLibWidgets PUBLIC ${CMAKE_BINARY_DIR}/${INSTALL_INCLUDEDIR})
set_target_properties(Pdf4QtLibWidgets PROPERTIES
VERSION ${PDF4QT_VERSION}
SOVERSION ${PDF4QT_VERSION}
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${PDF4QT_INSTALL_LIB_DIR}
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${PDF4QT_INSTALL_BIN_DIR})
install(TARGETS Pdf4QtLibWidgets RUNTIME DESTINATION ${PDF4QT_INSTALL_BIN_DIR} LIBRARY DESTINATION ${PDF4QT_INSTALL_LIB_DIR})

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,383 @@
// Copyright (C) 2020-2021 Jakub Melka
//
// This file is part of PDF4QT.
//
// PDF4QT is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// with the written consent of the copyright owner, any later version.
//
// PDF4QT is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
#ifndef PDFADVANCEDTOOLS_H
#define PDFADVANCEDTOOLS_H
#include "pdfwidgettool.h"
#include "pdfannotation.h"
class QActionGroup;
namespace pdf
{
/// Tool that creates 'sticky note' annotations. Multiple types of sticky
/// notes are available, user can select a type of sticky note. When
/// user select a point, popup window appears and user can enter a text.
class PDF4QTLIBSHARED_EXPORT PDFCreateStickyNoteTool : public PDFWidgetTool
{
Q_OBJECT
private:
using BaseClass = PDFWidgetTool;
public:
explicit PDFCreateStickyNoteTool(PDFDrawWidgetProxy* proxy, PDFToolManager* toolManager, QActionGroup* actionGroup, QObject* parent);
protected:
virtual void updateActions() override;
private:
void onActionTriggered(QAction* action);
void onPointPicked(PDFInteger pageIndex, QPointF pagePoint);
PDFToolManager* m_toolManager;
QActionGroup* m_actionGroup;
PDFPickTool* m_pickTool;
TextAnnotationIcon m_icon;
};
class PDF4QTLIBSHARED_EXPORT PDFCreateAnnotationTool : public PDFWidgetTool
{
Q_OBJECT
private:
using BaseClass = PDFWidgetTool;
public:
explicit PDFCreateAnnotationTool(PDFDrawWidgetProxy* proxy, QAction* action, QObject* parent);
protected:
virtual void updateActions() override;
};
/// Tool that creates url link annotation. Multiple types of link highlights
/// are available, user can select a link highlight. When link annotation
/// is clicked, url address is triggered.
class PDF4QTLIBSHARED_EXPORT PDFCreateHyperlinkTool : public PDFCreateAnnotationTool
{
Q_OBJECT
private:
using BaseClass = PDFCreateAnnotationTool;
public:
explicit PDFCreateHyperlinkTool(PDFDrawWidgetProxy* proxy, PDFToolManager* toolManager, QAction* action, QObject* parent);
LinkHighlightMode getHighlightMode() const;
void setHighlightMode(const LinkHighlightMode& highlightMode);
private:
void onRectanglePicked(pdf::PDFInteger pageIndex, QRectF pageRectangle);
PDFToolManager* m_toolManager;
PDFPickTool* m_pickTool;
LinkHighlightMode m_highlightMode = LinkHighlightMode::Outline;
};
/// Tool that creates free text note without callout line.
class PDF4QTLIBSHARED_EXPORT PDFCreateFreeTextTool : public PDFCreateAnnotationTool
{
Q_OBJECT
private:
using BaseClass = PDFCreateAnnotationTool;
public:
explicit PDFCreateFreeTextTool(PDFDrawWidgetProxy* proxy, PDFToolManager* toolManager, QAction* action, QObject* parent);
private:
void onRectanglePicked(pdf::PDFInteger pageIndex, QRectF pageRectangle);
PDFToolManager* m_toolManager;
PDFPickTool* m_pickTool;
};
/// Tool that creates line/polyline/polygon annotations.
class PDF4QTLIBSHARED_EXPORT PDFCreateLineTypeTool : public PDFCreateAnnotationTool
{
Q_OBJECT
private:
using BaseClass = PDFCreateAnnotationTool;
public:
enum class Type
{
Line,
PolyLine,
Polygon,
Rectangle
};
explicit PDFCreateLineTypeTool(PDFDrawWidgetProxy* proxy, PDFToolManager* toolManager, Type type, QAction* action, QObject* parent);
virtual void keyPressEvent(QWidget* widget, QKeyEvent* event) override;
virtual void keyReleaseEvent(QWidget* widget, QKeyEvent* event) override;
virtual void drawPage(QPainter* painter, PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter,
const QTransform& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const override;
PDFReal getPenWidth() const;
void setPenWidth(PDFReal penWidth);
QColor getStrokeColor() const;
void setStrokeColor(const QColor& strokeColor);
QColor getFillColor() const;
void setFillColor(const QColor& fillColor);
private:
void onPointPicked(PDFInteger pageIndex, QPointF pagePoint);
void onRectanglePicked(pdf::PDFInteger pageIndex, QRectF pageRectangle);
void finishDefinition();
PDFToolManager* m_toolManager;
PDFPickTool* m_pickTool;
Type m_type;
PDFReal m_penWidth;
QColor m_strokeColor;
QColor m_fillColor;
PDFInteger m_rectPageIndex = 0;
QRectF m_rectOnPage;
};
/// Tool that creates ellipse annotation.
class PDF4QTLIBSHARED_EXPORT PDFCreateEllipseTool : public PDFCreateAnnotationTool
{
Q_OBJECT
private:
using BaseClass = PDFCreateAnnotationTool;
public:
explicit PDFCreateEllipseTool(PDFDrawWidgetProxy* proxy, PDFToolManager* toolManager, QAction* action, QObject* parent);
virtual void drawPage(QPainter* painter,
PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter,
const QTransform& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const override;
PDFReal getPenWidth() const;
void setPenWidth(PDFReal penWidth);
QColor getStrokeColor() const;
void setStrokeColor(const QColor& strokeColor);
QColor getFillColor() const;
void setFillColor(const QColor& fillColor);
private:
void onRectanglePicked(pdf::PDFInteger pageIndex, QRectF pageRectangle);
PDFToolManager* m_toolManager;
PDFPickTool* m_pickTool;
PDFReal m_penWidth;
QColor m_strokeColor;
QColor m_fillColor;
};
class PDF4QTLIBSHARED_EXPORT PDFCreateFreehandCurveTool : public PDFCreateAnnotationTool
{
Q_OBJECT
private:
using BaseClass = PDFCreateAnnotationTool;
public:
explicit PDFCreateFreehandCurveTool(PDFDrawWidgetProxy* proxy, PDFToolManager* toolManager, QAction* action, QObject* parent);
virtual void drawPage(QPainter* painter, PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter,
const QTransform& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const override;
virtual void mousePressEvent(QWidget* widget, QMouseEvent* event) override;
virtual void mouseReleaseEvent(QWidget* widget, QMouseEvent* event) override;
virtual void mouseMoveEvent(QWidget* widget, QMouseEvent* event) override;
PDFReal getPenWidth() const;
void setPenWidth(const PDFReal& penWidth);
QColor getStrokeColor() const;
void setStrokeColor(const QColor& strokeColor);
private:
void resetTool();
PDFToolManager* m_toolManager;
PDFInteger m_pageIndex;
std::vector<QPointF> m_pickedPoints;
PDFReal m_penWidth;
QColor m_strokeColor;
};
/// Tool that creates 'stamp' annotations. Multiple types of stamps
/// are available, user can select a type of stamp (text).
class PDF4QTLIBSHARED_EXPORT PDFCreateStampTool : public PDFWidgetTool
{
Q_OBJECT
private:
using BaseClass = PDFWidgetTool;
public:
explicit PDFCreateStampTool(PDFDrawWidgetProxy* proxy, PDFToolManager* toolManager, QActionGroup* actionGroup, QObject* parent);
virtual void drawPage(QPainter* painter, PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter,
const QTransform& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const override;
virtual void mouseMoveEvent(QWidget* widget, QMouseEvent* event) override;
protected:
virtual void updateActions() override;
private:
void onActionTriggered(QAction* action);
void onPointPicked(PDFInteger pageIndex, QPointF pagePoint);
pdf::PDFInteger m_pageIndex;
PDFToolManager* m_toolManager;
QActionGroup* m_actionGroup;
PDFPickTool* m_pickTool;
PDFStampAnnotation m_stampAnnotation;
};
/// Tool for highlighting of text in document
class PDF4QTLIBSHARED_EXPORT PDFCreateHighlightTextTool : public PDFWidgetTool
{
Q_OBJECT
private:
using BaseClass = PDFWidgetTool;
public:
/// Creates new highlight text tool
/// \param proxy Proxy
/// \param type Annotation type, must be one of: Highlight, Underline, Squiggly, StrikeOut
/// \param actionGroup Action group with actions. Each action must define annotation type.
/// \param parent Parent
explicit PDFCreateHighlightTextTool(PDFDrawWidgetProxy* proxy, PDFToolManager* toolManager, QActionGroup* actionGroup, QObject* parent);
virtual void drawPage(QPainter* painter,
PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter,
const QTransform& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const override;
virtual void mousePressEvent(QWidget* widget, QMouseEvent* event) override;
virtual void mouseReleaseEvent(QWidget* widget, QMouseEvent* event) override;
virtual void mouseMoveEvent(QWidget* widget, QMouseEvent* event) override;
protected:
virtual void updateActions() override;
virtual void setActiveImpl(bool active) override;
private:
void onActionTriggered(QAction* action);
void updateCursor();
void setSelection(pdf::PDFTextSelection&& textSelection);
struct SelectionInfo
{
PDFInteger pageIndex = -1;
QPointF selectionStartPoint;
};
PDFToolManager* m_toolManager;
QActionGroup* m_actionGroup;
AnnotationType m_type;
pdf::PDFTextSelection m_textSelection;
SelectionInfo m_selectionInfo;
bool m_isCursorOverText;
};
/// Tool that creates redaction annotation from rectangle. Rectangle is not
/// selected from the text, it is just any rectangle.
class PDF4QTLIBSHARED_EXPORT PDFCreateRedactRectangleTool : public PDFCreateAnnotationTool
{
Q_OBJECT
private:
using BaseClass = PDFCreateAnnotationTool;
public:
explicit PDFCreateRedactRectangleTool(PDFDrawWidgetProxy* proxy, PDFToolManager* toolManager, QAction* action, QObject* parent);
private:
void onRectanglePicked(pdf::PDFInteger pageIndex, QRectF pageRectangle);
PDFToolManager* m_toolManager;
PDFPickTool* m_pickTool;
};
/// Tool for redaction of text in document. Creates redaction annotation from text selection.
class PDF4QTLIBSHARED_EXPORT PDFCreateRedactTextTool : public PDFWidgetTool
{
Q_OBJECT
private:
using BaseClass = PDFWidgetTool;
public:
explicit PDFCreateRedactTextTool(PDFDrawWidgetProxy* proxy, PDFToolManager* toolManager, QAction* action, QObject* parent);
virtual void drawPage(QPainter* painter,
PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter,
const QTransform& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const override;
virtual void mousePressEvent(QWidget* widget, QMouseEvent* event) override;
virtual void mouseReleaseEvent(QWidget* widget, QMouseEvent* event) override;
virtual void mouseMoveEvent(QWidget* widget, QMouseEvent* event) override;
protected:
virtual void updateActions() override;
virtual void setActiveImpl(bool active) override;
private:
void updateCursor();
void setSelection(pdf::PDFTextSelection&& textSelection);
struct SelectionInfo
{
PDFInteger pageIndex = -1;
QPointF selectionStartPoint;
};
PDFToolManager* m_toolManager;
pdf::PDFTextSelection m_textSelection;
SelectionInfo m_selectionInfo;
bool m_isCursorOverText;
};
} // namespace pdf
#endif // PDFADVANCEDTOOLS_H

View File

@ -0,0 +1,133 @@
// Copyright (C) 2022 Jakub Melka
//
// This file is part of PDF4QT.
//
// PDF4QT is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// with the written consent of the copyright owner, any later version.
//
// PDF4QT is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
#include "pdfcertificatemanagerdialog.h"
#include "ui_pdfcertificatemanagerdialog.h"
#include "pdfcreatecertificatedialog.h"
#include "pdfwidgetutils.h"
#include <QAction>
#include <QPushButton>
#include <QFileSystemModel>
#include <QDesktopServices>
#include <QMessageBox>
#include <QFileDialog>
namespace pdf
{
PDFCertificateManagerDialog::PDFCertificateManagerDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::PDFCertificateManagerDialog),
m_newCertificateButton(nullptr),
m_openCertificateDirectoryButton(nullptr),
m_deleteCertificateButton(nullptr),
m_importCertificateButton(nullptr),
m_certificateFileModel(nullptr)
{
ui->setupUi(this);
QDir::root().mkpath(PDFCertificateManager::getCertificateDirectory());
m_certificateFileModel = new QFileSystemModel(this);
QModelIndex rootIndex = m_certificateFileModel->setRootPath(PDFCertificateManager::getCertificateDirectory());
ui->fileView->setModel(m_certificateFileModel);
ui->fileView->setRootIndex(rootIndex);
m_newCertificateButton = ui->buttonBox->addButton(tr("Create"), QDialogButtonBox::ActionRole);
m_openCertificateDirectoryButton = ui->buttonBox->addButton(tr("Open Directory"), QDialogButtonBox::ActionRole);
m_deleteCertificateButton = ui->buttonBox->addButton(tr("Delete"), QDialogButtonBox::ActionRole);
m_importCertificateButton = ui->buttonBox->addButton(tr("Import"), QDialogButtonBox::ActionRole);
connect(m_newCertificateButton, &QPushButton::clicked, this, &PDFCertificateManagerDialog::onNewCertificateClicked);
connect(m_openCertificateDirectoryButton, &QPushButton::clicked, this, &PDFCertificateManagerDialog::onOpenCertificateDirectoryClicked);
connect(m_deleteCertificateButton, &QPushButton::clicked, this, &PDFCertificateManagerDialog::onDeleteCertificateClicked);
connect(m_importCertificateButton, &QPushButton::clicked, this, &PDFCertificateManagerDialog::onImportCertificateClicked);
setMinimumSize(pdf::PDFWidgetUtils::scaleDPI(this, QSize(640, 480)));
}
PDFCertificateManagerDialog::~PDFCertificateManagerDialog()
{
delete ui;
}
void PDFCertificateManagerDialog::onNewCertificateClicked()
{
PDFCreateCertificateDialog dialog(this);
if (dialog.exec() == PDFCreateCertificateDialog::Accepted)
{
const PDFCertificateManager::NewCertificateInfo info = dialog.getNewCertificateInfo();
m_certificateManager.createCertificate(info);
}
}
void PDFCertificateManagerDialog::onOpenCertificateDirectoryClicked()
{
QDesktopServices::openUrl(QString("file:///%1").arg(PDFCertificateManager::getCertificateDirectory(), QUrl::TolerantMode));
}
void PDFCertificateManagerDialog::onDeleteCertificateClicked()
{
QFileInfo fileInfo = m_certificateFileModel->fileInfo(ui->fileView->currentIndex());
if (fileInfo.exists())
{
if (QMessageBox::question(this, tr("Confirm delete"), tr("Do you want to delete certificate '%1'?").arg(fileInfo.fileName()), QMessageBox::No, QMessageBox::Yes) == QMessageBox::Yes)
{
QFile file(fileInfo.filePath());
if (!file.remove())
{
QMessageBox::critical(this, tr("Error"), tr("Cannot delete certificate '%1'").arg(fileInfo.fileName()));
}
}
}
}
void PDFCertificateManagerDialog::onImportCertificateClicked()
{
QString selectedFile = QFileDialog::getOpenFileName(this, tr("Import Certificate"), QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), tr("Certificate file (*.pfx);;All files (*.*)"));
if (selectedFile.isEmpty())
{
return;
}
QFile file(selectedFile);
if (file.exists())
{
QString path = PDFCertificateManager::getCertificateDirectory();
QString targetFile = QString("%1/%2").arg(path, QFileInfo(file).fileName());
if (QFile::exists(targetFile))
{
QMessageBox::critical(this, tr("Error"), tr("Target file exists. Please rename the certificate file to import."));
}
else
{
if (file.copy(targetFile))
{
QMessageBox::information(this, tr("Import Certificate"), tr("Certificate '%1' was successfully imported.").arg(file.fileName()));
}
else
{
QMessageBox::critical(this, tr("Import Certificate"), tr("Error occured during certificate '%1' import.").arg(file.fileName()));
}
}
}
}
} // namespace pdf

View File

@ -0,0 +1,61 @@
// Copyright (C) 2022 Jakub Melka
//
// This file is part of PDF4QT.
//
// PDF4QT is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// with the written consent of the copyright owner, any later version.
//
// PDF4QT is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
#ifndef PDFCERTIFICATEMANAGERDIALOG_H
#define PDFCERTIFICATEMANAGERDIALOG_H
#include "pdfcertificatemanager.h"
#include <QDialog>
class QAction;
class QFileSystemModel;
namespace Ui
{
class PDFCertificateManagerDialog;
}
namespace pdf
{
class PDF4QTLIBSHARED_EXPORT PDFCertificateManagerDialog : public QDialog
{
Q_OBJECT
public:
explicit PDFCertificateManagerDialog(QWidget* parent);
virtual ~PDFCertificateManagerDialog() override;
private:
void onNewCertificateClicked();
void onOpenCertificateDirectoryClicked();
void onDeleteCertificateClicked();
void onImportCertificateClicked();
Ui::PDFCertificateManagerDialog* ui;
PDFCertificateManager m_certificateManager;
QPushButton* m_newCertificateButton;
QPushButton* m_openCertificateDirectoryButton;
QPushButton* m_deleteCertificateButton;
QPushButton* m_importCertificateButton;
QFileSystemModel* m_certificateFileModel;
};
} // namespace pdf
#endif // PDFCERTIFICATEMANAGERDIALOG_H

View File

@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PDFCertificateManagerDialog</class>
<widget class="QDialog" name="PDFCertificateManagerDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>789</width>
<height>511</height>
</rect>
</property>
<property name="windowTitle">
<string>Certificate Manager</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="certificateGroupBox">
<property name="title">
<string>Certificates</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QTreeView" name="fileView"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>PDFCertificateManagerDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>PDFCertificateManagerDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,162 @@
// Copyright (C) 2022 Jakub Melka
//
// This file is part of PDF4QT.
//
// PDF4QT is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// with the written consent of the copyright owner, any later version.
//
// PDF4QT is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
#include "pdfcreatecertificatedialog.h"
#include "ui_pdfcreatecertificatedialog.h"
#include "pdfcertificatemanager.h"
#include <QMessageBox>
#include <QInputDialog>
#include <QDate>
#include <QCalendar>
namespace pdf
{
PDFCreateCertificateDialog::PDFCreateCertificateDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::PDFCreateCertificateDialog)
{
ui->setupUi(this);
ui->fileNameEdit->setReadOnly(true);
ui->fileNameEdit->setText(PDFCertificateManager::generateCertificateFileName());
ui->keyLengthCombo->addItem(tr("1024 bits"), 1024);
ui->keyLengthCombo->addItem(tr("2048 bits"), 2048);
ui->keyLengthCombo->addItem(tr("4096 bits"), 4096);
ui->keyLengthCombo->setCurrentIndex(ui->keyLengthCombo->findData(2048));
QList<QLocale> locales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyCountry);
std::sort(locales.begin(), locales.end(), [](const QLocale& left, const QLocale& right) { return QString::compare(left.nativeTerritoryName(), right.nativeTerritoryName(), Qt::CaseInsensitive) < 0; });
int currentIndex = 0;
QLocale currentLocale = QLocale::system();
for (const QLocale& locale : locales)
{
if (locale.territory() == QLocale::AnyTerritory)
{
continue;
}
if (locale.nativeTerritoryName().isEmpty())
{
continue;
}
QString localeName = locale.name();
QString countryCode = localeName.split(QChar('_')).back();
QString text = QString("%1 | %2").arg(countryCode, locale.nativeTerritoryName());
if (ui->countryCombo->findText(text) >= 0)
{
continue;
}
if (locale.bcp47Name() == currentLocale.bcp47Name())
{
currentIndex = ui->countryCombo->count();
}
ui->countryCombo->addItem(text, countryCode);
}
ui->countryCombo->setCurrentIndex(currentIndex);
ui->countryCombo->setMaxVisibleItems(25);
QDate minDate = QDate::currentDate();
ui->validTillEdit->setMinimumDate(minDate);
QDate selectedDate = minDate;
selectedDate = selectedDate.addYears(5, ui->validTillEdit->calendar());
ui->validTillEdit->setSelectedDate(selectedDate);
}
PDFCreateCertificateDialog::~PDFCreateCertificateDialog()
{
delete ui;
}
void PDFCreateCertificateDialog::accept()
{
if (validate())
{
bool ok = false;
QString password1 = QInputDialog::getText(this, tr("Certificate Protection"), tr("Enter password to protect your certificate."), QLineEdit::Password, QString(), &ok);
if (!ok)
{
return;
}
QString password2 = QInputDialog::getText(this, tr("Certificate Protection"), tr("Enter password again to verify password text."), QLineEdit::Password, QString(), &ok);
if (password1 != password2)
{
QMessageBox::critical(this, tr("Error"), tr("Reentered password is not equal to the first one!"));
return;
}
QDate date = ui->validTillEdit->selectedDate();
QDate currentDate = QDate::currentDate();
int days = currentDate.daysTo(date);
// Fill certificate info
m_newCertificateInfo.fileName = ui->fileNameEdit->text();
m_newCertificateInfo.privateKeyPasword = password1;
m_newCertificateInfo.certCountryCode = ui->countryCombo->currentData().toString();
m_newCertificateInfo.certOrganization = ui->organizationEdit->text();
m_newCertificateInfo.certOrganizationUnit = ui->organizationUnitEdit->text();
m_newCertificateInfo.certCommonName = ui->commonNameEdit->text();
m_newCertificateInfo.certEmail = ui->emailEdit->text();
m_newCertificateInfo.rsaKeyLength = ui->keyLengthCombo->currentData().toInt();
m_newCertificateInfo.validityInSeconds = days * 24 * 3600;
BaseClass::accept();
}
}
bool PDFCreateCertificateDialog::validate()
{
// validate empty text fields
if (ui->commonNameEdit->text().isEmpty())
{
QMessageBox::critical(this, tr("Error"), tr("Please enter a name!"));
ui->commonNameEdit->setFocus();
return false;
}
if (ui->organizationEdit->text().isEmpty())
{
QMessageBox::critical(this, tr("Error"), tr("Please enter an organization name!"));
ui->organizationEdit->setFocus();
return false;
}
if (ui->emailEdit->text().isEmpty())
{
QMessageBox::critical(this, tr("Error"), tr("Please enter an email address!"));
ui->emailEdit->setFocus();
return false;
}
return true;
}
} // namespace pdf

View File

@ -0,0 +1,59 @@
// Copyright (C) 2022 Jakub Melka
//
// This file is part of PDF4QT.
//
// PDF4QT is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// with the written consent of the copyright owner, any later version.
//
// PDF4QT is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
#ifndef PDFCREATECERTIFICATEDIALOG_H
#define PDFCREATECERTIFICATEDIALOG_H
#include "pdfcertificatemanager.h"
#include <QDialog>
namespace Ui
{
class PDFCreateCertificateDialog;
}
namespace pdf
{
class PDF4QTLIBSHARED_EXPORT PDFCreateCertificateDialog : public QDialog
{
Q_OBJECT
private:
using BaseClass = QDialog;
public:
explicit PDFCreateCertificateDialog(QWidget* parent);
virtual ~PDFCreateCertificateDialog() override;
const PDFCertificateManager::NewCertificateInfo& getNewCertificateInfo() const { return m_newCertificateInfo; }
public slots:
virtual void accept() override;
private:
bool validate();
PDFCertificateManager::NewCertificateInfo m_newCertificateInfo;
Ui::PDFCreateCertificateDialog* ui;
};
} // namespace pdf
#endif // PDFCREATECERTIFICATEDIALOG_H

View File

@ -0,0 +1,182 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PDFCreateCertificateDialog</class>
<widget class="QDialog" name="PDFCreateCertificateDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>514</width>
<height>488</height>
</rect>
</property>
<property name="windowTitle">
<string>Create Certificate</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="certificateGroupBox">
<property name="title">
<string>Create Self Signed Certificate</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="1" column="0">
<widget class="QLabel" name="nameLabel">
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="commonNameEdit">
<property name="clearButtonEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="organizationLabel">
<property name="text">
<string>Organization</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="organizationUnitLabel">
<property name="text">
<string>Organization Unit</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="emailLabel">
<property name="text">
<string>Email</string>
</property>
</widget>
</item>
<item row="8" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="organizationEdit">
<property name="clearButtonEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="organizationUnitEdit">
<property name="clearButtonEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLineEdit" name="emailEdit">
<property name="clearButtonEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="countryLabel">
<property name="text">
<string>Country</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="keyLengthLabel">
<property name="text">
<string>Key length</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="validityLabel">
<property name="text">
<string>Valid till</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QCalendarWidget" name="validTillEdit"/>
</item>
<item row="6" column="1">
<widget class="QComboBox" name="keyLengthCombo"/>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="countryCombo"/>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="fileNameEdit"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="certificateFileLabel">
<property name="text">
<string>Certificate file</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>PDFCreateCertificateDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>PDFCreateCertificateDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,658 @@
// Copyright (C) 2018-2023 Jakub Melka
//
// This file is part of PDF4QT.
//
// PDF4QT is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// with the written consent of the copyright owner, any later version.
//
// PDF4QT is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
#include "pdfdrawwidget.h"
#include "pdfdrawspacecontroller.h"
#include "pdfcompiler.h"
#include "pdfwidgettool.h"
#include "pdfannotation.h"
#include "pdfform.h"
#include "pdfdbgheap.h"
#include <QPainter>
#include <QGridLayout>
#include <QKeyEvent>
#include <QApplication>
#include <QPixmapCache>
#include <QColorSpace>
namespace pdf
{
PDFWidget::PDFWidget(const PDFCMSManager* cmsManager, RendererEngine engine, int samplesCount, QWidget* parent) :
QWidget(parent),
m_cmsManager(cmsManager),
m_toolManager(nullptr),
m_annotationManager(nullptr),
m_formManager(nullptr),
m_drawWidget(nullptr),
m_horizontalScrollBar(nullptr),
m_verticalScrollBar(nullptr),
m_proxy(nullptr)
{
m_drawWidget = createDrawWidget(getEffectiveRenderer(engine), samplesCount);
m_horizontalScrollBar = new QScrollBar(Qt::Horizontal, this);
m_verticalScrollBar = new QScrollBar(Qt::Vertical, this);
QGridLayout* layout = new QGridLayout(this);
layout->setSpacing(0);
layout->addWidget(m_drawWidget->getWidget(), 0, 0);
layout->addWidget(m_horizontalScrollBar, 1, 0);
layout->addWidget(m_verticalScrollBar, 0, 1);
layout->setContentsMargins(QMargins());
setLayout(layout);
setFocusProxy(m_drawWidget->getWidget());
m_proxy = new PDFDrawWidgetProxy(this);
m_proxy->init(this);
connect(m_proxy, &PDFDrawWidgetProxy::renderingError, this, &PDFWidget::onRenderingError);
connect(m_proxy, &PDFDrawWidgetProxy::repaintNeeded, m_drawWidget->getWidget(), QOverload<>::of(&QWidget::update));
connect(m_proxy, &PDFDrawWidgetProxy::pageImageChanged, this, &PDFWidget::onPageImageChanged);
updateRendererImpl();
}
PDFWidget::~PDFWidget()
{
}
bool PDFWidget::focusNextPrevChild(bool next)
{
if (m_formManager && m_formManager->focusNextPrevFormField(next))
{
return true;
}
return QWidget::focusNextPrevChild(next);
}
void PDFWidget::setDocument(const PDFModifiedDocument& document)
{
m_proxy->setDocument(document);
m_pageRenderingErrors.clear();
m_drawWidget->getWidget()->update();
}
void PDFWidget::updateRenderer(RendererEngine engine, int samplesCount)
{
engine = getEffectiveRenderer(engine);
PDFOpenGLDrawWidget* openglDrawWidget = qobject_cast<PDFOpenGLDrawWidget*>(m_drawWidget->getWidget());
PDFDrawWidget* softwareDrawWidget = qobject_cast<PDFDrawWidget*>(m_drawWidget->getWidget());
// Do we need to change renderer?
if ((openglDrawWidget && engine != RendererEngine::OpenGL) || (softwareDrawWidget && engine != RendererEngine::Software))
{
QGridLayout* layout = qobject_cast<QGridLayout*>(this->layout());
layout->removeWidget(m_drawWidget->getWidget());
delete m_drawWidget->getWidget();
m_drawWidget = createDrawWidget(engine, samplesCount);
layout->addWidget(m_drawWidget->getWidget(), 0, 0);
setFocusProxy(m_drawWidget->getWidget());
connect(m_proxy, &PDFDrawWidgetProxy::repaintNeeded, m_drawWidget->getWidget(), QOverload<>::of(&QWidget::update));
}
#ifdef PDF4QT_ENABLE_OPENGL
else if (openglDrawWidget)
{
// Just check the samples count
QSurfaceFormat format = openglDrawWidget->format();
if (format.samples() != samplesCount)
{
format.setSamples(samplesCount);
openglDrawWidget->setFormat(format);
}
}
#endif
updateRendererImpl();
}
void PDFWidget::updateCacheLimits(int compiledPageCacheLimit, int thumbnailsCacheLimit, int fontCacheLimit, int instancedFontCacheLimit)
{
m_proxy->getCompiler()->setCacheLimit(compiledPageCacheLimit);
QPixmapCache::setCacheLimit(thumbnailsCacheLimit);
m_proxy->getFontCache()->setCacheLimits(fontCacheLimit, instancedFontCacheLimit);
}
int PDFWidget::getPageRenderingErrorCount() const
{
int count = 0;
for (const auto& item : m_pageRenderingErrors)
{
count += item.second.size();
}
return count;
}
void PDFWidget::updateRendererImpl()
{
#ifdef PDF4QT_ENABLE_OPENGL
PDFOpenGLDrawWidget* openglDrawWidget = qobject_cast<PDFOpenGLDrawWidget*>(m_drawWidget->getWidget());
m_proxy->updateRenderer(openglDrawWidget != nullptr, openglDrawWidget ? openglDrawWidget->format() : QSurfaceFormat::defaultFormat());
#else
m_proxy->updateRenderer(false, QSurfaceFormat::defaultFormat());
#endif
}
void PDFWidget::onRenderingError(PDFInteger pageIndex, const QList<PDFRenderError>& errors)
{
// Empty list of error should not be reported!
Q_ASSERT(!errors.empty());
m_pageRenderingErrors[pageIndex] = errors;
Q_EMIT pageRenderingErrorsChanged(pageIndex, errors.size());
}
void PDFWidget::onPageImageChanged(bool all, const std::vector<PDFInteger>& pages)
{
if (all)
{
m_drawWidget->getWidget()->update();
}
else
{
std::vector<PDFInteger> currentPages = m_drawWidget->getCurrentPages();
Q_ASSERT(std::is_sorted(pages.cbegin(), pages.cend()));
for (PDFInteger pageIndex : currentPages)
{
if (std::binary_search(pages.cbegin(), pages.cend(), pageIndex))
{
m_drawWidget->getWidget()->update();
return;
}
}
}
}
IDrawWidget* PDFWidget::createDrawWidget(RendererEngine rendererEngine, int samplesCount)
{
switch (rendererEngine)
{
case RendererEngine::Software:
return new PDFDrawWidget(this, this);
case RendererEngine::OpenGL:
#ifdef PDF4QT_ENABLE_OPENGL
return new PDFOpenGLDrawWidget(this, samplesCount, this);
#else
Q_UNUSED(samplesCount);
return new PDFDrawWidget(this, this);
#endif
default:
Q_ASSERT(false);
break;
}
return nullptr;
}
void PDFWidget::removeInputInterface(IDrawWidgetInputInterface* inputInterface)
{
auto it = std::find(m_inputInterfaces.begin(), m_inputInterfaces.end(), inputInterface);
if (it != m_inputInterfaces.end())
{
m_inputInterfaces.erase(it);
}
}
void PDFWidget::addInputInterface(IDrawWidgetInputInterface* inputInterface)
{
if (inputInterface)
{
m_inputInterfaces.push_back(inputInterface);
std::sort(m_inputInterfaces.begin(), m_inputInterfaces.end(), IDrawWidgetInputInterface::Comparator());
}
}
RendererEngine PDFWidget::getEffectiveRenderer(RendererEngine rendererEngine)
{
if (rendererEngine == RendererEngine::OpenGL && !pdf::PDFRendererInfo::isHardwareAccelerationSupported())
{
return RendererEngine::Software;
}
return rendererEngine;
}
PDFFormManager* PDFWidget::getFormManager() const
{
return m_formManager;
}
void PDFWidget::setFormManager(PDFFormManager* formManager)
{
removeInputInterface(m_formManager);
m_formManager = formManager;
addInputInterface(m_formManager);
}
void PDFWidget::setToolManager(PDFToolManager* toolManager)
{
removeInputInterface(m_toolManager);
m_toolManager = toolManager;
addInputInterface(m_toolManager);
}
void PDFWidget::setAnnotationManager(PDFWidgetAnnotationManager* annotationManager)
{
removeInputInterface(m_annotationManager);
m_annotationManager = annotationManager;
addInputInterface(m_annotationManager);
}
template<typename BaseWidget>
PDFDrawWidgetBase<BaseWidget>::PDFDrawWidgetBase(PDFWidget* widget, QWidget* parent) :
BaseWidget(parent),
m_widget(widget),
m_mouseOperation(MouseOperation::None)
{
this->setFocusPolicy(Qt::StrongFocus);
this->setMouseTracking(true);
}
template<typename BaseWidget>
std::vector<PDFInteger> PDFDrawWidgetBase<BaseWidget>::getCurrentPages() const
{
return this->m_widget->getDrawWidgetProxy()->getPagesIntersectingRect(this->rect());
}
template<typename BaseWidget>
QSize PDFDrawWidgetBase<BaseWidget>::minimumSizeHint() const
{
return QSize(200, 200);
}
template<typename BaseWidget>
bool PDFDrawWidgetBase<BaseWidget>::event(QEvent* event)
{
if (event->type() == QEvent::ShortcutOverride)
{
return processEvent<QKeyEvent, &IDrawWidgetInputInterface::shortcutOverrideEvent>(static_cast<QKeyEvent*>(event));
}
return BaseWidget::event(event);
}
template<typename BaseWidget>
void PDFDrawWidgetBase<BaseWidget>::performMouseOperation(QPoint currentMousePosition)
{
switch (m_mouseOperation)
{
case MouseOperation::None:
// No operation performed
break;
case MouseOperation::Translate:
{
QPoint difference = currentMousePosition - m_lastMousePosition;
m_widget->getDrawWidgetProxy()->scrollByPixels(difference);
m_lastMousePosition = currentMousePosition;
break;
}
default:
Q_ASSERT(false);
}
}
template<typename BaseWidget>
template<typename Event, void (IDrawWidgetInputInterface::* Function)(QWidget*, Event*)>
bool PDFDrawWidgetBase<BaseWidget>::processEvent(Event* event)
{
QString tooltip;
for (IDrawWidgetInputInterface* inputInterface : m_widget->getInputInterfaces())
{
(inputInterface->*Function)(this, event);
// Update tooltip
if (tooltip.isEmpty())
{
tooltip = inputInterface->getTooltip();
}
// If event is accepted, then update cursor/tooltip and return
if (event->isAccepted())
{
this->setToolTip(tooltip);
this->updateCursor();
return true;
}
}
this->setToolTip(tooltip);
return false;
}
template<typename BaseWidget>
void PDFDrawWidgetBase<BaseWidget>::keyPressEvent(QKeyEvent* event)
{
event->ignore();
if (processEvent<QKeyEvent, &IDrawWidgetInputInterface::keyPressEvent>(event))
{
return;
}
// Vertical navigation
QScrollBar* verticalScrollbar = m_widget->getVerticalScrollbar();
if (verticalScrollbar->isVisible())
{
constexpr std::pair<QKeySequence::StandardKey, PDFDrawWidgetProxy::Operation> keyToOperations[] =
{
{ QKeySequence::MoveToStartOfDocument, PDFDrawWidgetProxy::NavigateDocumentStart },
{ QKeySequence::MoveToEndOfDocument, PDFDrawWidgetProxy::NavigateDocumentEnd },
{ QKeySequence::MoveToNextPage, PDFDrawWidgetProxy::NavigateNextPage },
{ QKeySequence::MoveToPreviousPage, PDFDrawWidgetProxy::NavigatePreviousPage },
{ QKeySequence::MoveToNextLine, PDFDrawWidgetProxy::NavigateNextStep },
{ QKeySequence::MoveToPreviousLine, PDFDrawWidgetProxy::NavigatePreviousStep }
};
for (const std::pair<QKeySequence::StandardKey, PDFDrawWidgetProxy::Operation>& keyToOperation : keyToOperations)
{
if (event->matches(keyToOperation.first))
{
m_widget->getDrawWidgetProxy()->performOperation(keyToOperation.second);
event->accept();
}
}
}
updateCursor();
}
template<typename BaseWidget>
void PDFDrawWidgetBase<BaseWidget>::keyReleaseEvent(QKeyEvent* event)
{
event->ignore();
if (processEvent<QKeyEvent, &IDrawWidgetInputInterface::keyReleaseEvent>(event))
{
return;
}
event->accept();
}
template<typename BaseWidget>
void PDFDrawWidgetBase<BaseWidget>::mousePressEvent(QMouseEvent* event)
{
event->ignore();
if (processEvent<QMouseEvent, &IDrawWidgetInputInterface::mousePressEvent>(event))
{
return;
}
if (event->button() == Qt::LeftButton)
{
m_mouseOperation = MouseOperation::Translate;
m_lastMousePosition = event->pos();
}
updateCursor();
event->accept();
}
template<typename BaseWidget>
void PDFDrawWidgetBase<BaseWidget>::mouseDoubleClickEvent(QMouseEvent* event)
{
event->ignore();
if (processEvent<QMouseEvent, &IDrawWidgetInputInterface::mouseDoubleClickEvent>(event))
{
return;
}
}
template<typename BaseWidget>
void PDFDrawWidgetBase<BaseWidget>::mouseReleaseEvent(QMouseEvent* event)
{
event->ignore();
if (processEvent<QMouseEvent, &IDrawWidgetInputInterface::mouseReleaseEvent>(event))
{
return;
}
performMouseOperation(event->pos());
switch (m_mouseOperation)
{
case MouseOperation::None:
break;
case MouseOperation::Translate:
{
m_mouseOperation = MouseOperation::None;
break;
}
default:
Q_ASSERT(false);
}
updateCursor();
event->accept();
}
template<typename BaseWidget>
void PDFDrawWidgetBase<BaseWidget>::mouseMoveEvent(QMouseEvent* event)
{
event->ignore();
if (processEvent<QMouseEvent, &IDrawWidgetInputInterface::mouseMoveEvent>(event))
{
return;
}
performMouseOperation(event->pos());
updateCursor();
event->accept();
}
template<typename BaseWidget>
void PDFDrawWidgetBase<BaseWidget>::updateCursor()
{
std::optional<QCursor> cursor;
for (IDrawWidgetInputInterface* inputInterface : m_widget->getInputInterfaces())
{
cursor = inputInterface->getCursor();
if (cursor)
{
// We have found cursor
break;
}
}
if (!cursor)
{
switch (m_mouseOperation)
{
case MouseOperation::None:
cursor = QCursor(Qt::OpenHandCursor);
break;
case MouseOperation::Translate:
cursor = QCursor(Qt::ClosedHandCursor);
break;
default:
Q_ASSERT(false);
break;
}
}
if (cursor)
{
this->setCursor(*cursor);
}
else
{
this->unsetCursor();
}
}
template<typename BaseWidget>
void PDFDrawWidgetBase<BaseWidget>::wheelEvent(QWheelEvent* event)
{
event->ignore();
if (processEvent<QWheelEvent, &IDrawWidgetInputInterface::wheelEvent>(event))
{
return;
}
Qt::KeyboardModifiers keyboardModifiers = QApplication::keyboardModifiers();
PDFDrawWidgetProxy* proxy = m_widget->getDrawWidgetProxy();
if (keyboardModifiers.testFlag(Qt::ControlModifier))
{
// Zoom in/Zoom out
const int angleDeltaY = event->angleDelta().y();
const PDFReal zoom = m_widget->getDrawWidgetProxy()->getZoom();
const PDFReal zoomStep = std::pow(PDFDrawWidgetProxy::ZOOM_STEP, static_cast<PDFReal>(angleDeltaY) / static_cast<PDFReal>(QWheelEvent::DefaultDeltasPerStep));
const PDFReal newZoom = zoom * zoomStep;
proxy->zoom(newZoom);
}
else
{
// Move Up/Down. Angle is negative, if wheel is scrolled down. First we try to scroll by pixel delta.
// Otherwise we compute scroll using angle.
QPoint scrollByPixels = event->pixelDelta();
if (scrollByPixels.isNull())
{
const QPoint angleDelta = event->angleDelta();
const bool shiftModifier = keyboardModifiers.testFlag(Qt::ShiftModifier);
int stepVertical = 0;
int stepHorizontal = shiftModifier ? m_widget->getHorizontalScrollbar()->pageStep() : m_widget->getHorizontalScrollbar()->singleStep();
if (proxy->isBlockMode())
{
// In block mode, we must calculate pixel offsets differently - scrollbars corresponds to indices of blocks,
// not to the pixels.
QRect boundingBox = proxy->getPagesIntersectingRectBoundingBox(this->rect());
if (boundingBox.isEmpty())
{
// This occurs, when we have not opened a document
boundingBox = this->rect();
}
stepVertical = shiftModifier ? boundingBox.height() : boundingBox.height() / 10;
}
else
{
stepVertical = shiftModifier ? m_widget->getVerticalScrollbar()->pageStep() : m_widget->getVerticalScrollbar()->singleStep();
}
const int scrollVertical = stepVertical * static_cast<PDFReal>(angleDelta.y()) / static_cast<PDFReal>(QWheelEvent::DefaultDeltasPerStep);
const int scrollHorizontal = stepHorizontal * static_cast<PDFReal>(angleDelta.x()) / static_cast<PDFReal>(QWheelEvent::DefaultDeltasPerStep);
scrollByPixels = QPoint(scrollHorizontal, scrollVertical);
}
QPoint offset = proxy->scrollByPixels(scrollByPixels);
if (offset.y() == 0 && scrollByPixels.y() != 0 && proxy->isBlockMode())
{
// We must move to another block (we are in block mode)
bool up = scrollByPixels.y() > 0;
m_widget->getVerticalScrollbar()->setValue(m_widget->getVerticalScrollbar()->value() + (up ? -1 : 1));
proxy->scrollByPixels(QPoint(0, up ? std::numeric_limits<int>::min() : std::numeric_limits<int>::max()));
}
}
event->accept();
}
#ifdef PDF4QT_ENABLE_OPENGL
PDFOpenGLDrawWidget::PDFOpenGLDrawWidget(PDFWidget* widget, int samplesCount, QWidget* parent) :
BaseClass(widget, parent)
{
QSurfaceFormat format = this->format();
format.setProfile(QSurfaceFormat::CoreProfile);
format.setSamples(samplesCount);
format.setColorSpace(QColorSpace(QColorSpace::SRgb));
format.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
setFormat(format);
}
PDFOpenGLDrawWidget::~PDFOpenGLDrawWidget()
{
}
void PDFOpenGLDrawWidget::resizeGL(int w, int h)
{
QOpenGLWidget::resizeGL(w, h);
getPDFWidget()->getDrawWidgetProxy()->update();
}
void PDFOpenGLDrawWidget::initializeGL()
{
QOpenGLWidget::initializeGL();
}
void PDFOpenGLDrawWidget::paintGL()
{
if (this->isValid())
{
QPainter painter(this);
getPDFWidget()->getDrawWidgetProxy()->draw(&painter, this->rect());
}
}
#endif
PDFDrawWidget::PDFDrawWidget(PDFWidget* widget, QWidget* parent) :
BaseClass(widget, parent)
{
}
PDFDrawWidget::~PDFDrawWidget()
{
}
void PDFDrawWidget::paintEvent(QPaintEvent* event)
{
Q_UNUSED(event);
QPainter painter(this);
getPDFWidget()->getDrawWidgetProxy()->draw(&painter, this->rect());
}
void PDFDrawWidget::resizeEvent(QResizeEvent* event)
{
BaseClass::resizeEvent(event);
getPDFWidget()->getDrawWidgetProxy()->update();
}
#ifdef PDF4QT_ENABLE_OPENGL
template class PDFDrawWidgetBase<QOpenGLWidget>;
#endif
template class PDFDrawWidgetBase<QWidget>;
} // namespace pdf

View File

@ -0,0 +1,226 @@
// Copyright (C) 2018-2021 Jakub Melka
//
// This file is part of PDF4QT.
//
// PDF4QT is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// with the written consent of the copyright owner, any later version.
//
// PDF4QT is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
#ifndef PDFDRAWWIDGET_H
#define PDFDRAWWIDGET_H
#include "pdfglobal.h"
#include "pdfrenderer.h"
#include <QWidget>
#include <QScrollBar>
#ifdef PDF4QT_ENABLE_OPENGL
#include <QOpenGLWidget>
#endif
namespace pdf
{
class PDFDocument;
class PDFCMSManager;
class PDFToolManager;
class PDFDrawWidget;
class PDFFormManager;
class PDFDrawWidgetProxy;
class PDFModifiedDocument;
class PDFWidgetAnnotationManager;
class IDrawWidgetInputInterface;
class IDrawWidget
{
public:
virtual ~IDrawWidget() = default;
virtual QWidget* getWidget() = 0;
/// Returns page indices, which are currently displayed in the widget
virtual std::vector<PDFInteger> getCurrentPages() const = 0;
/// Runs event on this widget
virtual bool doEvent(QEvent* event) = 0;
};
class PDF4QTLIBSHARED_EXPORT PDFWidget : public QWidget
{
Q_OBJECT
public:
/// Constructs new PDFWidget.
/// \param cmsManager Color management system manager
/// \param engine Rendering engine type
/// \param samplesCount Samples count for rendering engine MSAA antialiasing
explicit PDFWidget(const PDFCMSManager* cmsManager, RendererEngine engine, int samplesCount, QWidget* parent);
virtual ~PDFWidget() override;
virtual bool focusNextPrevChild(bool next) override;
using PageRenderingErrors = std::map<PDFInteger, QList<PDFRenderError>>;
/// Sets the document to be viewed in this widget. Document can be nullptr,
/// in that case, widget contents are cleared. Optional content activity can be nullptr,
/// if this occurs, no content is suppressed.
/// \param document Document
void setDocument(const PDFModifiedDocument& document);
/// Update rendering engine according the settings
/// \param engine Engine type
/// \param samplesCount Samples count for rendering engine MSAA antialiasing
void updateRenderer(RendererEngine engine, int samplesCount);
/// Updates cache limits
/// \param compiledPageCacheLimit Compiled page cache limit [bytes]
/// \param thumbnailsCacheLimit Thumbnail image cache limit [kB]
/// \param fontCacheLimit Font cache limit [-]
/// \param instancedFontCacheLimit Instanced font cache limit [-]
void updateCacheLimits(int compiledPageCacheLimit, int thumbnailsCacheLimit, int fontCacheLimit, int instancedFontCacheLimit);
const PDFCMSManager* getCMSManager() const { return m_cmsManager; }
PDFToolManager* getToolManager() const { return m_toolManager; }
PDFWidgetAnnotationManager* getAnnotationManager() const { return m_annotationManager; }
IDrawWidget* getDrawWidget() const { return m_drawWidget; }
QScrollBar* getHorizontalScrollbar() const { return m_horizontalScrollBar; }
QScrollBar* getVerticalScrollbar() const { return m_verticalScrollBar; }
PDFDrawWidgetProxy* getDrawWidgetProxy() const { return m_proxy; }
const PageRenderingErrors* getPageRenderingErrors() const { return &m_pageRenderingErrors; }
int getPageRenderingErrorCount() const;
const std::vector<IDrawWidgetInputInterface*>& getInputInterfaces() const { return m_inputInterfaces; }
void setToolManager(PDFToolManager* toolManager);
void setAnnotationManager(PDFWidgetAnnotationManager* annotationManager);
PDFFormManager* getFormManager() const;
void setFormManager(PDFFormManager* formManager);
void removeInputInterface(IDrawWidgetInputInterface* inputInterface);
void addInputInterface(IDrawWidgetInputInterface* inputInterface);
signals:
void pageRenderingErrorsChanged(pdf::PDFInteger pageIndex, int errorsCount);
private:
RendererEngine getEffectiveRenderer(RendererEngine rendererEngine);
void updateRendererImpl();
void onRenderingError(PDFInteger pageIndex, const QList<PDFRenderError>& errors);
void onPageImageChanged(bool all, const std::vector<PDFInteger>& pages);
IDrawWidget* createDrawWidget(RendererEngine rendererEngine, int samplesCount);
const PDFCMSManager* m_cmsManager;
PDFToolManager* m_toolManager;
PDFWidgetAnnotationManager* m_annotationManager;
PDFFormManager* m_formManager;
IDrawWidget* m_drawWidget;
QScrollBar* m_horizontalScrollBar;
QScrollBar* m_verticalScrollBar;
PDFDrawWidgetProxy* m_proxy;
PageRenderingErrors m_pageRenderingErrors;
std::vector<IDrawWidgetInputInterface*> m_inputInterfaces;
};
template<typename BaseWidget>
class PDFDrawWidgetBase : public BaseWidget, public IDrawWidget
{
public:
explicit PDFDrawWidgetBase(PDFWidget* widget, QWidget* parent);
virtual ~PDFDrawWidgetBase() override = default;
/// Returns page indices, which are currently displayed in the widget
virtual std::vector<PDFInteger> getCurrentPages() const override;
virtual QSize minimumSizeHint() const override;
virtual QWidget* getWidget() override { return this; }
virtual bool doEvent(QEvent* event) override { return this->event(event); }
protected:
virtual bool event(QEvent* event) override;
virtual void keyPressEvent(QKeyEvent* event) override;
virtual void keyReleaseEvent(QKeyEvent* event) override;
virtual void mousePressEvent(QMouseEvent* event) override;
virtual void mouseDoubleClickEvent(QMouseEvent *event) override;
virtual void mouseReleaseEvent(QMouseEvent* event) override;
virtual void mouseMoveEvent(QMouseEvent* event) override;
virtual void wheelEvent(QWheelEvent* event) override;
PDFWidget* getPDFWidget() const { return m_widget; }
private:
void updateCursor();
template<typename Event, void(IDrawWidgetInputInterface::* Function)(QWidget*, Event*)>
bool processEvent(Event* event);
enum class MouseOperation
{
None,
Translate
};
/// Performs the mouse operation (under the current mouse position)
/// \param currentMousePosition Current position of the mouse
void performMouseOperation(QPoint currentMousePosition);
PDFWidget* m_widget;
QPoint m_lastMousePosition;
MouseOperation m_mouseOperation;
};
class PDFDrawWidget : public PDFDrawWidgetBase<QWidget>
{
Q_OBJECT
private:
using BaseClass = PDFDrawWidgetBase<QWidget>;
public:
explicit PDFDrawWidget(PDFWidget* widget, QWidget* parent);
virtual ~PDFDrawWidget() override;
protected:
virtual void paintEvent(QPaintEvent* event) override;
virtual void resizeEvent(QResizeEvent* event) override;
};
#ifdef PDF4QT_ENABLE_OPENGL
class PDFOpenGLDrawWidget : public PDFDrawWidgetBase<QOpenGLWidget>
{
Q_OBJECT
private:
using BaseClass = PDFDrawWidgetBase<QOpenGLWidget>;
public:
explicit PDFOpenGLDrawWidget(PDFWidget* widget, int samplesCount, QWidget* parent);
virtual ~PDFOpenGLDrawWidget() override;
protected:
virtual void resizeGL(int w, int h) override;
virtual void initializeGL() override;
virtual void paintGL() override;
};
#else
using PDFOpenGLDrawWidget = PDFDrawWidget;
#endif
#ifdef PDF4QT_ENABLE_OPENGL
extern template class PDFDrawWidgetBase<QOpenGLWidget>;
#endif
extern template class PDFDrawWidgetBase<QWidget>;
} // namespace pdf
#endif // PDFDRAWWIDGET_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,76 @@
// Copyright (C) 2020-2021 Jakub Melka
//
// This file is part of PDF4QT.
//
// PDF4QT is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// with the written consent of the copyright owner, any later version.
//
// PDF4QT is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
#ifndef PDFOBJECTEDITORWIDGET_H
#define PDFOBJECTEDITORWIDGET_H
#include "pdfobjecteditormodel.h"
#include <QWidget>
#include <QDialog>
class QTabWidget;
class QDialogButtonBox;
namespace pdf
{
class PDFObjectEditorWidgetMapper;
enum class EditObjectType
{
Annotation
};
class PDFObjectEditorWidget : public QWidget
{
Q_OBJECT
private:
using BaseClass = QWidget;
public:
explicit PDFObjectEditorWidget(EditObjectType type, QWidget* parent);
void setObject(PDFObject object);
PDFObject getObject();
private:
PDFObjectEditorWidgetMapper* m_mapper;
QTabWidget* m_tabWidget;
};
class PDFEditObjectDialog : public QDialog
{
Q_OBJECT
private:
using BaseClass = QDialog;
public:
explicit PDFEditObjectDialog(EditObjectType type, QWidget* parent);
void setObject(PDFObject object);
PDFObject getObject();
private:
PDFObjectEditorWidget* m_widget;
QDialogButtonBox* m_buttonBox;
};
} // namespace pdf
#endif // PDFOBJECTEDITORWIDGET_H

View File

@ -0,0 +1,296 @@
// Copyright (C) 2020-2021 Jakub Melka
//
// This file is part of PDF4QT.
//
// PDF4QT is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// with the written consent of the copyright owner, any later version.
//
// PDF4QT is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
#ifndef PDFOBJECTEDITORWIDGET_IMPL_H
#define PDFOBJECTEDITORWIDGET_IMPL_H
#include "pdfobjecteditormodel.h"
class QLabel;
class QGroupBox;
class QComboBox;
class QTabWidget;
class QGridLayout;
class QLineEdit;
class QTextBrowser;
class QPushButton;
class QDateTimeEdit;
class QCheckBox;
class QDoubleSpinBox;
namespace pdf
{
class PDFObjectEditorMappedWidgetAdapter : public QObject
{
Q_OBJECT
private:
using BaseClass = QObject;
public:
explicit PDFObjectEditorMappedWidgetAdapter(PDFObjectEditorAbstractModel* model, size_t attribute, QObject* parent);
/// Returns PDFObject value currently present in the widget
virtual PDFObject getValue() const = 0;
/// Sets PDFObject value to the widget. If data are incompatible,
/// then no data are set to the widget.
/// \param object Value to be set to the widget
virtual void setValue(PDFObject object) = 0;
/// Updates widget state (for example, visibility, enabledness and readonly mode)
virtual void update() = 0;
signals:
void commitRequested(size_t attribute);
protected:
// Initializes label text with attributes name
void initLabel(QLabel* label);
PDFObjectEditorAbstractModel* m_model;
size_t m_attribute;
};
class PDFObjectEditorWidgetMapper : public QObject
{
Q_OBJECT
private:
using BaseClass = QObject;
public:
explicit PDFObjectEditorWidgetMapper(PDFObjectEditorAbstractModel* model, QObject* parent);
void initialize(QTabWidget* tabWidget);
void setObject(PDFObject object);
PDFObject getObject();
private:
struct Subcategory
{
QString name;
std::vector<size_t> attributes;
};
struct Category
{
QString name;
std::vector<Subcategory> subcategories;
QWidget* page = nullptr;
Subcategory* getOrCreateSubcategory(QString name);
};
void loadWidgets();
void onEditedObjectChanged();
void onCommitRequested(size_t attribute);
void createMappedAdapter(QGroupBox* groupBox, QGridLayout* layout, size_t attribute);
Category* getOrCreateCategory(QString categoryName);
/// Returns true, if category is visible
/// \param category Category
bool isCategoryVisible(const Category& category) const;
PDFObjectEditorAbstractModel* m_model;
QTabWidget* m_tabWidget;
std::vector<Category> m_categories;
std::map<size_t, PDFObjectEditorMappedWidgetAdapter*> m_adapters;
bool m_isCommitingDisabled;
};
class PDFObjectEditorMappedComboBoxAdapter : public PDFObjectEditorMappedWidgetAdapter
{
Q_OBJECT
private:
using BaseClass = PDFObjectEditorMappedWidgetAdapter;
public:
explicit PDFObjectEditorMappedComboBoxAdapter(QLabel* label, QComboBox* comboBox, PDFObjectEditorAbstractModel* model, size_t attribute, QObject* parent);
virtual PDFObject getValue() const override;
virtual void setValue(PDFObject object) override;
virtual void update() override;
private:
QLabel* m_label;
QComboBox* m_comboBox;
};
class PDFObjectEditorMappedLineEditAdapter : public PDFObjectEditorMappedWidgetAdapter
{
Q_OBJECT
private:
using BaseClass = PDFObjectEditorMappedWidgetAdapter;
public:
explicit PDFObjectEditorMappedLineEditAdapter(QLabel* label, QLineEdit* lineEdit, PDFObjectEditorAbstractModel* model, size_t attribute, QObject* parent);
virtual PDFObject getValue() const override;
virtual void setValue(PDFObject object) override;
virtual void update() override;
private:
QLabel* m_label;
QLineEdit* m_lineEdit;
};
class PDFObjectEditorMappedTextBrowserAdapter : public PDFObjectEditorMappedWidgetAdapter
{
Q_OBJECT
private:
using BaseClass = PDFObjectEditorMappedWidgetAdapter;
public:
explicit PDFObjectEditorMappedTextBrowserAdapter(QLabel* label, QTextBrowser* textBrowser, PDFObjectEditorAbstractModel* model, size_t attribute, QObject* parent);
virtual PDFObject getValue() const override;
virtual void setValue(PDFObject object) override;
virtual void update() override;
private:
QLabel* m_label;
QTextBrowser* m_textBrowser;
};
class PDFObjectEditorMappedRectangleAdapter : public PDFObjectEditorMappedWidgetAdapter
{
Q_OBJECT
private:
using BaseClass = PDFObjectEditorMappedWidgetAdapter;
public:
explicit PDFObjectEditorMappedRectangleAdapter(QLabel* label, QPushButton* pushButton, PDFObjectEditorAbstractModel* model, size_t attribute, QObject* parent);
virtual PDFObject getValue() const override;
virtual void setValue(PDFObject object) override;
virtual void update() override;
private:
QLabel* m_label;
QPushButton* m_pushButton;
PDFObject m_rectangle;
};
class PDFObjectEditorMappedDateTimeAdapter : public PDFObjectEditorMappedWidgetAdapter
{
Q_OBJECT
private:
using BaseClass = PDFObjectEditorMappedWidgetAdapter;
public:
explicit PDFObjectEditorMappedDateTimeAdapter(QLabel* label, QDateTimeEdit* dateTimeEdit, PDFObjectEditorAbstractModel* model, size_t attribute, QObject* parent);
virtual PDFObject getValue() const override;
virtual void setValue(PDFObject object) override;
virtual void update() override;
private:
QLabel* m_label;
QDateTimeEdit* m_dateTimeEdit;
};
class PDFObjectEditorMappedFlagsAdapter : public PDFObjectEditorMappedWidgetAdapter
{
Q_OBJECT
private:
using BaseClass = PDFObjectEditorMappedWidgetAdapter;
public:
explicit PDFObjectEditorMappedFlagsAdapter(std::vector<std::pair<uint32_t, QCheckBox*>> flagCheckBoxes,
PDFObjectEditorAbstractModel* model,
size_t attribute,
QObject* parent);
virtual PDFObject getValue() const override;
virtual void setValue(PDFObject object) override;
virtual void update() override;
private:
std::vector<std::pair<uint32_t, QCheckBox*>> m_flagCheckBoxes;
};
class PDFObjectEditorMappedCheckBoxAdapter : public PDFObjectEditorMappedWidgetAdapter
{
Q_OBJECT
private:
using BaseClass = PDFObjectEditorMappedWidgetAdapter;
public:
explicit PDFObjectEditorMappedCheckBoxAdapter(QLabel* label, QCheckBox* checkBox, PDFObjectEditorAbstractModel* model, size_t attribute, QObject* parent);
virtual PDFObject getValue() const override;
virtual void setValue(PDFObject object) override;
virtual void update() override;
private:
QLabel* m_label;
QCheckBox* m_checkBox;
};
class PDFObjectEditorMappedColorAdapter : public PDFObjectEditorMappedWidgetAdapter
{
Q_OBJECT
private:
using BaseClass = PDFObjectEditorMappedWidgetAdapter;
public:
explicit PDFObjectEditorMappedColorAdapter(QLabel* label, QComboBox* comboBox, PDFObjectEditorAbstractModel* model, size_t attribute, QObject* parent);
virtual PDFObject getValue() const override;
virtual void setValue(PDFObject object) override;
virtual void update() override;
private:
QIcon getIconForColor(QColor color) const;
QLabel* m_label;
QComboBox* m_comboBox;
};
class PDFObjectEditorMappedDoubleAdapter : public PDFObjectEditorMappedWidgetAdapter
{
Q_OBJECT
private:
using BaseClass = PDFObjectEditorMappedWidgetAdapter;
public:
explicit PDFObjectEditorMappedDoubleAdapter(QLabel* label, QDoubleSpinBox* spinBox, PDFObjectEditorAbstractModel* model, size_t attribute, QObject* parent);
virtual PDFObject getValue() const override;
virtual void setValue(PDFObject object) override;
virtual void update() override;
private:
QLabel* m_label;
QDoubleSpinBox* m_spinBox;
};
} // namespace pdf
#endif // PDFOBJECTEDITORWIDGET_IMPL_H

View File

@ -0,0 +1,548 @@
// Copyright (C) 2022 Jakub Melka
//
// This file is part of PDF4QT.
//
// PDF4QT is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// with the written consent of the copyright owner, any later version.
//
// PDF4QT is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
#include "pdfpagecontenteditorstylesettings.h"
#include "ui_pdfpagecontenteditorstylesettings.h"
#include "pdfwidgetutils.h"
#include "pdfpagecontentelements.h"
#include <QFontDialog>
#include <QColorDialog>
#include <QLineEdit>
#include <QVBoxLayout>
#include <QGroupBox>
#include <QDialogButtonBox>
#include <QTextEdit>
namespace pdf
{
PDFPageContentEditorStyleSettings::PDFPageContentEditorStyleSettings(QWidget* parent) :
QWidget(parent),
ui(new Ui::PDFPageContentEditorStyleSettings)
{
ui->setupUi(this);
for (QString colorName : QColor::colorNames())
{
QColor color(colorName);
QIcon icon = getIconForColor(color);
ui->penColorCombo->addItem(icon, colorName, color);
ui->brushColorCombo->addItem(icon, colorName, color);
}
ui->penStyleCombo->addItem(tr("None"), int(Qt::NoPen));
ui->penStyleCombo->addItem(tr("Solid"), int(Qt::SolidLine));
ui->penStyleCombo->addItem(tr("Dashed"), int(Qt::DashLine));
ui->penStyleCombo->addItem(tr("Dotted"), int(Qt::DotLine));
ui->penStyleCombo->addItem(tr("Dash-dot"), int(Qt::DashDotLine));
ui->penStyleCombo->addItem(tr("Dash-dot-dot"), int(Qt::DashDotDotLine));
ui->brushStyleCombo->addItem(tr("None"), int(Qt::NoBrush));
ui->brushStyleCombo->addItem(tr("Solid"), int(Qt::SolidPattern));
ui->brushStyleCombo->addItem(tr("Horizontal"), int(Qt::HorPattern));
ui->brushStyleCombo->addItem(tr("Vertical"), int(Qt::VerPattern));
ui->brushStyleCombo->addItem(tr("B-Diagonal"), int(Qt::BDiagPattern));
ui->brushStyleCombo->addItem(tr("F-Diagonal"), int(Qt::FDiagPattern));
ui->brushStyleCombo->addItem(tr("Cross"), int(Qt::CrossPattern));
connect(ui->fontComboBox, &QFontComboBox::currentFontChanged, this, &PDFPageContentEditorStyleSettings::onFontChanged);
connect(ui->selectPenColorButton, &QToolButton::clicked, this, &PDFPageContentEditorStyleSettings::onSelectPenColorButtonClicked);
connect(ui->selectBrushColorButton, &QToolButton::clicked, this, &PDFPageContentEditorStyleSettings::onSelectBrushColorButtonClicked);
connect(ui->selectFontButton, &QToolButton::clicked, this, &PDFPageContentEditorStyleSettings::onSelectFontButtonClicked);
connect(ui->penWidthEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &PDFPageContentEditorStyleSettings::onPenWidthChanged);
connect(ui->penStyleCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &PDFPageContentEditorStyleSettings::onPenStyleChanged);
connect(ui->brushStyleCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &PDFPageContentEditorStyleSettings::onBrushStyleChanged);
connect(ui->textAngleEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &PDFPageContentEditorStyleSettings::onTextAngleChanged);
connect(ui->penColorCombo->lineEdit(), &QLineEdit::editingFinished, this, &PDFPageContentEditorStyleSettings::onPenColorComboTextChanged);
connect(ui->penColorCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &PDFPageContentEditorStyleSettings::onPenColorComboIndexChanged);
connect(ui->brushColorCombo->lineEdit(), &QLineEdit::editingFinished, this, &PDFPageContentEditorStyleSettings::onBrushColorComboTextChanged);
connect(ui->brushColorCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &PDFPageContentEditorStyleSettings::onBrushColorComboIndexChanged);
m_alignmentMapper.setMapping(ui->al11Button, int(Qt::AlignLeft | Qt::AlignTop));
m_alignmentMapper.setMapping(ui->al12Button, int(Qt::AlignHCenter | Qt::AlignTop));
m_alignmentMapper.setMapping(ui->al13Button, int(Qt::AlignRight | Qt::AlignTop));
m_alignmentMapper.setMapping(ui->al21Button, int(Qt::AlignLeft | Qt::AlignVCenter));
m_alignmentMapper.setMapping(ui->al22Button, int(Qt::AlignCenter));
m_alignmentMapper.setMapping(ui->al23Button, int(Qt::AlignRight | Qt::AlignVCenter));
m_alignmentMapper.setMapping(ui->al31Button, int(Qt::AlignLeft | Qt::AlignBottom));
m_alignmentMapper.setMapping(ui->al32Button, int(Qt::AlignHCenter | Qt::AlignBottom));
m_alignmentMapper.setMapping(ui->al33Button, int(Qt::AlignRight | Qt::AlignBottom));
for (QRadioButton* radioButton : findChildren<QRadioButton*>())
{
connect(radioButton, &QRadioButton::clicked, &m_alignmentMapper, QOverload<>::of(&QSignalMapper::map));
}
connect(&m_alignmentMapper, &QSignalMapper::mappedInt, this, &PDFPageContentEditorStyleSettings::onAlignmentRadioButtonClicked);
loadFromElement(nullptr, true);
}
PDFPageContentEditorStyleSettings::~PDFPageContentEditorStyleSettings()
{
delete ui;
}
void PDFPageContentEditorStyleSettings::loadFromElement(const PDFPageContentElement* element, bool forceUpdate)
{
const PDFPageContentStyledElement* styledElement = dynamic_cast<const PDFPageContentStyledElement*>(element);
const PDFPageContentElementTextBox* textElement = dynamic_cast<const PDFPageContentElementTextBox*>(element);
StyleFeatures features = None;
if (styledElement)
{
features.setFlag(Pen);
features.setFlag(PenColor);
features.setFlag(Brush);
}
if (textElement)
{
features.setFlag(PenColor);
features.setFlag(Text);
}
const bool hasPen = features.testFlag(Pen);
const bool hasPenColor = features.testFlag(PenColor);
const bool hasBrush = features.testFlag(Brush);
const bool hasText = features.testFlag(Text);
ui->penWidthEdit->setEnabled(hasPen);
ui->penWidthLabel->setEnabled(hasPen);
ui->penStyleCombo->setEnabled(hasPen);
ui->penStyleLabel->setEnabled(hasPen);
ui->penColorCombo->setEnabled(hasPenColor);
ui->penColorLabel->setEnabled(hasPenColor);
ui->selectPenColorButton->setEnabled(hasPenColor);
ui->brushStyleLabel->setEnabled(hasBrush);
ui->brushStyleCombo->setEnabled(hasBrush);
ui->brushColorCombo->setEnabled(hasBrush);
ui->brushColorLabel->setEnabled(hasBrush);
ui->selectBrushColorButton->setEnabled(hasBrush);
ui->fontComboBox->setEnabled(hasText);
ui->fontLabel->setEnabled(hasText);
ui->selectFontButton->setEnabled(hasText);
for (QRadioButton* radioButton : findChildren<QRadioButton*>())
{
radioButton->setEnabled(hasText);
}
ui->textAlignmentLabel->setEnabled(hasText);
ui->textAngleLabel->setEnabled(hasText);
ui->textAngleEdit->setEnabled(hasText);
QPen pen(Qt::SolidLine);
QBrush brush(Qt::transparent);
QFont font = QGuiApplication::font();
Qt::Alignment alignment = Qt::AlignCenter;
PDFReal textAngle = 0.0;
if (styledElement)
{
pen = styledElement->getPen();
brush = styledElement->getBrush();
}
if (textElement)
{
font = textElement->getFont();
alignment = textElement->getAlignment();
textAngle = textElement->getAngle();
}
setPen(pen, forceUpdate);
setBrush(brush, forceUpdate);
setFont(font, forceUpdate);
setFontAlignment(alignment, forceUpdate);
setTextAngle(textAngle, forceUpdate);
}
void PDFPageContentEditorStyleSettings::setPen(const QPen& pen, bool forceUpdate)
{
if (m_pen != pen || forceUpdate)
{
const bool oldBlockSignals = blockSignals(true);
m_pen = pen;
ui->penWidthEdit->setValue(pen.widthF());
ui->penStyleCombo->setCurrentIndex(ui->penStyleCombo->findData(int(pen.style())));
setColorToComboBox(ui->penColorCombo, pen.color());
blockSignals(oldBlockSignals);
Q_EMIT penChanged(m_pen);
}
}
void PDFPageContentEditorStyleSettings::setBrush(const QBrush& brush, bool forceUpdate)
{
if (m_brush != brush || forceUpdate)
{
const bool oldBlockSignals = blockSignals(true);
m_brush = brush;
ui->brushStyleCombo->setCurrentIndex(ui->brushStyleCombo->findData(int(brush.style())));
setColorToComboBox(ui->brushColorCombo, brush.color());
blockSignals(oldBlockSignals);
Q_EMIT brushChanged(m_brush);
}
}
void PDFPageContentEditorStyleSettings::setFont(const QFont& font, bool forceUpdate)
{
if (m_font != font || forceUpdate)
{
const bool oldBlockSignals = blockSignals(true);
m_font = font;
ui->fontComboBox->setCurrentFont(m_font);
blockSignals(oldBlockSignals);
Q_EMIT fontChanged(m_font);
}
}
void PDFPageContentEditorStyleSettings::setFontAlignment(Qt::Alignment alignment, bool forceUpdate)
{
if (m_alignment != alignment || forceUpdate)
{
const bool oldBlockSignals = blockSignals(true);
for (QRadioButton* radioButton : findChildren<QRadioButton*>())
{
radioButton->setChecked(false);
}
m_alignment = alignment;
QRadioButton* radioButton = qobject_cast<QRadioButton*>(m_alignmentMapper.mapping(int(alignment)));
radioButton->setChecked(true);
blockSignals(oldBlockSignals);
Q_EMIT alignmentChanged(m_alignment);
}
}
void PDFPageContentEditorStyleSettings::setTextAngle(PDFReal angle, bool forceUpdate)
{
if (ui->textAngleEdit->value() != angle || forceUpdate)
{
const bool oldBlockSignals = blockSignals(true);
ui->textAngleEdit->setValue(angle);
blockSignals(oldBlockSignals);
Q_EMIT textAngleChanged(ui->textAngleEdit->value());
}
}
bool PDFPageContentEditorStyleSettings::showEditElementStyleDialog(QWidget* parent,
PDFPageContentElement* element)
{
QDialog dialog(parent);
dialog.setWindowTitle(tr("Edit Item"));
dialog.setLayout(new QVBoxLayout());
QTextEdit* textEdit = nullptr;
PDFPageContentStyledElement* styledElement = dynamic_cast<PDFPageContentStyledElement*>(element);
PDFPageContentElementTextBox* textElement = dynamic_cast<PDFPageContentElementTextBox*>(element);
if (textElement)
{
QGroupBox* contentGroupBox = new QGroupBox(&dialog);
textEdit = new QTextEdit(textElement->getText(), contentGroupBox);
textEdit->setFont(textElement->getFont());
textEdit->setAlignment(textElement->getAlignment());
textEdit->setTextColor(textElement->getPen().color());
contentGroupBox->setTitle(tr("Content"));
contentGroupBox->setLayout(new QVBoxLayout());
contentGroupBox->layout()->addWidget(textEdit);
dialog.layout()->addWidget(contentGroupBox);
}
PDFPageContentEditorStyleSettings* appearanceWidget = new PDFPageContentEditorStyleSettings(&dialog);
appearanceWidget->loadFromElement(element, true);
if (textEdit)
{
connect(appearanceWidget, &PDFPageContentEditorStyleSettings::alignmentChanged, textEdit, &QTextEdit::setAlignment);
connect(appearanceWidget, &PDFPageContentEditorStyleSettings::fontChanged, textEdit, &QTextEdit::setFont);
connect(appearanceWidget, &PDFPageContentEditorStyleSettings::penChanged, textEdit, [textEdit](const QPen& pen) { textEdit->setTextColor(pen.color()); });
}
QGroupBox* appearanceGroupBox = new QGroupBox(&dialog);
appearanceGroupBox->setTitle(tr("Appearance"));
appearanceGroupBox->setLayout(new QVBoxLayout());
appearanceGroupBox->layout()->addWidget(appearanceWidget);
dialog.layout()->addWidget(appearanceGroupBox);
QDialogButtonBox* dialogButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, &dialog);
connect(dialogButtonBox, &QDialogButtonBox::accepted, &dialog, &QDialog::accept);
connect(dialogButtonBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
dialog.layout()->addWidget(dialogButtonBox);
if (dialog.exec() == QDialog::Accepted)
{
if (styledElement)
{
styledElement->setPen(appearanceWidget->getPen());
styledElement->setBrush(appearanceWidget->getBrush());
}
if (textElement)
{
textElement->setText(textEdit->toPlainText());
textElement->setFont(appearanceWidget->getFont());
textElement->setAlignment(appearanceWidget->getAlignment());
textElement->setAngle(appearanceWidget->getTextAngle());
}
return true;
}
return false;
}
QIcon PDFPageContentEditorStyleSettings::getIconForColor(QColor color) const
{
QIcon icon;
QSize iconSize = PDFWidgetUtils::scaleDPI(this, QSize(16, 16));
QPixmap pixmap(iconSize.width(), iconSize.height());
pixmap.fill(color);
icon.addPixmap(pixmap);
return icon;
}
void PDFPageContentEditorStyleSettings::setColorToComboBox(QComboBox* comboBox, QColor color)
{
if (!color.isValid())
{
return;
}
QString name = color.name(QColor::HexArgb);
int index = comboBox->findData(color, Qt::UserRole, Qt::MatchExactly);
if (index == -1)
{
// Jakub Melka: try to find text (color name)
index = comboBox->findText(name);
}
if (index != -1)
{
comboBox->setCurrentIndex(index);
}
else
{
comboBox->addItem(getIconForColor(color), name, color);
comboBox->setCurrentIndex(comboBox->count() - 1);
}
}
void PDFPageContentEditorStyleSettings::onSelectFontButtonClicked()
{
bool ok = false;
QFont font = QFontDialog::getFont(&ok, m_font, this, tr("Select Font"));
if (ok && m_font != font)
{
m_font = font;
ui->fontComboBox->setCurrentFont(m_font);
Q_EMIT fontChanged(m_font);
}
}
void PDFPageContentEditorStyleSettings::setPenColor(QColor color)
{
if (color.isValid() && m_pen.color() != color)
{
m_pen.setColor(color);
setColorToComboBox(ui->penColorCombo, color);
Q_EMIT penChanged(m_pen);
}
}
void PDFPageContentEditorStyleSettings::onSelectPenColorButtonClicked()
{
QColor color = QColorDialog::getColor(m_pen.color(), this, tr("Select Color for Pen"), QColorDialog::ShowAlphaChannel);
setPenColor(color);
}
void PDFPageContentEditorStyleSettings::setBrushColor(QColor color)
{
if (color.isValid() && m_brush.color() != color)
{
m_brush.setColor(color);
setColorToComboBox(ui->brushColorCombo, color);
Q_EMIT brushChanged(m_brush);
}
}
Qt::Alignment PDFPageContentEditorStyleSettings::getAlignment() const
{
return m_alignment;
}
PDFReal PDFPageContentEditorStyleSettings::getTextAngle() const
{
return ui->textAngleEdit->value();
}
const QFont& PDFPageContentEditorStyleSettings::getFont() const
{
return m_font;
}
const QBrush& PDFPageContentEditorStyleSettings::getBrush() const
{
return m_brush;
}
const QPen& PDFPageContentEditorStyleSettings::getPen() const
{
return m_pen;
}
void PDFPageContentEditorStyleSettings::onSelectBrushColorButtonClicked()
{
QColor color = QColorDialog::getColor(m_pen.color(), this, tr("Select Color for Brush"), QColorDialog::ShowAlphaChannel);
setBrushColor(color);
}
void PDFPageContentEditorStyleSettings::onPenWidthChanged(double value)
{
if (m_pen.widthF() != value)
{
m_pen.setWidthF(value);
Q_EMIT penChanged(m_pen);
}
}
void PDFPageContentEditorStyleSettings::onTextAngleChanged(double value)
{
Q_EMIT textAngleChanged(value);
}
void PDFPageContentEditorStyleSettings::onAlignmentRadioButtonClicked(int alignment)
{
Qt::Alignment alignmentValue = static_cast<Qt::Alignment>(alignment);
if (m_alignment != alignmentValue)
{
m_alignment = alignmentValue;
Q_EMIT alignmentChanged(m_alignment);
}
}
void PDFPageContentEditorStyleSettings::onPenStyleChanged()
{
Qt::PenStyle penStyle = static_cast<Qt::PenStyle>(ui->penStyleCombo->currentData().toInt());
if (m_pen.style() != penStyle)
{
m_pen.setStyle(penStyle);
Q_EMIT penChanged(m_pen);
}
}
void PDFPageContentEditorStyleSettings::onBrushStyleChanged()
{
Qt::BrushStyle brushStyle = static_cast<Qt::BrushStyle>(ui->brushStyleCombo->currentData().toInt());
if (m_brush.style() != brushStyle)
{
m_brush.setStyle(brushStyle);
Q_EMIT brushChanged(m_brush);
}
}
void PDFPageContentEditorStyleSettings::onPenColorComboTextChanged()
{
QColor color(ui->penColorCombo->currentText());
if (color.isValid())
{
setColorToComboBox(ui->penColorCombo, color);
if (m_pen.color() != color)
{
m_pen.setColor(color);
Q_EMIT penChanged(m_pen);
}
}
else if (ui->penColorCombo->currentIndex() != -1)
{
ui->penColorCombo->setEditText(ui->penColorCombo->itemText(ui->penColorCombo->currentIndex()));
}
}
void PDFPageContentEditorStyleSettings::onPenColorComboIndexChanged()
{
const int index = ui->penColorCombo->currentIndex();
QColor color = ui->penColorCombo->itemData(index, Qt::UserRole).value<QColor>();
if (color.isValid() && m_pen.color() != color)
{
m_pen.setColor(color);
Q_EMIT penChanged(m_pen);
}
}
void PDFPageContentEditorStyleSettings::onBrushColorComboTextChanged()
{
QColor color(ui->brushColorCombo->currentText());
if (color.isValid())
{
setColorToComboBox(ui->brushColorCombo, color);
if (m_brush.color() != color)
{
m_brush.setColor(color);
Q_EMIT brushChanged(m_brush);
}
}
else if (ui->brushColorCombo->currentIndex() != -1)
{
ui->brushColorCombo->setEditText(ui->brushColorCombo->itemText(ui->brushColorCombo->currentIndex()));
}
}
void PDFPageContentEditorStyleSettings::onBrushColorComboIndexChanged()
{
const int index = ui->brushColorCombo->currentIndex();
QColor color = ui->brushColorCombo->itemData(index, Qt::UserRole).value<QColor>();
if (color.isValid() && m_brush.color() != color)
{
m_brush.setColor(color);
Q_EMIT brushChanged(m_brush);
}
}
void PDFPageContentEditorStyleSettings::onFontChanged(const QFont& font)
{
if (m_font != font)
{
m_font = font;
Q_EMIT fontChanged(m_font);
}
}
} // namespace pdf

View File

@ -0,0 +1,118 @@
// Copyright (C) 2022 Jakub Melka
//
// This file is part of PDF4QT.
//
// PDF4QT is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// with the written consent of the copyright owner, any later version.
//
// PDF4QT is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
#ifndef PDFPAGECONTENTEDITORSTYLESETTINGS_H
#define PDFPAGECONTENTEDITORSTYLESETTINGS_H
#include "pdfglobal.h"
#include <QPen>
#include <QIcon>
#include <QFont>
#include <QBrush>
#include <QWidget>
#include <QSignalMapper>
namespace Ui
{
class PDFPageContentEditorStyleSettings;
}
class QComboBox;
namespace pdf
{
class PDFPageContentElement;
class PDF4QTLIBSHARED_EXPORT PDFPageContentEditorStyleSettings : public QWidget
{
Q_OBJECT
public:
enum StyleFeature
{
None = 0,
Pen = 1 << 0,
PenColor = 1 << 1,
Brush = 1 << 2,
Text = 1 << 3
};
Q_DECLARE_FLAGS(StyleFeatures, StyleFeature)
explicit PDFPageContentEditorStyleSettings(QWidget* parent);
virtual ~PDFPageContentEditorStyleSettings() override;
/// Loads data from element, element can be nullptr
/// \param element Element
void loadFromElement(const PDFPageContentElement* element, bool forceUpdate);
void setPen(const QPen& pen, bool forceUpdate);
void setBrush(const QBrush& brush, bool forceUpdate);
void setFont(const QFont& font, bool forceUpdate);
void setFontAlignment(Qt::Alignment alignment, bool forceUpdate);
void setTextAngle(PDFReal angle, bool forceUpdate);
static bool showEditElementStyleDialog(QWidget* parent, PDFPageContentElement* element);
const QPen& getPen() const;
const QBrush& getBrush() const;
const QFont& getFont() const;
Qt::Alignment getAlignment() const;
PDFReal getTextAngle() const;
signals:
void penChanged(const QPen& pen);
void brushChanged(const QBrush& brush);
void fontChanged(const QFont& font);
void alignmentChanged(Qt::Alignment alignment);
void textAngleChanged(pdf::PDFReal angle);
private slots:
void onSelectFontButtonClicked();
void onSelectPenColorButtonClicked();
void onSelectBrushColorButtonClicked();
void onPenWidthChanged(double value);
void onTextAngleChanged(double value);
void onAlignmentRadioButtonClicked(int alignment);
void onPenStyleChanged();
void onBrushStyleChanged();
void onPenColorComboTextChanged();
void onPenColorComboIndexChanged();
void onBrushColorComboTextChanged();
void onBrushColorComboIndexChanged();
private:
Ui::PDFPageContentEditorStyleSettings* ui;
void onFontChanged(const QFont& font);
void setColorToComboBox(QComboBox* comboBox, QColor color);
QIcon getIconForColor(QColor color) const;
void setPenColor(QColor color);
void setBrushColor(QColor color);
QPen m_pen;
QBrush m_brush;
QFont m_font;
Qt::Alignment m_alignment = Qt::AlignCenter;
QSignalMapper m_alignmentMapper;
};
} // namespace pdf
#endif // PDFPAGECONTENTEDITORSTYLESETTINGS_H

View File

@ -0,0 +1,230 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PDFPageContentEditorStyleSettings</class>
<widget class="QWidget" name="PDFPageContentEditorStyleSettings">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>344</width>
<height>310</height>
</rect>
</property>
<property name="windowTitle">
<string>Style Settings</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="7" column="0">
<widget class="QLabel" name="textAlignmentLabel">
<property name="text">
<string>Text Alignment</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="penStyleLabel">
<property name="text">
<string>Pen Style</string>
</property>
</widget>
</item>
<item row="8" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::MinimumExpanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="brushColorCombo">
<property name="editable">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="1" colspan="2">
<widget class="QComboBox" name="penStyleCombo"/>
</item>
<item row="0" column="1" colspan="2">
<widget class="QDoubleSpinBox" name="penWidthEdit"/>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="penColorCombo">
<property name="editable">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="penWidthLabel">
<property name="text">
<string>Pen Width</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="brushColorLabel">
<property name="text">
<string>Brush Color</string>
</property>
</widget>
</item>
<item row="7" column="1" colspan="2">
<layout class="QGridLayout" name="textAlignmentLayout">
<item row="1" column="0">
<widget class="QRadioButton" name="al21Button">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QRadioButton" name="al13Button">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QRadioButton" name="al32Button">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QRadioButton" name="al22Button">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QRadioButton" name="al33Button">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QRadioButton" name="al31Button">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QRadioButton" name="al23Button">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QRadioButton" name="al11Button">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QRadioButton" name="al12Button">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="3">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="6" column="1" colspan="2">
<widget class="QDoubleSpinBox" name="textAngleEdit">
<property name="minimum">
<double>-90.000000000000000</double>
</property>
<property name="maximum">
<double>90.000000000000000</double>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QToolButton" name="selectBrushColorButton">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="penColorLabel">
<property name="text">
<string>Pen Color</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="fontLabel">
<property name="text">
<string>Font</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QFontComboBox" name="fontComboBox"/>
</item>
<item row="6" column="0">
<widget class="QLabel" name="textAngleLabel">
<property name="text">
<string>Text Angle</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QToolButton" name="selectPenColorButton">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="QToolButton" name="selectFontButton">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="brushStyleLabel">
<property name="text">
<string>Brush Style</string>
</property>
</widget>
</item>
<item row="3" column="1" colspan="2">
<widget class="QComboBox" name="brushStyleCombo"/>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,969 @@
// Copyright (C) 2022 Jakub Melka
//
// This file is part of PDF4QT.
//
// PDF4QT is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// with the written consent of the copyright owner, any later version.
//
// PDF4QT is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
#include "pdfpagecontenteditortools.h"
#include "pdfpagecontentelements.h"
#include "pdfpainterutils.h"
#include "pdftexteditpseudowidget.h"
#include "pdfdrawwidget.h"
#include <QPen>
#include <QPainter>
#include <QMouseEvent>
#include <QFileDialog>
#include <QImageReader>
#include <QGuiApplication>
namespace pdf
{
PDFCreatePCElementTool::PDFCreatePCElementTool(PDFDrawWidgetProxy* proxy,
PDFPageContentScene* scene,
QAction* action,
QObject* parent) :
PDFWidgetTool(proxy, action, parent),
m_scene(scene)
{
}
void PDFCreatePCElementTool::setPen(const QPen& pen)
{
if (PDFPageContentStyledElement* styledElement = dynamic_cast<PDFPageContentStyledElement*>(getElement()))
{
styledElement->setPen(pen);
Q_EMIT getProxy()->repaintNeeded();
}
}
void PDFCreatePCElementTool::setBrush(const QBrush& brush)
{
if (PDFPageContentStyledElement* styledElement = dynamic_cast<PDFPageContentStyledElement*>(getElement()))
{
styledElement->setBrush(brush);
Q_EMIT getProxy()->repaintNeeded();
}
}
void PDFCreatePCElementTool::setFont(const QFont& font)
{
if (PDFPageContentElementTextBox* textBoxElement = dynamic_cast<PDFPageContentElementTextBox*>(getElement()))
{
textBoxElement->setFont(font);
Q_EMIT getProxy()->repaintNeeded();
}
}
void PDFCreatePCElementTool::setAlignment(Qt::Alignment alignment)
{
if (PDFPageContentElementTextBox* textBoxElement = dynamic_cast<PDFPageContentElementTextBox*>(getElement()))
{
textBoxElement->setAlignment(alignment);
Q_EMIT getProxy()->repaintNeeded();
}
}
void PDFCreatePCElementTool::setTextAngle(PDFReal angle)
{
if (PDFPageContentElementTextBox* textBoxElement = dynamic_cast<PDFPageContentElementTextBox*>(getElement()))
{
textBoxElement->setAngle(angle);
Q_EMIT getProxy()->repaintNeeded();
}
}
QRectF PDFCreatePCElementTool::getRectangleFromPickTool(PDFPickTool* pickTool,
const QTransform& pagePointToDevicePointMatrix)
{
const std::vector<QPointF>& points = pickTool->getPickedPoints();
if (points.empty())
{
return QRectF();
}
QPointF mousePoint = pagePointToDevicePointMatrix.inverted().map(pickTool->getSnappedPoint());
QPointF point = points.front();
qreal xMin = qMin(point.x(), mousePoint.x());
qreal xMax = qMax(point.x(), mousePoint.x());
qreal yMin = qMin(point.y(), mousePoint.y());
qreal yMax = qMax(point.y(), mousePoint.y());
qreal width = xMax - xMin;
qreal height = yMax - yMin;
if (!qFuzzyIsNull(width) && !qFuzzyIsNull(height))
{
return QRectF(xMin, yMin, width, height);
}
return QRectF();
}
PDFCreatePCElementRectangleTool::PDFCreatePCElementRectangleTool(PDFDrawWidgetProxy* proxy,
PDFPageContentScene* scene,
QAction* action,
bool isRounded,
QObject* parent) :
BaseClass(proxy, scene, action, parent),
m_pickTool(nullptr),
m_element(nullptr)
{
m_pickTool = new PDFPickTool(proxy, PDFPickTool::Mode::Rectangles, this);
m_pickTool->setDrawSelectionRectangle(false);
addTool(m_pickTool);
connect(m_pickTool, &PDFPickTool::rectanglePicked, this, &PDFCreatePCElementRectangleTool::onRectanglePicked);
QPen pen(Qt::SolidLine);
pen.setWidthF(1.0);
m_element = new PDFPageContentElementRectangle();
m_element->setBrush(Qt::NoBrush);
m_element->setPen(std::move(pen));
m_element->setRounded(isRounded);
updateActions();
}
PDFCreatePCElementRectangleTool::~PDFCreatePCElementRectangleTool()
{
delete m_element;
}
void PDFCreatePCElementRectangleTool::drawPage(QPainter* painter,
PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter,
const QTransform& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const
{
BaseClass::drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors);
if (pageIndex != m_pickTool->getPageIndex())
{
return;
}
QRectF rectangle = getRectangleFromPickTool(m_pickTool, pagePointToDevicePointMatrix);
if (!rectangle.isValid())
{
return;
}
m_element->setPageIndex(pageIndex);
m_element->setRectangle(rectangle);
m_element->drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors);
}
const PDFPageContentElement* PDFCreatePCElementRectangleTool::getElement() const
{
return m_element;
}
PDFPageContentElement* PDFCreatePCElementRectangleTool::getElement()
{
return m_element;
}
void PDFCreatePCElementRectangleTool::onRectanglePicked(PDFInteger pageIndex, QRectF pageRectangle)
{
if (pageRectangle.isEmpty())
{
return;
}
m_element->setPageIndex(pageIndex);
m_element->setRectangle(pageRectangle);
m_scene->addElement(m_element->clone());
setActive(false);
}
PDFCreatePCElementLineTool::PDFCreatePCElementLineTool(PDFDrawWidgetProxy* proxy,
PDFPageContentScene* scene,
QAction* action,
bool isHorizontal,
bool isVertical,
QObject* parent) :
BaseClass(proxy, scene, action, parent),
m_pickTool(nullptr),
m_element(nullptr)
{
m_pickTool = new PDFPickTool(proxy, PDFPickTool::Mode::Points, this);
m_pickTool->setDrawSelectionRectangle(false);
addTool(m_pickTool);
connect(m_pickTool, &PDFPickTool::pointPicked, this, &PDFCreatePCElementLineTool::onPointPicked);
QPen pen(Qt::SolidLine);
pen.setWidthF(2.0);
pen.setCapStyle(Qt::RoundCap);
PDFPageContentElementLine::LineGeometry geometry = PDFPageContentElementLine::LineGeometry::General;
if (isHorizontal)
{
geometry = PDFPageContentElementLine::LineGeometry::Horizontal;
}
if (isVertical)
{
geometry = PDFPageContentElementLine::LineGeometry::Vertical;
}
m_element = new PDFPageContentElementLine();
m_element->setBrush(Qt::NoBrush);
m_element->setPen(std::move(pen));
m_element->setGeometry(geometry);
updateActions();
}
PDFCreatePCElementLineTool::~PDFCreatePCElementLineTool()
{
delete m_element;
}
void PDFCreatePCElementLineTool::drawPage(QPainter* painter,
PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter,
const QTransform& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const
{
BaseClass::drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors);
if (pageIndex != m_pickTool->getPageIndex() || !m_startPoint)
{
return;
}
m_element->setPageIndex(pageIndex);
QPointF startPoint = *m_startPoint;
QPointF endPoint = pagePointToDevicePointMatrix.inverted().map(m_pickTool->getSnappedPoint());
QLineF line(startPoint, endPoint);
if (!qFuzzyIsNull(line.length()))
{
m_element->setLine(line);
}
m_element->drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors);
}
const PDFPageContentElement* PDFCreatePCElementLineTool::getElement() const
{
return m_element;
}
PDFPageContentElement* PDFCreatePCElementLineTool::getElement()
{
return m_element;
}
void PDFCreatePCElementLineTool::clear()
{
m_startPoint = std::nullopt;
}
void PDFCreatePCElementLineTool::onPointPicked(PDFInteger pageIndex, QPointF pagePoint)
{
if (!m_startPoint || m_element->getPageIndex() != pageIndex)
{
m_startPoint = pagePoint;
m_element->setPageIndex(pageIndex);
m_element->setLine(QLineF(pagePoint, pagePoint));
return;
}
if (qFuzzyCompare(m_startPoint.value().x(), pagePoint.x()) &&
qFuzzyCompare(m_startPoint.value().y(), pagePoint.y()))
{
// Jakub Melka: Point is same as the start point
clear();
return;
}
QLineF line = m_element->getLine();
line.setP2(pagePoint);
m_element->setLine(line);
m_scene->addElement(m_element->clone());
clear();
setActive(false);
}
PDFCreatePCElementImageTool::PDFCreatePCElementImageTool(PDFDrawWidgetProxy* proxy,
PDFPageContentScene* scene,
QAction* action,
QByteArray content,
bool askSelectImage,
QObject* parent) :
BaseClass(proxy, scene, action, parent),
m_pickTool(nullptr),
m_element(nullptr),
m_askSelectImage(askSelectImage)
{
m_pickTool = new PDFPickTool(proxy, PDFPickTool::Mode::Rectangles, this);
m_pickTool->setDrawSelectionRectangle(false);
addTool(m_pickTool);
connect(m_pickTool, &PDFPickTool::rectanglePicked, this, &PDFCreatePCElementImageTool::onRectanglePicked);
m_element = new PDFPageContentImageElement();
m_element->setContent(content);
updateActions();
}
PDFCreatePCElementImageTool::~PDFCreatePCElementImageTool()
{
delete m_element;
}
void PDFCreatePCElementImageTool::drawPage(QPainter* painter,
PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter,
const QTransform& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const
{
BaseClass::drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors);
if (pageIndex != m_pickTool->getPageIndex())
{
return;
}
QRectF rectangle = getRectangleFromPickTool(m_pickTool, pagePointToDevicePointMatrix);
if (!rectangle.isValid())
{
return;
}
m_element->setPageIndex(pageIndex);
m_element->setRectangle(rectangle);
{
PDFPainterStateGuard guard(painter);
painter->setWorldTransform(QTransform(pagePointToDevicePointMatrix), true);
painter->setRenderHint(QPainter::Antialiasing);
painter->setPen(Qt::DotLine);
painter->setBrush(Qt::NoBrush);
painter->drawRect(rectangle);
}
m_element->drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors);
}
const PDFPageContentElement* PDFCreatePCElementImageTool::getElement() const
{
return m_element;
}
PDFPageContentElement* PDFCreatePCElementImageTool::getElement()
{
return m_element;
}
void PDFCreatePCElementImageTool::setActiveImpl(bool active)
{
BaseClass::setActiveImpl(active);
if (active && m_askSelectImage)
{
QTimer::singleShot(0, this, &PDFCreatePCElementImageTool::selectImage);
}
}
void PDFCreatePCElementImageTool::selectImage()
{
if (m_imageDirectory.isEmpty())
{
QStringList pictureDirectiories = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation);
if (!pictureDirectiories.isEmpty())
{
m_imageDirectory = pictureDirectiories.last();
}
else
{
m_imageDirectory = QDir::currentPath();
}
}
QList<QByteArray> mimeTypes = QImageReader::supportedMimeTypes();
QStringList mimeTypeFilters;
for (const QByteArray& mimeType : mimeTypes)
{
mimeTypeFilters.append(mimeType);
}
QFileDialog dialog(getProxy()->getWidget(), tr("Select Image"));
dialog.setDirectory(m_imageDirectory);
dialog.setMimeTypeFilters(mimeTypeFilters);
dialog.selectMimeTypeFilter("image/svg+xml");
dialog.setAcceptMode(QFileDialog::AcceptOpen);
dialog.setFileMode(QFileDialog::ExistingFile);
if (dialog.exec() == QFileDialog::Accepted)
{
QString fileName = dialog.selectedFiles().constFirst();
QFile file(fileName);
if (file.open(QFile::ReadOnly))
{
m_element->setContent(file.readAll());
file.close();
}
else
{
setActive(false);
}
}
else
{
setActive(false);
}
}
void PDFCreatePCElementImageTool::onRectanglePicked(PDFInteger pageIndex, QRectF pageRectangle)
{
if (pageRectangle.isEmpty())
{
return;
}
m_element->setPageIndex(pageIndex);
m_element->setRectangle(pageRectangle);
m_scene->addElement(m_element->clone());
setActive(false);
}
PDFCreatePCElementDotTool::PDFCreatePCElementDotTool(PDFDrawWidgetProxy* proxy,
PDFPageContentScene* scene,
QAction* action,
QObject* parent) :
BaseClass(proxy, scene, action, parent),
m_pickTool(nullptr),
m_element(nullptr)
{
m_pickTool = new PDFPickTool(proxy, PDFPickTool::Mode::Points, this);
m_pickTool->setDrawSelectionRectangle(false);
addTool(m_pickTool);
connect(m_pickTool, &PDFPickTool::pointPicked, this, &PDFCreatePCElementDotTool::onPointPicked);
QPen pen(Qt::SolidLine);
pen.setWidthF(5.0);
pen.setCapStyle(Qt::RoundCap);
m_element = new PDFPageContentElementDot();
m_element->setBrush(Qt::NoBrush);
m_element->setPen(std::move(pen));
updateActions();
}
PDFCreatePCElementDotTool::~PDFCreatePCElementDotTool()
{
delete m_element;
}
void PDFCreatePCElementDotTool::drawPage(QPainter* painter,
PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter,
const QTransform& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const
{
BaseClass::drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors);
QPointF point = pagePointToDevicePointMatrix.inverted().map(m_pickTool->getSnappedPoint());
PDFPainterStateGuard guard(painter);
painter->setWorldTransform(QTransform(pagePointToDevicePointMatrix), true);
painter->setRenderHint(QPainter::Antialiasing);
painter->setPen(m_element->getPen());
painter->setBrush(m_element->getBrush());
painter->drawPoint(point);
}
const PDFPageContentElement* PDFCreatePCElementDotTool::getElement() const
{
return m_element;
}
PDFPageContentElement* PDFCreatePCElementDotTool::getElement()
{
return m_element;
}
void PDFCreatePCElementDotTool::onPointPicked(PDFInteger pageIndex, QPointF pagePoint)
{
m_element->setPageIndex(pageIndex);
m_element->setPoint(pagePoint);
m_scene->addElement(m_element->clone());
m_element->setPageIndex(-1);
setActive(false);
}
PDFCreatePCElementFreehandCurveTool::PDFCreatePCElementFreehandCurveTool(PDFDrawWidgetProxy* proxy,
PDFPageContentScene* scene,
QAction* action,
QObject* parent) :
BaseClass(proxy, scene, action, parent),
m_element(nullptr)
{
QPen pen(Qt::SolidLine);
pen.setWidthF(2.0);
pen.setCapStyle(Qt::RoundCap);
m_element = new PDFPageContentElementFreehandCurve();
m_element->setBrush(Qt::NoBrush);
m_element->setPen(std::move(pen));
}
PDFCreatePCElementFreehandCurveTool::~PDFCreatePCElementFreehandCurveTool()
{
delete m_element;
}
void PDFCreatePCElementFreehandCurveTool::drawPage(QPainter* painter,
PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter,
const QTransform& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const
{
BaseClass::drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors);
if (pageIndex != m_element->getPageIndex() || m_element->isEmpty())
{
return;
}
m_element->drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors);
}
const PDFPageContentElement* PDFCreatePCElementFreehandCurveTool::getElement() const
{
return m_element;
}
PDFPageContentElement* PDFCreatePCElementFreehandCurveTool::getElement()
{
return m_element;
}
void PDFCreatePCElementFreehandCurveTool::mousePressEvent(QWidget* widget, QMouseEvent* event)
{
Q_UNUSED(widget);
event->accept();
if (event->button() == Qt::LeftButton)
{
// Try to perform pick point
QPointF pagePoint;
PDFInteger pageIndex = getProxy()->getPageUnderPoint(event->pos(), &pagePoint);
if (pageIndex != -1 && // We have picked some point on page
(m_element->getPageIndex() == -1 || m_element->getPageIndex() == pageIndex)) // We are under current page
{
m_element->setPageIndex(pageIndex);
m_element->addStartPoint(pagePoint);
}
}
else if (event->button() == Qt::RightButton)
{
resetTool();
}
Q_EMIT getProxy()->repaintNeeded();
}
void PDFCreatePCElementFreehandCurveTool::mouseReleaseEvent(QWidget* widget, QMouseEvent* event)
{
Q_UNUSED(widget);
event->accept();
if (event->button() == Qt::LeftButton)
{
// Try to perform pick point
QPointF pagePoint;
PDFInteger pageIndex = getProxy()->getPageUnderPoint(event->pos(), &pagePoint);
if (pageIndex != -1 && // We have picked some point on page
(m_element->getPageIndex() == pageIndex)) // We are under current page
{
m_element->setPageIndex(pageIndex);
m_element->addPoint(pagePoint);
if (!m_element->isEmpty())
{
m_scene->addElement(m_element->clone());
}
}
resetTool();
}
Q_EMIT getProxy()->repaintNeeded();
}
void PDFCreatePCElementFreehandCurveTool::mouseMoveEvent(QWidget* widget, QMouseEvent* event)
{
Q_UNUSED(widget);
event->accept();
if (event->buttons() & Qt::LeftButton && m_element->getPageIndex() != -1)
{
// Try to add point to the path
QPointF pagePoint;
PDFInteger pageIndex = getProxy()->getPageUnderPoint(event->pos(), &pagePoint);
if (pageIndex == m_element->getPageIndex())
{
m_element->addPoint(pagePoint);
}
Q_EMIT getProxy()->repaintNeeded();
}
}
void PDFCreatePCElementFreehandCurveTool::setActiveImpl(bool active)
{
BaseClass::setActiveImpl(active);
if (!active)
{
resetTool();
}
}
void PDFCreatePCElementFreehandCurveTool::resetTool()
{
m_element->clear();
}
PDFCreatePCElementTextTool::PDFCreatePCElementTextTool(PDFDrawWidgetProxy* proxy,
PDFPageContentScene* scene,
QAction* action,
QObject* parent) :
BaseClass(proxy, scene, action, parent),
m_pickTool(nullptr),
m_element(nullptr),
m_textEditWidget(nullptr)
{
m_pickTool = new PDFPickTool(proxy, PDFPickTool::Mode::Rectangles, this);
m_pickTool->setDrawSelectionRectangle(true);
connect(m_pickTool, &PDFPickTool::rectanglePicked, this, &PDFCreatePCElementTextTool::onRectanglePicked);
QFont font = QGuiApplication::font();
font.setPixelSize(16.0);
m_element = new PDFPageContentElementTextBox();
m_element->setBrush(Qt::NoBrush);
m_element->setPen(QPen(Qt::SolidLine));
m_element->setFont(font);
m_textEditWidget = new PDFTextEditPseudowidget(PDFFormField::Multiline);
}
PDFCreatePCElementTextTool::~PDFCreatePCElementTextTool()
{
delete m_textEditWidget;
delete m_element;
}
void PDFCreatePCElementTextTool::drawPage(QPainter* painter,
PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter,
const QTransform& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const
{
BaseClass::drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors);
if (pageIndex != m_element->getPageIndex())
{
return;
}
if (isEditing())
{
PDFPainterStateGuard guard(painter);
AnnotationDrawParameters parameters;
parameters.painter = painter;
parameters.boundingRectangle = m_element->getRectangle();
parameters.key.first = PDFAppeareanceStreams::Appearance::Normal;
parameters.colorConvertor = getProxy()->getCMSManager()->getColorConvertor();
PDFRenderer::applyFeaturesToColorConvertor(getProxy()->getFeatures(), parameters.colorConvertor);
painter->setWorldTransform(QTransform(pagePointToDevicePointMatrix), true);
m_textEditWidget->draw(parameters, true);
}
}
const PDFPageContentElement* PDFCreatePCElementTextTool::getElement() const
{
return m_element;
}
PDFPageContentElement* PDFCreatePCElementTextTool::getElement()
{
return m_element;
}
void PDFCreatePCElementTextTool::resetTool()
{
m_textEditWidget->setText(QString());
m_element->setText(QString());
m_element->setPageIndex(-1);
if (getTopToolstackTool())
{
removeTool();
}
}
void PDFCreatePCElementTextTool::setActiveImpl(bool active)
{
BaseClass::setActiveImpl(active);
if (active)
{
Q_ASSERT(!getTopToolstackTool());
addTool(m_pickTool);
}
else
{
resetTool();
}
m_pickTool->setActive(active);
}
void PDFCreatePCElementTextTool::onRectanglePicked(PDFInteger pageIndex, QRectF pageRectangle)
{
if (pageRectangle.isEmpty())
{
return;
}
m_element->setPageIndex(pageIndex);
m_element->setRectangle(pageRectangle);
m_textEditWidget->setAppearance(m_element->getFont(),
m_element->getAlignment(),
m_element->getRectangle(),
std::numeric_limits<int>::max(),
m_element->getPen().color());
removeTool();
}
void PDFCreatePCElementTextTool::finishEditing()
{
m_element->setText(m_textEditWidget->getText());
if (!m_element->getText().isEmpty())
{
m_scene->addElement(m_element->clone());
}
resetTool();
setActive(false);
}
std::optional<QPointF> PDFCreatePCElementTextTool::getPagePointUnderMouse(QMouseEvent* event) const
{
QPointF pagePoint;
PDFInteger pageIndex = getProxy()->getPageUnderPoint(event->pos(), &pagePoint);
if (pageIndex == m_element->getPageIndex() &&
m_element->getRectangle().contains(pagePoint))
{
return pagePoint;
}
return std::nullopt;
}
bool PDFCreatePCElementTextTool::isEditing() const
{
return isActive() && !getTopToolstackTool();
}
void PDFCreatePCElementTextTool::shortcutOverrideEvent(QWidget* widget, QKeyEvent* event)
{
Q_UNUSED(widget);
if (isEditing())
{
m_textEditWidget->shortcutOverrideEvent(widget, event);
}
}
void PDFCreatePCElementTextTool::keyPressEvent(QWidget* widget, QKeyEvent* event)
{
event->ignore();
if (!isEditing())
{
BaseClass::keyPressEvent(widget, event);
return;
}
if (event->key() == Qt::Key_Escape)
{
return;
}
if (!m_textEditWidget->isMultiline() && (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return))
{
// Commit the editor and create element
finishEditing();
event->accept();
return;
}
m_textEditWidget->keyPressEvent(widget, event);
if (event->isAccepted())
{
widget->update();
}
}
void PDFCreatePCElementTextTool::mousePressEvent(QWidget* widget, QMouseEvent* event)
{
if (isEditing())
{
if (event->button() == Qt::LeftButton)
{
std::optional<QPointF> pagePoint = getPagePointUnderMouse(event);
if (pagePoint)
{
const int cursorPosition = m_textEditWidget->getCursorPositionFromWidgetPosition(pagePoint.value(), true);
m_textEditWidget->setCursorPosition(cursorPosition, event->modifiers() & Qt::ShiftModifier);
}
else
{
finishEditing();
}
event->accept();
widget->update();
}
}
else
{
BaseClass::mousePressEvent(widget, event);
}
}
void PDFCreatePCElementTextTool::mouseDoubleClickEvent(QWidget* widget, QMouseEvent* event)
{
if (isEditing())
{
if (event->button() == Qt::LeftButton)
{
std::optional<QPointF> pagePoint = getPagePointUnderMouse(event);
if (pagePoint)
{
const int cursorPosition = m_textEditWidget->getCursorPositionFromWidgetPosition(pagePoint.value(), true);
m_textEditWidget->setCursorPosition(cursorPosition, false);
m_textEditWidget->setCursorPosition(m_textEditWidget->getCursorWordBackward(), false);
m_textEditWidget->setCursorPosition(m_textEditWidget->getCursorWordForward(), true);
}
else
{
finishEditing();
}
event->accept();
widget->update();
}
}
else
{
BaseClass::mousePressEvent(widget, event);
}
}
void PDFCreatePCElementTextTool::mouseMoveEvent(QWidget* widget, QMouseEvent* event)
{
if (isEditing())
{
std::optional<QPointF> pagePoint = getPagePointUnderMouse(event);
if (pagePoint)
{
// We must test, if left mouse button is pressed while
// we are moving the mouse - if yes, then select the text.
if (event->buttons() & Qt::LeftButton)
{
const int cursorPosition = m_textEditWidget->getCursorPositionFromWidgetPosition(pagePoint.value(), true);
m_textEditWidget->setCursorPosition(cursorPosition, true);
event->accept();
widget->update();
}
}
}
else
{
BaseClass::mouseMoveEvent(widget, event);
}
}
void PDFCreatePCElementTextTool::wheelEvent(QWidget* widget, QWheelEvent* event)
{
if (isEditing())
{
event->ignore();
}
else
{
BaseClass::wheelEvent(widget, event);
}
}
void PDFCreatePCElementTextTool::setFont(const QFont& font)
{
BaseClass::setFont(font);
m_textEditWidget->setAppearance(font, m_element->getAlignment(), m_element->getRectangle(), std::numeric_limits<int>::max(), m_element->getPen().color());
Q_EMIT getProxy()->repaintNeeded();
}
void PDFCreatePCElementTextTool::setAlignment(Qt::Alignment alignment)
{
BaseClass::setAlignment(alignment);
m_textEditWidget->setAppearance(m_element->getFont(), alignment, m_element->getRectangle(), std::numeric_limits<int>::max(), m_element->getPen().color());
Q_EMIT getProxy()->repaintNeeded();
}
void PDFCreatePCElementTextTool::setPen(const QPen& pen)
{
BaseClass::setPen(pen);
QFont font = m_element->getFont();
font.setHintingPreference(QFont::PreferNoHinting);
if (font.pointSizeF() > 0.0)
{
font.setPixelSize(qRound(font.pointSizeF()));
}
m_textEditWidget->setAppearance(font, m_element->getAlignment(), m_element->getRectangle(), std::numeric_limits<int>::max(), pen.color());
Q_EMIT getProxy()->repaintNeeded();
}
} // namespace pdf

View File

@ -0,0 +1,292 @@
// Copyright (C) 2022 Jakub Melka
//
// This file is part of PDF4QT.
//
// PDF4QT is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// with the written consent of the copyright owner, any later version.
//
// PDF4QT is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
#ifndef PDFPAGECONTENTEDITORTOOLS_H
#define PDFPAGECONTENTEDITORTOOLS_H
#include "pdfwidgettool.h"
namespace pdf
{
class PDFPageContentScene;
class PDFPageContentElement;
class PDFPageContentImageElement;
class PDFPageContentElementDot;
class PDFPageContentElementLine;
class PDFPageContentElementTextBox;
class PDFPageContentElementRectangle;
class PDFPageContentElementFreehandCurve;
class PDFTextEditPseudowidget;
class PDF4QTLIBSHARED_EXPORT PDFCreatePCElementTool : public PDFWidgetTool
{
Q_OBJECT
public:
PDFCreatePCElementTool(PDFDrawWidgetProxy* proxy,
PDFPageContentScene* scene,
QAction* action,
QObject* parent);
virtual const PDFPageContentElement* getElement() const = 0;
virtual PDFPageContentElement* getElement() = 0;
virtual void setPen(const QPen& pen);
virtual void setBrush(const QBrush& brush);
virtual void setFont(const QFont& font);
virtual void setAlignment(Qt::Alignment alignment);
virtual void setTextAngle(pdf::PDFReal angle);
protected:
static QRectF getRectangleFromPickTool(PDFPickTool* pickTool, const QTransform& pagePointToDevicePointMatrix);
PDFPageContentScene* m_scene;
};
/// Tool that creates rectangle element.
class PDF4QTLIBSHARED_EXPORT PDFCreatePCElementRectangleTool : public PDFCreatePCElementTool
{
Q_OBJECT
private:
using BaseClass = PDFCreatePCElementTool;
public:
explicit PDFCreatePCElementRectangleTool(PDFDrawWidgetProxy* proxy,
PDFPageContentScene* scene,
QAction* action,
bool isRounded,
QObject* parent);
virtual ~PDFCreatePCElementRectangleTool() override;
virtual void drawPage(QPainter* painter,
PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter,
const QTransform& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const override;
virtual const PDFPageContentElement* getElement() const override;
virtual PDFPageContentElement* getElement() override;
private:
void onRectanglePicked(pdf::PDFInteger pageIndex, QRectF pageRectangle);
PDFPickTool* m_pickTool;
PDFPageContentElementRectangle* m_element;
};
/// Tool that displays SVG image (or raster image)
class PDF4QTLIBSHARED_EXPORT PDFCreatePCElementImageTool : public PDFCreatePCElementTool
{
Q_OBJECT
private:
using BaseClass = PDFCreatePCElementTool;
public:
explicit PDFCreatePCElementImageTool(PDFDrawWidgetProxy* proxy,
PDFPageContentScene* scene,
QAction* action,
QByteArray content,
bool askSelectImage,
QObject* parent);
virtual ~PDFCreatePCElementImageTool() override;
virtual void drawPage(QPainter* painter,
PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter,
const QTransform& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const override;
virtual const PDFPageContentElement* getElement() const override;
virtual PDFPageContentElement* getElement() override;
protected:
virtual void setActiveImpl(bool active) override;
private:
void selectImage();
void onRectanglePicked(pdf::PDFInteger pageIndex, QRectF pageRectangle);
PDFPickTool* m_pickTool;
PDFPageContentImageElement* m_element;
bool m_askSelectImage;
QString m_imageDirectory;
};
/// Tool that creates line element.
class PDF4QTLIBSHARED_EXPORT PDFCreatePCElementLineTool : public PDFCreatePCElementTool
{
Q_OBJECT
private:
using BaseClass = PDFCreatePCElementTool;
public:
explicit PDFCreatePCElementLineTool(PDFDrawWidgetProxy* proxy,
PDFPageContentScene* scene,
QAction* action,
bool isHorizontal,
bool isVertical,
QObject* parent);
virtual ~PDFCreatePCElementLineTool() override;
virtual void drawPage(QPainter* painter,
PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter,
const QTransform& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const override;
virtual const PDFPageContentElement* getElement() const override;
virtual PDFPageContentElement* getElement() override;
private:
void clear();
void onPointPicked(pdf::PDFInteger pageIndex, QPointF pagePoint);
PDFPickTool* m_pickTool;
PDFPageContentElementLine* m_element;
std::optional<QPointF> m_startPoint;
};
/// Tool that creates dot element.
class PDF4QTLIBSHARED_EXPORT PDFCreatePCElementDotTool : public PDFCreatePCElementTool
{
Q_OBJECT
private:
using BaseClass = PDFCreatePCElementTool;
public:
explicit PDFCreatePCElementDotTool(PDFDrawWidgetProxy* proxy,
PDFPageContentScene* scene,
QAction* action,
QObject* parent);
virtual ~PDFCreatePCElementDotTool() override;
virtual void drawPage(QPainter* painter,
PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter,
const QTransform& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const override;
virtual const PDFPageContentElement* getElement() const override;
virtual PDFPageContentElement* getElement() override;
private:
void onPointPicked(pdf::PDFInteger pageIndex, QPointF pagePoint);
PDFPickTool* m_pickTool;
PDFPageContentElementDot* m_element;
};
/// Tool that creates freehand curve element.
class PDF4QTLIBSHARED_EXPORT PDFCreatePCElementFreehandCurveTool : public PDFCreatePCElementTool
{
Q_OBJECT
private:
using BaseClass = PDFCreatePCElementTool;
public:
explicit PDFCreatePCElementFreehandCurveTool(PDFDrawWidgetProxy* proxy,
PDFPageContentScene* scene,
QAction* action,
QObject* parent);
virtual ~PDFCreatePCElementFreehandCurveTool() override;
virtual void drawPage(QPainter* painter,
PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter,
const QTransform& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const override;
virtual const PDFPageContentElement* getElement() const override;
virtual PDFPageContentElement* getElement() override;
virtual void mousePressEvent(QWidget* widget, QMouseEvent* event) override;
virtual void mouseReleaseEvent(QWidget* widget, QMouseEvent* event) override;
virtual void mouseMoveEvent(QWidget* widget, QMouseEvent* event) override;
protected:
virtual void setActiveImpl(bool active) override;
private:
void resetTool();
PDFPageContentElementFreehandCurve* m_element;
};
/// Tool that displays SVG image
class PDF4QTLIBSHARED_EXPORT PDFCreatePCElementTextTool : public PDFCreatePCElementTool
{
Q_OBJECT
private:
using BaseClass = PDFCreatePCElementTool;
public:
explicit PDFCreatePCElementTextTool(PDFDrawWidgetProxy* proxy,
PDFPageContentScene* scene,
QAction* action,
QObject* parent);
virtual ~PDFCreatePCElementTextTool() override;
virtual void drawPage(QPainter* painter,
PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter,
const QTransform& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const override;
virtual const PDFPageContentElement* getElement() const override;
virtual PDFPageContentElement* getElement() override;
virtual void setPen(const QPen& pen) override;
virtual void setFont(const QFont& font) override;
virtual void setAlignment(Qt::Alignment alignment) override;
virtual void setActiveImpl(bool active) override;
virtual void shortcutOverrideEvent(QWidget* widget, QKeyEvent* event) override;
virtual void keyPressEvent(QWidget* widget, QKeyEvent* event) override;
virtual void mousePressEvent(QWidget* widget, QMouseEvent* event) override;
virtual void mouseDoubleClickEvent(QWidget* widget, QMouseEvent* event) override;
virtual void mouseMoveEvent(QWidget* widget, QMouseEvent* event) override;
virtual void wheelEvent(QWidget* widget, QWheelEvent* event) override;
private:
void onRectanglePicked(pdf::PDFInteger pageIndex, QRectF pageRectangle);
void finishEditing();
void resetTool();
std::optional<QPointF> getPagePointUnderMouse(QMouseEvent* event) const;
bool isEditing() const;
PDFPickTool* m_pickTool;
PDFPageContentElementTextBox* m_element;
PDFTextEditPseudowidget* m_textEditWidget;
};
} // namespace pdf
#endif // PDFPAGECONTENTEDITORTOOLS_H

View File

@ -0,0 +1,276 @@
// Copyright (C) 2022 Jakub Melka
//
// This file is part of PDF4QT.
//
// PDF4QT is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// with the written consent of the copyright owner, any later version.
//
// PDF4QT is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
#include "pdfpagecontenteditorwidget.h"
#include "pdfpagecontenteditorstylesettings.h"
#include "ui_pdfpagecontenteditorwidget.h"
#include "pdfwidgetutils.h"
#include "pdfpagecontentelements.h"
#include "pdfutils.h"
#include <QAction>
#include <QToolButton>
namespace pdf
{
PDFPageContentEditorWidget::PDFPageContentEditorWidget(QWidget* parent) :
QDockWidget(parent),
ui(new Ui::PDFPageContentEditorWidget),
m_toolBoxColumnCount(6),
m_scene(nullptr),
m_selectionChangeEnabled(true),
m_updatesEnabled(true)
{
ui->setupUi(this);
m_toolButtonIconSize = PDFWidgetUtils::scaleDPI(this, QSize(32, 32));
for (QToolButton* button : findChildren<QToolButton*>())
{
button->setIconSize(m_toolButtonIconSize);
}
m_settingsWidget = new PDFPageContentEditorStyleSettings(this);
ui->appearanceLayout->addWidget(m_settingsWidget);
m_operationMapper.setMapping(ui->alignVertTopButton, static_cast<int>(PDFPageContentElementManipulator::Operation::AlignTop));
m_operationMapper.setMapping(ui->alignVertMiddleButton, static_cast<int>(PDFPageContentElementManipulator::Operation::AlignCenterVertically));
m_operationMapper.setMapping(ui->alignVertBottomButton, static_cast<int>(PDFPageContentElementManipulator::Operation::AlignBottom));
m_operationMapper.setMapping(ui->alignHorLeftButton, static_cast<int>(PDFPageContentElementManipulator::Operation::AlignLeft));
m_operationMapper.setMapping(ui->alignHorMiddleButton, static_cast<int>(PDFPageContentElementManipulator::Operation::AlignCenterHorizontally));
m_operationMapper.setMapping(ui->alignHorRightButton, static_cast<int>(PDFPageContentElementManipulator::Operation::AlignRight));
m_operationMapper.setMapping(ui->setSameWidthButton, static_cast<int>(PDFPageContentElementManipulator::Operation::SetSameWidth));
m_operationMapper.setMapping(ui->setSameHeightButton, static_cast<int>(PDFPageContentElementManipulator::Operation::SetSameHeight));
m_operationMapper.setMapping(ui->setSameSizeButton, static_cast<int>(PDFPageContentElementManipulator::Operation::SetSameSize));
m_operationMapper.setMapping(ui->centerHorizontallyButton, static_cast<int>(PDFPageContentElementManipulator::Operation::CenterHorizontally));
m_operationMapper.setMapping(ui->centerVerticallyButton, static_cast<int>(PDFPageContentElementManipulator::Operation::CenterVertically));
m_operationMapper.setMapping(ui->centerRectButton, static_cast<int>(PDFPageContentElementManipulator::Operation::CenterHorAndVert));
m_operationMapper.setMapping(ui->layoutHorizontallyButton, static_cast<int>(PDFPageContentElementManipulator::Operation::LayoutHorizontally));
m_operationMapper.setMapping(ui->layoutVerticallyButton, static_cast<int>(PDFPageContentElementManipulator::Operation::LayoutVertically));
m_operationMapper.setMapping(ui->layoutFormButton, static_cast<int>(PDFPageContentElementManipulator::Operation::LayoutForm));
m_operationMapper.setMapping(ui->layoutGridButton, static_cast<int>(PDFPageContentElementManipulator::Operation::LayoutGrid));
connect(ui->alignVertTopButton, &QToolButton::clicked, &m_operationMapper, QOverload<>::of(&QSignalMapper::map));
connect(ui->alignVertMiddleButton, &QToolButton::clicked, &m_operationMapper, QOverload<>::of(&QSignalMapper::map));
connect(ui->alignVertBottomButton, &QToolButton::clicked, &m_operationMapper, QOverload<>::of(&QSignalMapper::map));
connect(ui->alignHorLeftButton, &QToolButton::clicked, &m_operationMapper, QOverload<>::of(&QSignalMapper::map));
connect(ui->alignHorMiddleButton, &QToolButton::clicked, &m_operationMapper, QOverload<>::of(&QSignalMapper::map));
connect(ui->alignHorRightButton, &QToolButton::clicked, &m_operationMapper, QOverload<>::of(&QSignalMapper::map));
connect(ui->setSameWidthButton, &QToolButton::clicked, &m_operationMapper, QOverload<>::of(&QSignalMapper::map));
connect(ui->setSameHeightButton, &QToolButton::clicked, &m_operationMapper, QOverload<>::of(&QSignalMapper::map));
connect(ui->setSameSizeButton, &QToolButton::clicked, &m_operationMapper, QOverload<>::of(&QSignalMapper::map));
connect(ui->centerHorizontallyButton, &QToolButton::clicked, &m_operationMapper, QOverload<>::of(&QSignalMapper::map));
connect(ui->centerVerticallyButton, &QToolButton::clicked, &m_operationMapper, QOverload<>::of(&QSignalMapper::map));
connect(ui->centerRectButton, &QToolButton::clicked, &m_operationMapper, QOverload<>::of(&QSignalMapper::map));
connect(ui->layoutHorizontallyButton, &QToolButton::clicked, &m_operationMapper, QOverload<>::of(&QSignalMapper::map));
connect(ui->layoutVerticallyButton, &QToolButton::clicked, &m_operationMapper, QOverload<>::of(&QSignalMapper::map));
connect(ui->layoutFormButton, &QToolButton::clicked, &m_operationMapper, QOverload<>::of(&QSignalMapper::map));
connect(ui->layoutGridButton, &QToolButton::clicked, &m_operationMapper, QOverload<>::of(&QSignalMapper::map));
connect(&m_actionMapper, &QSignalMapper::mappedObject, this, &PDFPageContentEditorWidget::onActionTriggerRequest);
connect(&m_operationMapper, &QSignalMapper::mappedInt, this, &PDFPageContentEditorWidget::operationTriggered);
connect(ui->itemsListWidget->selectionModel(), &QItemSelectionModel::selectionChanged, this, &PDFPageContentEditorWidget::onItemSelectionChanged);
connect(m_settingsWidget, &PDFPageContentEditorStyleSettings::penChanged, this, &PDFPageContentEditorWidget::penChanged);
connect(m_settingsWidget, &PDFPageContentEditorStyleSettings::brushChanged, this, &PDFPageContentEditorWidget::brushChanged);
connect(m_settingsWidget, &PDFPageContentEditorStyleSettings::fontChanged, this, &PDFPageContentEditorWidget::fontChanged);
connect(m_settingsWidget, &PDFPageContentEditorStyleSettings::alignmentChanged, this, &PDFPageContentEditorWidget::alignmentChanged);
connect(m_settingsWidget, &PDFPageContentEditorStyleSettings::textAngleChanged, this, &PDFPageContentEditorWidget::textAngleChanged);
}
PDFPageContentEditorWidget::~PDFPageContentEditorWidget()
{
delete ui;
}
void PDFPageContentEditorWidget::addAction(QAction* action)
{
// First, find position for our action
int row = 0;
int column = 0;
while (true)
{
if (!ui->toolGroupBoxLayout->itemAtPosition(row, column))
{
break;
}
++column;
if (column == m_toolBoxColumnCount)
{
column = 0;
++row;
}
}
QToolButton* button = new QToolButton(this);
button->setIcon(action->icon());
button->setText(action->text());
button->setToolTip(action->toolTip());
button->setCheckable(action->isCheckable());
button->setChecked(action->isChecked());
button->setEnabled(action->isEnabled());
button->setShortcut(action->shortcut());
button->setIconSize(m_toolButtonIconSize);
m_actionMapper.setMapping(button, action);
connect(button, &QToolButton::clicked, &m_actionMapper, QOverload<>::of(&QSignalMapper::map));
connect(action, &QAction::changed, this, &PDFPageContentEditorWidget::onActionChanged);
ui->toolGroupBoxLayout->addWidget(button, row, column, Qt::AlignCenter);
}
QToolButton* PDFPageContentEditorWidget::getToolButtonForOperation(int operation) const
{
return qobject_cast<QToolButton*>(m_operationMapper.mapping(operation));
}
void PDFPageContentEditorWidget::updateItemsInListWidget()
{
if (!m_updatesEnabled)
{
return;
}
pdf::PDFTemporaryValueChange guard(&m_updatesEnabled, false);
ui->itemsListWidget->setUpdatesEnabled(false);
if (m_scene)
{
std::set<PDFInteger> presentElementIds;
std::set<PDFInteger> elementIds = m_scene->getElementIds();
// Remove items which are not here
for (int i = 0; i < ui->itemsListWidget->count();)
{
QListWidgetItem* item = ui->itemsListWidget->item(i);
const PDFInteger elementId = item->data(Qt::UserRole).toLongLong();
if (!elementIds.count(elementId))
{
delete ui->itemsListWidget->takeItem(i);
}
else
{
presentElementIds.insert(elementId);
++i;
}
}
// Add items which are here
for (PDFInteger elementId : elementIds)
{
if (presentElementIds.count(elementId))
{
continue;
}
const PDFPageContentElement* element = m_scene->getElementById(elementId);
Q_ASSERT(element);
QListWidgetItem* item = new QListWidgetItem(element->getDescription());
item->setData(Qt::UserRole, int(elementId));
ui->itemsListWidget->addItem(item);
}
}
else
{
ui->itemsListWidget->clear();
}
ui->itemsListWidget->setUpdatesEnabled(true);
}
void PDFPageContentEditorWidget::onActionTriggerRequest(QObject* actionObject)
{
QAction* action = qobject_cast<QAction*>(actionObject);
Q_ASSERT(action);
action->trigger();
}
void PDFPageContentEditorWidget::onActionChanged()
{
QAction* action = qobject_cast<QAction*>(sender());
QToolButton* button = qobject_cast<QToolButton*>(m_actionMapper.mapping(action));
Q_ASSERT(action);
Q_ASSERT(button);
button->setChecked(action->isChecked());
button->setEnabled(action->isEnabled());
}
void PDFPageContentEditorWidget::onItemSelectionChanged()
{
if (m_selectionChangeEnabled)
{
Q_EMIT itemSelectionChangedByUser();
}
}
PDFPageContentScene* PDFPageContentEditorWidget::scene() const
{
return m_scene;
}
void PDFPageContentEditorWidget::setScene(PDFPageContentScene* newScene)
{
if (m_scene != newScene)
{
m_scene = newScene;
updateItemsInListWidget();
}
}
std::set<PDFInteger> PDFPageContentEditorWidget::getSelection() const
{
std::set<PDFInteger> result;
for (int i = 0; i < ui->itemsListWidget->count(); ++i)
{
QListWidgetItem* item = ui->itemsListWidget->item(i);
if (item->isSelected())
{
const PDFInteger elementId = item->data(Qt::UserRole).toLongLong();
result.insert(elementId);
}
}
return result;
}
void PDFPageContentEditorWidget::setSelection(const std::set<PDFInteger>& selection)
{
pdf::PDFTemporaryValueChange guard(&m_selectionChangeEnabled, false);
for (int i = 0; i < ui->itemsListWidget->count(); ++i)
{
QListWidgetItem* item = ui->itemsListWidget->item(i);
const PDFInteger elementId = item->data(Qt::UserRole).toLongLong();
item->setSelected(selection.count(elementId));
}
}
void PDFPageContentEditorWidget::loadStyleFromElement(const PDFPageContentElement* element)
{
m_settingsWidget->loadFromElement(element, false);
}
} // namespace pdf

View File

@ -0,0 +1,95 @@
// Copyright (C) 2022 Jakub Melka
//
// This file is part of PDF4QT.
//
// PDF4QT is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// with the written consent of the copyright owner, any later version.
//
// PDF4QT is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
#ifndef PDFPAGECONTENTEDITORWIDGET_H
#define PDFPAGECONTENTEDITORWIDGET_H
#include "pdfglobal.h"
#include <QDockWidget>
#include <QSignalMapper>
#include <set>
class QToolButton;
namespace Ui
{
class PDFPageContentEditorWidget;
}
namespace pdf
{
class PDFPageContentScene;
class PDFPageContentElement;
class PDFPageContentEditorStyleSettings;
class PDF4QTLIBSHARED_EXPORT PDFPageContentEditorWidget : public QDockWidget
{
Q_OBJECT
public:
explicit PDFPageContentEditorWidget(QWidget* parent);
virtual ~PDFPageContentEditorWidget() override;
/// Adds external action to the tool box
void addAction(QAction* action);
QToolButton* getToolButtonForOperation(int operation) const;
/// Update items in list widget
void updateItemsInListWidget();
PDFPageContentScene* scene() const;
void setScene(PDFPageContentScene* newScene);
std::set<PDFInteger> getSelection() const;
void setSelection(const std::set<PDFInteger>& selection);
/// Loads style from element, element can be nullptr
/// \param element Element
void loadStyleFromElement(const PDFPageContentElement* element);
signals:
void operationTriggered(int operation);
void itemSelectionChangedByUser();
void penChanged(const QPen& pen);
void brushChanged(const QBrush& brush);
void fontChanged(const QFont& font);
void alignmentChanged(Qt::Alignment alignment);
void textAngleChanged(pdf::PDFReal angle);
private:
void onActionTriggerRequest(QObject* actionObject);
void onActionChanged();
void onItemSelectionChanged();
Ui::PDFPageContentEditorWidget* ui;
PDFPageContentEditorStyleSettings* m_settingsWidget;
QSignalMapper m_actionMapper;
QSignalMapper m_operationMapper;
int m_toolBoxColumnCount;
QSize m_toolButtonIconSize;
PDFPageContentScene* m_scene;
bool m_selectionChangeEnabled;
bool m_updatesEnabled;
};
} // namespace pdf
#endif // PDFPAGECONTENTEDITORWIDGET_H

View File

@ -0,0 +1,204 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PDFPageContentEditorWidget</class>
<widget class="QDockWidget" name="PDFPageContentEditorWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>333</width>
<height>607</height>
</rect>
</property>
<property name="allowedAreas">
<set>Qt::LeftDockWidgetArea|Qt::RightDockWidgetArea</set>
</property>
<property name="windowTitle">
<string>Content editor</string>
</property>
<widget class="QWidget" name="dockWidgetContents">
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,0,0,1">
<item>
<widget class="QGroupBox" name="toolGroupBox">
<property name="title">
<string>Toolbox</string>
</property>
<layout class="QGridLayout" name="toolGroupBoxLayout"/>
</widget>
</item>
<item>
<widget class="QGroupBox" name="adjustBox">
<property name="title">
<string>Geometry Tools</string>
</property>
<layout class="QGridLayout" name="geometryToolsGroupBoxLayout">
<item row="0" column="0">
<widget class="QToolButton" name="alignVertTopButton">
<property name="toolTip">
<string>Align to Top</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QToolButton" name="alignVertMiddleButton">
<property name="toolTip">
<string>Align to Vertical Center</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QToolButton" name="alignVertBottomButton">
<property name="toolTip">
<string>Align to Bottom</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QToolButton" name="setSameWidthButton">
<property name="toolTip">
<string>Set Same Width</string>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QToolButton" name="setSameHeightButton">
<property name="toolTip">
<string>Set Same Height</string>
</property>
</widget>
</item>
<item row="0" column="5">
<widget class="QToolButton" name="setSameSizeButton">
<property name="toolTip">
<string>Set Same Size</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QToolButton" name="alignHorLeftButton">
<property name="toolTip">
<string>Align to Left</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QToolButton" name="alignHorMiddleButton">
<property name="toolTip">
<string>Align to Horizontal Center</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QToolButton" name="alignHorRightButton">
<property name="toolTip">
<string>Align to Right</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QToolButton" name="centerHorizontallyButton">
<property name="toolTip">
<string>Center Horizontally</string>
</property>
</widget>
</item>
<item row="1" column="4">
<widget class="QToolButton" name="centerVerticallyButton">
<property name="toolTip">
<string>Center Vertically</string>
</property>
</widget>
</item>
<item row="1" column="5">
<widget class="QToolButton" name="centerRectButton">
<property name="toolTip">
<string>Center to Page Media Box</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="layoutGroupBox">
<property name="title">
<string>Layout Tools</string>
</property>
<layout class="QHBoxLayout" name="layoutToolBoxLayout">
<item>
<widget class="QToolButton" name="layoutHorizontallyButton">
<property name="toolTip">
<string>Make Horizontal Layout</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="layoutVerticallyButton">
<property name="toolTip">
<string>Make Vertical Layout</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="layoutFormButton">
<property name="toolTip">
<string>Make Form Layout</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="layoutGridButton">
<property name="toolTip">
<string>Make Grid Layout</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="appearanceGroupBox">
<property name="title">
<string>Appearance</string>
</property>
<layout class="QVBoxLayout" name="appearanceLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="contentGroupBox">
<property name="title">
<string>Content</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QListWidget" name="itemsListWidget">
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,111 @@
// Copyright (C) 2019-2022 Jakub Melka
//
// This file is part of PDF4QT.
//
// PDF4QT is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// with the written consent of the copyright owner, any later version.
//
// PDF4QT is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
#include "pdfrenderingerrorswidget.h"
#include "pdfdrawwidget.h"
#include "pdfdbgheap.h"
#include "ui_pdfrenderingerrorswidget.h"
#include "pdfwidgetutils.h"
namespace pdf
{
PDFRenderingErrorsWidget::PDFRenderingErrorsWidget(QWidget* parent, PDFWidget* pdfWidget) :
QDialog(parent),
ui(new Ui::PDFRenderingErrorsWidget)
{
ui->setupUi(this);
ui->renderErrorsTreeWidget->setColumnCount(3);
ui->renderErrorsTreeWidget->setColumnWidth(0, 100);
ui->renderErrorsTreeWidget->setColumnWidth(1, 300);
ui->renderErrorsTreeWidget->setHeaderLabels({ tr("Page"), tr("Error type"), tr("Description") });
Q_ASSERT(pdfWidget);
std::vector<PDFInteger> currentPages = pdfWidget->getDrawWidget()->getCurrentPages();
std::sort(currentPages.begin(), currentPages.end());
QTreeWidgetItem* scrollToItem = nullptr;
const PDFWidget::PageRenderingErrors* pageRenderingErrors = pdfWidget->getPageRenderingErrors();
for (const auto& pageRenderingError : *pageRenderingErrors)
{
const PDFInteger pageIndex = pageRenderingError.first;
QTreeWidgetItem* root = new QTreeWidgetItem(ui->renderErrorsTreeWidget, QStringList() << QString::number(pageIndex + 1) << QString() << QString());
for (const PDFRenderError& error : pageRenderingError.second)
{
QString typeString;
switch (error.type)
{
case RenderErrorType::Error:
{
typeString = tr("Error");
break;
}
case RenderErrorType::Warning:
{
typeString = tr("Warning");
break;
}
case RenderErrorType::NotImplemented:
{
typeString = tr("Not implemented");
break;
}
case RenderErrorType::NotSupported:
{
typeString = tr("Not supported");
break;
}
default:
{
Q_ASSERT(false);
break;
}
}
new QTreeWidgetItem(root, QStringList() << QString() << typeString << error.message);
}
bool isCurrentPage = std::binary_search(currentPages.cbegin(), currentPages.cend(), pageIndex);
root->setExpanded(isCurrentPage);
if (isCurrentPage && !scrollToItem)
{
scrollToItem = root;
}
}
if (scrollToItem)
{
ui->renderErrorsTreeWidget->scrollToItem(scrollToItem, QAbstractItemView::EnsureVisible);
}
pdf::PDFWidgetUtils::style(this);
}
PDFRenderingErrorsWidget::~PDFRenderingErrorsWidget()
{
delete ui;
}
} // namespace pdf

View File

@ -0,0 +1,48 @@
// Copyright (C) 2019-2021 Jakub Melka
//
// This file is part of PDF4QT.
//
// PDF4QT is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// with the written consent of the copyright owner, any later version.
//
// PDF4QT is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
#ifndef PDFRENDERINGERRORSWIDGET_H
#define PDFRENDERINGERRORSWIDGET_H
#include "pdfglobal.h"
#include <QDialog>
namespace Ui
{
class PDFRenderingErrorsWidget;
}
namespace pdf
{
class PDFWidget;
class PDF4QTLIBSHARED_EXPORT PDFRenderingErrorsWidget : public QDialog
{
Q_OBJECT
public:
explicit PDFRenderingErrorsWidget(QWidget* parent, PDFWidget* pdfWidget);
virtual ~PDFRenderingErrorsWidget() override;
private:
Ui::PDFRenderingErrorsWidget* ui;
};
} // namespace pdf
#endif // PDFRENDERINGERRORSWIDGET_H

View File

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PDFRenderingErrorsWidget</class>
<widget class="QDialog" name="PDFRenderingErrorsWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>798</width>
<height>510</height>
</rect>
</property>
<property name="windowTitle">
<string>Rendering errors</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTreeWidget" name="renderErrorsTreeWidget">
<column>
<property name="text">
<string notr="true">1</string>
</property>
</column>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>PDFRenderingErrorsWidget</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>PDFRenderingErrorsWidget</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,137 @@
// Copyright (C) 2020-2022 Jakub Melka
//
// This file is part of PDF4QT.
//
// PDF4QT is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// with the written consent of the copyright owner, any later version.
//
// PDF4QT is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
#include "pdfselectpagesdialog.h"
#include "pdfdbgheap.h"
#include "ui_pdfselectpagesdialog.h"
#include "pdfwidgetutils.h"
#include <QMessageBox>
namespace pdf
{
PDFSelectPagesDialog::PDFSelectPagesDialog(QString windowTitle,
QString groupBoxTitle,
pdf::PDFInteger pageCount,
const std::vector<pdf::PDFInteger>& visiblePages,
QWidget* parent) :
QDialog(parent),
ui(new Ui::PDFSelectPagesDialog),
m_pageCount(pageCount),
m_visiblePages(visiblePages)
{
ui->setupUi(this);
setWindowTitle(windowTitle);
ui->selectPagesGroupBox->setTitle(groupBoxTitle);
for (pdf::PDFInteger& pageIndex : m_visiblePages)
{
++pageIndex;
}
m_evenPages.reserve(pageCount / 2);
m_oddPages.reserve(pageCount / 2 + 1);
for (pdf::PDFInteger i = 1; i <= pageCount; ++i)
{
if (i % 2 == 1)
{
m_oddPages.push_back(i);
}
else
{
m_evenPages.push_back(i);
}
}
connect(ui->customPageRangeRadioButton, &QRadioButton::toggled, this, &PDFSelectPagesDialog::updateUi);
setMinimumWidth(pdf::PDFWidgetUtils::scaleDPI_x(this, 400));
updateUi();
pdf::PDFWidgetUtils::style(this);
}
PDFSelectPagesDialog::~PDFSelectPagesDialog()
{
delete ui;
}
std::vector<pdf::PDFInteger> PDFSelectPagesDialog::getSelectedPages() const
{
std::vector<pdf::PDFInteger> result;
if (ui->visiblePagesRadioButton->isChecked())
{
result = m_visiblePages;
}
else if (ui->allPagesRadioButton->isChecked())
{
result.resize(m_pageCount, 0);
std::iota(result.begin(), result.end(), 1);
}
else if (ui->evenPagesRadioButton->isChecked())
{
result = m_evenPages;
}
else if (ui->oddPagesRadioButton->isChecked())
{
result = m_oddPages;
}
else if (ui->customPageRangeRadioButton->isChecked())
{
QString errorMessage;
result = pdf::PDFClosedIntervalSet::parse(1, m_pageCount, ui->customPageRangeEdit->text(), &errorMessage).unfold();
}
else
{
Q_ASSERT(false);
}
return result;
}
void PDFSelectPagesDialog::updateUi()
{
ui->customPageRangeEdit->setEnabled(ui->customPageRangeRadioButton->isChecked());
}
void PDFSelectPagesDialog::accept()
{
if (ui->customPageRangeRadioButton->isChecked())
{
QString errorMessage;
pdf::PDFClosedIntervalSet::parse(1, m_pageCount, ui->customPageRangeEdit->text(), &errorMessage);
if (!errorMessage.isEmpty())
{
QMessageBox::critical(this, tr("Error"), errorMessage);
return;
}
}
if (getSelectedPages().empty())
{
QMessageBox::critical(this, tr("Error"), tr("Selected page range is empty."));
return;
}
QDialog::accept();
}
} // namespace pdf

View File

@ -0,0 +1,62 @@
// Copyright (C) 2020-2021 Jakub Melka
//
// This file is part of PDF4QT.
//
// PDF4QT is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// with the written consent of the copyright owner, any later version.
//
// PDF4QT is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
#ifndef PDFSELECTPAGESDIALOG_H
#define PDFSELECTPAGESDIALOG_H
#include "pdfutils.h"
#include <QDialog>
namespace Ui
{
class PDFSelectPagesDialog;
}
namespace pdf
{
class PDF4QTLIBSHARED_EXPORT PDFSelectPagesDialog : public QDialog
{
Q_OBJECT
public:
explicit PDFSelectPagesDialog(QString windowTitle,
QString groupBoxTitle,
pdf::PDFInteger pageCount,
const std::vector<pdf::PDFInteger>& visiblePages,
QWidget* parent);
virtual ~PDFSelectPagesDialog() override;
virtual void accept() override;
std::vector<pdf::PDFInteger> getSelectedPages() const;
private:
void updateUi();
Ui::PDFSelectPagesDialog* ui;
pdf::PDFInteger m_pageCount;
std::vector<pdf::PDFInteger> m_visiblePages;
std::vector<pdf::PDFInteger> m_evenPages;
std::vector<pdf::PDFInteger> m_oddPages;
};
} // namespace pdf
#endif // PDFSELECTPAGESDIALOG_H

View File

@ -0,0 +1,121 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PDFSelectPagesDialog</class>
<widget class="QDialog" name="PDFSelectPagesDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>218</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="selectPagesGroupBox">
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0" colspan="2">
<widget class="QRadioButton" name="allPagesRadioButton">
<property name="text">
<string>All pages</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="customPageRangeEdit"/>
</item>
<item row="2" column="0" colspan="2">
<widget class="QRadioButton" name="evenPagesRadioButton">
<property name="text">
<string>Even pages</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<widget class="QRadioButton" name="oddPagesRadioButton">
<property name="text">
<string>Odd pages</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QRadioButton" name="customPageRangeRadioButton">
<property name="text">
<string>Custom page range:</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QRadioButton" name="visiblePagesRadioButton">
<property name="text">
<string>Visible pages</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="5" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>PDFSelectPagesDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>PDFSelectPagesDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,620 @@
// Copyright (C) 2020-2021 Jakub Melka
//
// This file is part of PDF4QT.
//
// PDF4QT is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// with the written consent of the copyright owner, any later version.
//
// PDF4QT is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
#ifndef PDFWIDGETTOOL_H
#define PDFWIDGETTOOL_H
#include "pdfdrawspacecontroller.h"
#include "pdftextlayout.h"
#include "pdfsnapper.h"
#include <QDialog>
#include <QCursor>
class QCheckBox;
class QLineEdit;
namespace pdf
{
/// Base class for various widget tools (for example, searching, text selection,
/// screenshots, zoom tool etc.). Each tool can have subtools (for example,
/// screenshot tool is picking screenshot rectangle).
class PDF4QTLIBSHARED_EXPORT PDFWidgetTool : public QObject, public IDocumentDrawInterface
{
Q_OBJECT
private:
using BaseClass = QObject;
public:
explicit PDFWidgetTool(PDFDrawWidgetProxy* proxy, QObject* parent);
explicit PDFWidgetTool(PDFDrawWidgetProxy* proxy, QAction* action, QObject* parent);
virtual ~PDFWidgetTool();
/// Sets document, shuts down the tool, if it is active, and document
/// is changing.
/// \param document Document
void setDocument(const PDFModifiedDocument& document);
/// Sets tool as active or inactive. If tool is active, then it is processed
/// in draw widget proxy events (such as drawing etc.).
/// \param active Is tool active?
void setActive(bool active);
/// Returns true, if tool is active
bool isActive() const { return m_active; }
/// Returns action for activating/deactivating this tool
QAction* getAction() const { return m_action; }
/// Handles shortcut override event from widget, over which tool operates
/// \param widget Widget, over which tool operates
/// \param event Event
virtual void shortcutOverrideEvent(QWidget* widget, QKeyEvent* event);
/// Handles key press event from widget, over which tool operates
/// \param widget Widget, over which tool operates
/// \param event Event
virtual void keyPressEvent(QWidget* widget, QKeyEvent* event);
/// Handles key release event from widget
/// \param widget Widget
/// \param event Event
virtual void keyReleaseEvent(QWidget* widget, QKeyEvent* event);
/// Handles mouse press event from widget, over which tool operates
/// \param widget Widget, over which tool operates
/// \param event Event
virtual void mousePressEvent(QWidget* widget, QMouseEvent* event);
/// Handles mouse double click event from widget, over which tool operates
/// \param widget Widget, over which tool operates
/// \param event Event
virtual void mouseDoubleClickEvent(QWidget* widget, QMouseEvent* event);
/// Handles mouse release event from widget, over which tool operates
/// \param widget Widget, over which tool operates
/// \param event Event
virtual void mouseReleaseEvent(QWidget* widget, QMouseEvent* event);
/// Handles mouse move event from widget, over which tool operates
/// \param widget Widget, over which tool operates
/// \param event Event
virtual void mouseMoveEvent(QWidget* widget, QMouseEvent* event);
/// Handles mouse wheel event from widget, over which tool operates
/// \param widget Widget, over which tool operates
/// \param event Event
virtual void wheelEvent(QWidget* widget, QWheelEvent* event);
/// Returns actual cursor defined by the tool. Cursor can be undefined,
/// in this case, optional will be set to nullopt.
const std::optional<QCursor>& getCursor() const;
signals:
/// Informs about tool activity changed (if tool is enabled or disabled)
/// \param active Is tool active?
void toolActivityChanged(bool active);
/// This is signal is set, when we want to display information message
/// to the user, with timeout in miliseconds, after which message
/// dissappears.
/// \param text Text, which should be displayed to the user
/// \param timeout Timeout in miliseconds
void messageDisplayRequest(const QString& text, int timeout);
protected:
virtual void setActiveImpl(bool active);
virtual void updateActions();
/// Returns currently active tool from toolstack,
/// or nullptr, if no active tool from toolstack exists.
PDFWidgetTool* getTopToolstackTool() const;
const PDFDocument* getDocument() const { return m_document; }
PDFDrawWidgetProxy* getProxy() const { return m_proxy; }
inline void setCursor(QCursor cursor) { m_cursor = qMove(cursor); }
inline void unsetCursor() { m_cursor = std::nullopt; }
void addTool(PDFWidgetTool* tool);
void removeTool();
private:
bool m_active;
const PDFDocument* m_document;
QAction* m_action;
PDFDrawWidgetProxy* m_proxy;
std::vector<PDFWidgetTool*> m_toolStack;
std::optional<QCursor> m_cursor;
};
/// Simple tool for find text in PDF document. It is much simpler than advanced
/// search and can't search using regular expressions.
class PDFFindTextTool : public PDFWidgetTool
{
Q_OBJECT
private:
using BaseClass = PDFWidgetTool;
public:
/// Construct new text search tool
/// \param proxy Draw widget proxy
/// \param prevAction Action for navigating to previous result
/// \param nextAction Action for navigating to next result
/// \param parent Parent object
/// \param parentDialog Paret dialog for tool dialog
explicit PDFFindTextTool(PDFDrawWidgetProxy* proxy, QAction* prevAction, QAction* nextAction, QObject* parent, QWidget* parentDialog);
virtual void drawPage(QPainter* painter,
PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter,
const QTransform& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const override;
protected:
virtual void setActiveImpl(bool active) override;
virtual void updateActions() override;
private:
void onSearchText();
void onActionPrevious();
void onActionNext();
void onDialogRejected();
void performSearch();
void updateResultsUI();
void updateTitle();
void clearResults();
void goToCurrentResult();
QAction* m_prevAction;
QAction* m_nextAction;
QWidget* m_parentDialog;
QDialog* m_dialog;
QCheckBox* m_caseSensitiveCheckBox;
QCheckBox* m_wholeWordsCheckBox;
QLineEdit* m_findTextEdit;
QPushButton* m_previousButton;
QPushButton* m_nextButton;
QString m_savedText;
bool m_savedIsCaseSensitive = false;
bool m_savedIsWholeWords = false;
pdf::PDFTextSelection getTextSelection() const { return m_textSelection.get(this, &PDFFindTextTool::getTextSelectionImpl); }
pdf::PDFTextSelection getTextSelectionImpl() const;
pdf::PDFTextSelection getTextSelectionSelectedResultOnly() const;
struct SearchParameters
{
QString phrase;
bool isCaseSensitive = false;
bool isWholeWordsOnly = false;
bool isSearchFinished = false;
};
SearchParameters m_parameters;
pdf::PDFFindResults m_findResults;
size_t m_selectedResultIndex;
mutable pdf::PDFCachedItem<pdf::PDFTextSelection> m_textSelection;
};
/// Tool for selection of text in document
class PDFSelectTextTool : public PDFWidgetTool
{
Q_OBJECT
private:
using BaseClass = PDFWidgetTool;
public:
/// Construct new text selection tool
/// \param proxy Draw widget proxy
/// \param action Tool activation action
/// \param copyTextAction Copy text action
/// \param selectAllAction Select all text action
/// \param deselectAction Deselect text action
/// \param parent Parent object
explicit PDFSelectTextTool(PDFDrawWidgetProxy* proxy, QAction* action, QAction* copyTextAction, QAction* selectAllAction, QAction* deselectAction, QObject* parent);
virtual void drawPage(QPainter* painter,
PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter,
const QTransform& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const override;
virtual void mousePressEvent(QWidget* widget, QMouseEvent* event) override;
virtual void mouseReleaseEvent(QWidget* widget, QMouseEvent* event) override;
virtual void mouseMoveEvent(QWidget* widget, QMouseEvent* event) override;
protected:
virtual void setActiveImpl(bool active) override;
virtual void updateActions() override;
private:
void updateCursor();
void onActionCopyText();
void onActionSelectAll();
void onActionDeselect();
void setSelection(pdf::PDFTextSelection&& textSelection);
struct SelectionInfo
{
PDFInteger pageIndex = -1;
QPointF selectionStartPoint;
};
QAction* m_copyTextAction;
QAction* m_selectAllAction;
QAction* m_deselectAction;
pdf::PDFTextSelection m_textSelection;
SelectionInfo m_selectionInfo;
bool m_isCursorOverText;
};
/// Tool to magnify specific area in the drawing widget
class PDF4QTLIBSHARED_EXPORT PDFMagnifierTool : public PDFWidgetTool
{
Q_OBJECT
private:
using BaseClass = PDFWidgetTool;
public:
/// Constructs new magnifier tool
/// \param proxy Draw widget proxy
/// \param action Tool activation action
/// \param parent Parent object
explicit PDFMagnifierTool(PDFDrawWidgetProxy* proxy, QAction* action, QObject* parent);
virtual void mousePressEvent(QWidget* widget, QMouseEvent* event) override;
virtual void mouseReleaseEvent(QWidget* widget, QMouseEvent* event) override;
virtual void mouseMoveEvent(QWidget* widget, QMouseEvent* event) override;
virtual void drawPostRendering(QPainter* painter, QRect rect) const override;
int getMagnifierSize() const;
void setMagnifierSize(int magnifierSize);
PDFReal getMagnifierZoom() const;
void setMagnifierZoom(const PDFReal& magnifierZoom);
protected:
virtual void setActiveImpl(bool active) override;
private:
QPoint m_mousePos;
int m_magnifierSize;
PDFReal m_magnifierZoom;
};
/// Tools for picking various items on page - points, rectangles, images etc.
class PDF4QTLIBSHARED_EXPORT PDFPickTool : public PDFWidgetTool
{
Q_OBJECT
private:
using BaseClass = PDFWidgetTool;
public:
enum class Mode
{
Points, ///< Pick points
Rectangles, ///< Pick rectangles
Images ///< Pick images
};
/// Constructs new picking tool
/// \param proxy Draw widget proxy
/// \param mode Picking mode
/// \param parent Parent object
explicit PDFPickTool(PDFDrawWidgetProxy* proxy, Mode mode, QObject* parent);
virtual void drawPage(QPainter* painter, PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter,
const QTransform& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const override;
virtual void drawPostRendering(QPainter* painter, QRect rect) const override;
virtual void mousePressEvent(QWidget* widget, QMouseEvent* event) override;
virtual void mouseReleaseEvent(QWidget* widget, QMouseEvent* event) override;
virtual void mouseMoveEvent(QWidget* widget, QMouseEvent* event) override;
QPointF getSnappedPoint() const;
PDFInteger getPageIndex() const { return m_pageIndex; }
const std::vector<QPointF>& getPickedPoints() const { return m_pickedPoints; }
/// Sets custom snap points for given page. If points on page aren't currently picked,
/// function does nothing. Snap data are not updated.
/// \param pageIndex pageIndex
/// \param snapPoints Custom snap points
void setCustomSnapPoints(PDFInteger pageIndex, const std::vector<QPointF>& snapPoints);
void resetTool();
/// Turns on/off drawing of selection rectangle, if rectangle picking
/// mode is active.
/// \param drawSelectionRectangle Draw selection rectangle?
void setDrawSelectionRectangle(bool drawSelectionRectangle);
QColor getSelectionRectangleColor() const;
void setSelectionRectangleColor(QColor selectionRectangleColor);
signals:
void pointPicked(pdf::PDFInteger pageIndex, QPointF pagePoint);
void rectanglePicked(pdf::PDFInteger pageIndex, QRectF pageRectangle);
void imagePicked(const QImage& image);
protected:
virtual void setActiveImpl(bool active) override;
private:
void buildSnapData();
Mode m_mode;
PDFSnapper m_snapper;
QPoint m_mousePosition;
PDFInteger m_pageIndex;
std::vector<QPointF> m_pickedPoints;
bool m_drawSelectionRectangle;
QColor m_selectionRectangleColor;
};
/// Tool for selection of table in document. Rows and columns
/// are automatically detected and are modifiable by the user.
class PDFSelectTableTool : public PDFWidgetTool
{
Q_OBJECT
private:
using BaseClass = PDFWidgetTool;
public:
/// Construct new table selection tool
/// \param proxy Draw widget proxy
/// \param action Tool activation action
/// \param parent Parent object
explicit PDFSelectTableTool(PDFDrawWidgetProxy* proxy, QAction* action, QObject* parent);
virtual void drawPage(QPainter* painter,
PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter,
const QTransform& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const override;
virtual void shortcutOverrideEvent(QWidget* widget, QKeyEvent* event) override;
virtual void keyPressEvent(QWidget* widget, QKeyEvent* event) override;
virtual void mousePressEvent(QWidget* widget, QMouseEvent* event) override;
virtual void mouseMoveEvent(QWidget* widget, QMouseEvent* event) override;
protected:
virtual void setActiveImpl(bool active) override;
private:
void setPickedRectangle(const QRectF& newPickedRectangle);
void setPageIndex(PDFInteger newPageIndex);
void setTextLayout(PDFTextLayout&& newTextLayout);
void autodetectTableGeometry();
bool isTablePicked() const;
void onRectanglePicked(PDFInteger pageIndex, QRectF pageRectangle);
PDFPickTool* m_pickTool;
PDFInteger m_pageIndex;
QRectF m_pickedRectangle;
PDFTextLayout m_textLayout;
bool m_isTransposed;
std::vector<PDFReal> m_horizontalBreaks;
std::vector<PDFReal> m_verticalBreaks;
PageRotation m_rotation;
};
/// Tool that makes screenshot of page area and copies it to the clipboard,
/// using current client area to determine image size.
class PDF4QTLIBSHARED_EXPORT PDFScreenshotTool : public PDFWidgetTool
{
Q_OBJECT
private:
using BaseClass = PDFWidgetTool;
public:
/// Constructs new screenshot tool
/// \param proxy Draw widget proxy
/// \param action Tool activation action
/// \param parent Parent object
explicit PDFScreenshotTool(PDFDrawWidgetProxy* proxy, QAction* action, QObject* parent);
private:
void onRectanglePicked(PDFInteger pageIndex, QRectF pageRectangle);
PDFPickTool* m_pickTool;
};
/// Tool that extracts image from page and copies it to the clipboard,
/// using image original size (not zoomed size from widget area)
class PDF4QTLIBSHARED_EXPORT PDFExtractImageTool : public PDFWidgetTool
{
Q_OBJECT
private:
using BaseClass = PDFWidgetTool;
public:
/// Constructs new extract image tool
/// \param proxy Draw widget proxy
/// \param action Tool activation action
/// \param parent Parent object
explicit PDFExtractImageTool(PDFDrawWidgetProxy* proxy, QAction* action, QObject* parent);
protected:
virtual void updateActions() override;
private:
void onImagePicked(const QImage& image);
PDFPickTool* m_pickTool;
};
/// Manager used for managing tools, their activity, availability
/// and other settings. It also defines a predefined set of tools,
/// available for various purposes (text searching, magnifier tool etc.)
class PDF4QTLIBSHARED_EXPORT PDFToolManager : public QObject, public IDrawWidgetInputInterface
{
Q_OBJECT
private:
using BaseClass = QObject;
public:
struct Actions
{
QAction* findPrevAction = nullptr; ///< Action for navigating to previous result
QAction* findNextAction = nullptr; ///< Action for navigating to next result
QAction* selectTextToolAction = nullptr;
QAction* selectTableToolAction = nullptr;
QAction* selectAllAction = nullptr;
QAction* deselectAction = nullptr;
QAction* copyTextAction = nullptr;
QAction* magnifierAction = nullptr;
QAction* screenshotToolAction = nullptr;
QAction* extractImageAction = nullptr;
};
/// Construct new text search tool
/// \param proxy Draw widget proxy
/// \param actions Actions
/// \param parent Parent object
/// \param parentDialog Paret dialog for tool dialog
explicit PDFToolManager(PDFDrawWidgetProxy* proxy, Actions actions, QObject* parent, QWidget* parentDialog);
/// Sets document
/// \param document Document
void setDocument(const PDFModifiedDocument& document);
enum PredefinedTools
{
PickRectangleTool,
FindTextTool,
SelectTextTool,
SelectTableTool,
MagnifierTool,
ScreenshotTool,
ExtractImageTool,
ToolEnd
};
/// Sets active tool
void setActiveTool(PDFWidgetTool* tool);
/// Adds a new tool to tool manager
void addTool(PDFWidgetTool* tool);
/// Picks rectangle, if rectangle is successfully picked,
/// then callback is called.
/// \param callback Callback function
void pickRectangle(std::function<void(PDFInteger, QRectF)> callback);
/// Returns first active tool from tool set. If no tool is active,
/// then nullptr is returned.
PDFWidgetTool* getActiveTool() const;
/// Returns find text tool
PDFFindTextTool* getFindTextTool() const;
/// Returns magnifier tool
PDFMagnifierTool* getMagnifierTool() const;
/// Handles shortcut override event
virtual void shortcutOverrideEvent(QWidget* widget, QKeyEvent* event) override;
/// Handles key press event from widget, over which tool operates
/// \param widget Widget, over which tool operates
/// \param event Event
virtual void keyPressEvent(QWidget* widget, QKeyEvent* event) override;
/// Handles key release event from widget
/// \param widget Widget
/// \param event Event
virtual void keyReleaseEvent(QWidget* widget, QKeyEvent* event) override;
/// Handles mouse press event from widget, over which tool operates
/// \param widget Widget, over which tool operates
/// \param event Event
virtual void mousePressEvent(QWidget* widget, QMouseEvent* event) override;
/// Handles mouse double click event from widget, over which tool operates
/// \param widget Widget, over which tool operates
/// \param event Event
virtual void mouseDoubleClickEvent(QWidget* widget, QMouseEvent* event) override;
/// Handles mouse release event from widget, over which tool operates
/// \param widget Widget, over which tool operates
/// \param event Event
virtual void mouseReleaseEvent(QWidget* widget, QMouseEvent* event) override;
/// Handles mouse move event from widget, over which tool operates
/// \param widget Widget, over which tool operates
/// \param event Event
virtual void mouseMoveEvent(QWidget* widget, QMouseEvent* event) override;
/// Handles mouse wheel event from widget, over which tool operates
/// \param widget Widget, over which tool operates
/// \param event Event
virtual void wheelEvent(QWidget* widget, QWheelEvent* event) override;
/// Returns actual cursor defined by the tool. Cursor can be undefined,
/// in this case, optional will be set to nullopt.
virtual const std::optional<QCursor>& getCursor() const override;
virtual QString getTooltip() const override { return QString(); }
virtual int getInputPriority() const override { return ToolPriority; }
signals:
/// This is signal is set, when we want to display information message
/// to the user, with timeout in miliseconds, after which message
/// dissappears.
/// \param text Text, which should be displayed to the user
/// \param timeout Timeout in miliseconds
void messageDisplayRequest(const QString& text, int timeout);
/// This signal is emitted, when tool changes the document by some way
/// \param documet Modified document
void documentModified(pdf::PDFModifiedDocument document);
private:
void onToolActivityChanged(bool active);
void onToolActionTriggered(bool checked);
void onRectanglePicked(PDFInteger pageIndex, QRectF pageRectangle);
std::set<PDFWidgetTool*> m_tools;
std::array<PDFWidgetTool*, ToolEnd> m_predefinedTools;
std::map<QAction*, PDFWidgetTool*> m_actionsToTools;
std::function<void(PDFInteger, QRectF)> m_pickRectangleCallback;
};
} // namespace pdf
#endif // PDFWIDGETTOOL_H

View File

@ -0,0 +1,212 @@
// Copyright (C) 2019-2022 Jakub Melka
//
// This file is part of PDF4QT.
//
// PDF4QT is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// with the written consent of the copyright owner, any later version.
//
// PDF4QT is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
#include "pdfwidgetutils.h"
#include "pdfcolorconvertor.h"
#include "pdfdbgheap.h"
#include <QDialog>
#include <QLayout>
#include <QPainter>
#include <QPixmap>
#include <QGroupBox>
#include <QApplication>
#ifdef Q_OS_MAC
int qt_default_dpi_x() { return 72; }
int qt_default_dpi_y() { return 72; }
#else
int qt_default_dpi_x() { return 96; }
int qt_default_dpi_y() { return 96; }
#endif
namespace pdf
{
static constexpr bool isScalingNeeded()
{
return QT_VERSION_MAJOR < 6;
}
int PDFWidgetUtils::getPixelSize(const QPaintDevice* device, pdf::PDFReal sizeMM)
{
const int width = device->width();
const int height = device->height();
if (width > height)
{
return PDFReal(width) * sizeMM / PDFReal(device->widthMM());
}
else
{
return PDFReal(height) * sizeMM / PDFReal(device->heightMM());
}
}
int PDFWidgetUtils::scaleDPI_x(const QPaintDevice* device, int unscaledSize)
{
if constexpr (isScalingNeeded())
{
const double logicalDPI_x = device->logicalDpiX();
const double defaultDPI_x = qt_default_dpi_x();
return (logicalDPI_x / defaultDPI_x) * unscaledSize;
}
else
{
return unscaledSize;
}
}
int PDFWidgetUtils::scaleDPI_y(const QPaintDevice* device, int unscaledSize)
{
if constexpr (isScalingNeeded())
{
const double logicalDPI_y = device->logicalDpiY();
const double defaultDPI_y = qt_default_dpi_y();
return (logicalDPI_y / defaultDPI_y) * unscaledSize;
}
else
{
return unscaledSize;
}
}
PDFReal PDFWidgetUtils::scaleDPI_x(const QPaintDevice* device, PDFReal unscaledSize)
{
if constexpr (isScalingNeeded())
{
const double logicalDPI_x = device->logicalDpiX();
const double defaultDPI_x = qt_default_dpi_x();
return (logicalDPI_x / defaultDPI_x) * unscaledSize;
}
else
{
return unscaledSize;
}
}
void PDFWidgetUtils::scaleWidget(QWidget* widget, QSize unscaledSize)
{
if constexpr (!isScalingNeeded())
{
return;
}
const double logicalDPI_x = widget->logicalDpiX();
const double logicalDPI_y = widget->logicalDpiY();
const double defaultDPI_x = qt_default_dpi_x();
const double defaultDPI_y = qt_default_dpi_y();
const int width = (logicalDPI_x / defaultDPI_x) * unscaledSize.width();
const int height = (logicalDPI_y / defaultDPI_y) * unscaledSize.height();
widget->resize(width, height);
}
QSize PDFWidgetUtils::scaleDPI(const QPaintDevice* widget, QSize unscaledSize)
{
if constexpr (isScalingNeeded())
{
const double logicalDPI_x = widget->logicalDpiX();
const double logicalDPI_y = widget->logicalDpiY();
const double defaultDPI_x = qt_default_dpi_x();
const double defaultDPI_y = qt_default_dpi_y();
const int width = (logicalDPI_x / defaultDPI_x) * unscaledSize.width();
const int height = (logicalDPI_y / defaultDPI_y) * unscaledSize.height();
return QSize(width, height);
}
else
{
return unscaledSize;
}
}
void PDFWidgetUtils::style(QWidget* widget)
{
const int dialogMarginX = scaleDPI_x(widget, 12);
const int dialogMarginY = scaleDPI_y(widget, 12);
const int dialogSpacing = scaleDPI_y(widget, 12);
const int groupBoxMarginX = scaleDPI_x(widget, 20);
const int groupBoxMarginY = scaleDPI_y(widget, 20);
const int groupBoxSpacing = scaleDPI_y(widget, 12);
QList<QWidget*> childWidgets = widget->findChildren<QWidget*>();
childWidgets.append(widget);
for (QWidget* childWidget : childWidgets)
{
if (qobject_cast<QGroupBox*>(childWidget))
{
childWidget->layout()->setContentsMargins(groupBoxMarginX, groupBoxMarginY, groupBoxMarginX, groupBoxMarginY);
childWidget->layout()->setSpacing(groupBoxSpacing);
}
else if (qobject_cast<QDialog*>(childWidget))
{
childWidget->layout()->setContentsMargins(dialogMarginX, dialogMarginY, dialogMarginX, dialogMarginY);
childWidget->layout()->setSpacing(dialogSpacing);
}
}
}
bool PDFWidgetUtils::isDarkTheme()
{
QPalette palette = QApplication::palette();
QColor backgroundColor = palette.color(QPalette::Window);
QColor textColor = palette.color(QPalette::WindowText);
return backgroundColor.lightness() < textColor.lightness();
}
void PDFWidgetUtils::convertActionForDarkTheme(QAction* action, QSize iconSize, qreal devicePixelRatioF)
{
if (!action)
{
return;
}
QIcon icon = action->icon();
if (!icon.isNull())
{
icon = pdf::PDFWidgetUtils::convertIconForDarkTheme(icon, iconSize, devicePixelRatioF);
action->setIcon(icon);
}
}
QIcon PDFWidgetUtils::convertIconForDarkTheme(QIcon icon, QSize iconSize, qreal devicePixelRatioF)
{
QPixmap pixmap(iconSize * devicePixelRatioF);
pixmap.setDevicePixelRatio(devicePixelRatioF);
pixmap.fill(Qt::transparent);
QPainter painter(&pixmap);
icon.paint(&painter, QRect(0, 0, iconSize.width(), iconSize.height()));
painter.end();
PDFColorConvertor convertor;
convertor.setMode(PDFColorConvertor::Mode::InvertedColors);
QImage image = pixmap.toImage();
image = convertor.convert(image);
pixmap = QPixmap::fromImage(image);
return QIcon(pixmap);
}
} // namespace pdf

View File

@ -0,0 +1,94 @@
// Copyright (C) 2019-2021 Jakub Melka
//
// This file is part of PDF4QT.
//
// PDF4QT is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// with the written consent of the copyright owner, any later version.
//
// PDF4QT is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
#ifndef PDFWIDGETUTILS_H
#define PDFWIDGETUTILS_H
#include "pdfglobal.h"
#include <QIcon>
#include <QWidget>
namespace pdf
{
class PDF4QTLIBSHARED_EXPORT PDFWidgetUtils
{
public:
PDFWidgetUtils() = delete;
/// Converts size in MM to pixel size
static int getPixelSize(const QPaintDevice* device, pdf::PDFReal sizeMM);
/// Scale horizontal DPI value
/// \param device Paint device to obtain logical DPI for scaling
static int scaleDPI_x(const QPaintDevice* device, int unscaledSize);
/// Scale vertical DPI value
/// \param device Paint device to obtain logical DPI for scaling
static int scaleDPI_y(const QPaintDevice* device, int unscaledSize);
/// Scale horizontal DPI value
/// \param device Paint device to obtain logical DPI for scaling
static PDFReal scaleDPI_x(const QPaintDevice* device, PDFReal unscaledSize);
/// Scales widget based on DPI
/// \param widget Widget to be scaled
/// \param unscaledSize Unscaled size of the widget
static void scaleWidget(QWidget* widget, QSize unscaledSize);
/// Scales size based on DPI
/// \param device Paint device to obtain logical DPI for scaling
/// \param unscaledSize Unscaled size
static QSize scaleDPI(const QPaintDevice* widget, QSize unscaledSize);
/// Apply style to the widget
static void style(QWidget* widget);
/// Vrátí true, pokud je aktuálně nastavená dark theme
/// pro aplikaci.
static bool isDarkTheme();
/// Converts an action's icon for use in a dark theme.
/// \param action Pointer to the QAction to be converted.
/// \param iconSize The size of the action's icon.
/// \param devicePixelRatioF The device pixel ratio factor.
static void convertActionForDarkTheme(QAction* action, QSize iconSize, qreal devicePixelRatioF);
/// Converts an icon for use in a dark theme.
/// \param icon The icon to be converted.
/// \param iconSize The size of the icon.
/// \param devicePixelRatioF The device pixel ratio factor.
static QIcon convertIconForDarkTheme(QIcon icon, QSize iconSize, qreal devicePixelRatioF);
/// Converts an action's icon for use in a dark theme.
/// \param action Pointer to the QAction to be converted.
/// \param iconSize The size of the action's icon.
/// \param devicePixelRatioF The device pixel ratio factor.
template<typename Container>
static void convertActionsForDarkTheme(const Container& actions, QSize iconSize, qreal devicePixelRatioF)
{
for (QAction* action : actions)
{
convertActionForDarkTheme(action, iconSize, devicePixelRatioF);
}
}
};
} // namespace pdf
#endif // PDFWIDGETUTILS_H