Merge remote-tracking branch 'remotes/origin/branches/issue54'

This commit is contained in:
Jakub Melka 2023-12-17 19:11:46 +01:00
commit 5d001ede45
36 changed files with 1634 additions and 121 deletions

View File

@ -27,12 +27,12 @@ add_executable(Pdf4QtDocPageOrganizer
pageitemdelegate.h pageitemdelegate.h
pageitemmodel.cpp pageitemmodel.cpp
pageitemmodel.h pageitemmodel.h
selectbookmarkstoregroupdialog.cpp selectoutlinetoregroupdialog.cpp
selectbookmarkstoregroupdialog.h selectoutlinetoregroupdialog.h
aboutdialog.ui aboutdialog.ui
assembleoutputsettingsdialog.ui assembleoutputsettingsdialog.ui
mainwindow.ui mainwindow.ui
selectbookmarkstoregroupdialog.ui selectoutlinetoregroupdialog.ui
resources.qrc resources.qrc
icon.rc icon.rc
) )

View File

@ -20,7 +20,7 @@
#include "aboutdialog.h" #include "aboutdialog.h"
#include "assembleoutputsettingsdialog.h" #include "assembleoutputsettingsdialog.h"
#include "selectbookmarkstoregroupdialog.h" #include "selectoutlinetoregroupdialog.h"
#include "pdfaction.h" #include "pdfaction.h"
#include "pdfwidgetutils.h" #include "pdfwidgetutils.h"
@ -94,7 +94,7 @@ MainWindow::MainWindow(QWidget* parent) :
ui->actionInvert_Selection->setData(int(Operation::InvertSelection)); ui->actionInvert_Selection->setData(int(Operation::InvertSelection));
ui->actionRegroup_Even_Odd->setData(int(Operation::RegroupEvenOdd)); ui->actionRegroup_Even_Odd->setData(int(Operation::RegroupEvenOdd));
ui->actionRegroup_by_Page_Pairs->setData(int(Operation::RegroupPaired)); ui->actionRegroup_by_Page_Pairs->setData(int(Operation::RegroupPaired));
ui->actionRegroup_by_Bookmarks->setData(int(Operation::RegroupBookmarks)); ui->actionRegroup_by_Outline->setData(int(Operation::RegroupOutline));
ui->actionRegroup_by_Alternating_Pages->setData(int(Operation::RegroupAlternatingPages)); ui->actionRegroup_by_Alternating_Pages->setData(int(Operation::RegroupAlternatingPages));
ui->actionRegroup_by_Alternating_Pages_Reversed_Order->setData(int(Operation::RegroupAlternatingPagesReversed)); ui->actionRegroup_by_Alternating_Pages_Reversed_Order->setData(int(Operation::RegroupAlternatingPagesReversed));
ui->actionPrepare_Icon_Theme->setData(int(Operation::PrepareIconTheme)); ui->actionPrepare_Icon_Theme->setData(int(Operation::PrepareIconTheme));
@ -132,7 +132,7 @@ MainWindow::MainWindow(QWidget* parent) :
m_iconTheme.registerAction(ui->actionClear, ":/pdfdocpage/resources/clear.svg"); m_iconTheme.registerAction(ui->actionClear, ":/pdfdocpage/resources/clear.svg");
m_iconTheme.registerAction(ui->actionRegroup_Even_Odd, ":/pdfdocpage/resources/regroup-even-odd.svg"); m_iconTheme.registerAction(ui->actionRegroup_Even_Odd, ":/pdfdocpage/resources/regroup-even-odd.svg");
m_iconTheme.registerAction(ui->actionRegroup_by_Page_Pairs, ":/pdfdocpage/resources/regroup-pairs.svg"); m_iconTheme.registerAction(ui->actionRegroup_by_Page_Pairs, ":/pdfdocpage/resources/regroup-pairs.svg");
m_iconTheme.registerAction(ui->actionRegroup_by_Bookmarks, ":/pdfdocpage/resources/regroup-bookmarks.svg"); m_iconTheme.registerAction(ui->actionRegroup_by_Outline, ":/pdfdocpage/resources/regroup-outline.svg");
m_iconTheme.registerAction(ui->actionRegroup_by_Alternating_Pages, ":/pdfdocpage/resources/regroup-alternating.svg"); m_iconTheme.registerAction(ui->actionRegroup_by_Alternating_Pages, ":/pdfdocpage/resources/regroup-alternating.svg");
m_iconTheme.registerAction(ui->actionRegroup_by_Alternating_Pages_Reversed_Order, ":/pdfdocpage/resources/regroup-alternating-reversed.svg"); m_iconTheme.registerAction(ui->actionRegroup_by_Alternating_Pages_Reversed_Order, ":/pdfdocpage/resources/regroup-alternating-reversed.svg");
m_iconTheme.registerAction(ui->actionInvert_Selection, ":/pdfdocpage/resources/invert-selection.svg"); m_iconTheme.registerAction(ui->actionInvert_Selection, ":/pdfdocpage/resources/invert-selection.svg");
@ -162,7 +162,7 @@ MainWindow::MainWindow(QWidget* parent) :
selectToolbar->addActions({ ui->actionSelect_None, ui->actionSelect_All, ui->actionSelect_Even, ui->actionSelect_Odd, ui->actionSelect_Portrait, ui->actionSelect_Landscape, ui->actionInvert_Selection }); selectToolbar->addActions({ ui->actionSelect_None, ui->actionSelect_All, ui->actionSelect_Even, ui->actionSelect_Odd, ui->actionSelect_Portrait, ui->actionSelect_Landscape, ui->actionInvert_Selection });
QToolBar* regroupToolbar = addToolBar(tr("Regroup")); QToolBar* regroupToolbar = addToolBar(tr("Regroup"));
regroupToolbar->setObjectName("regroup_toolbar"); regroupToolbar->setObjectName("regroup_toolbar");
regroupToolbar->addActions({ ui->actionRegroup_Even_Odd, ui->actionRegroup_by_Page_Pairs, ui->actionRegroup_by_Bookmarks, ui->actionRegroup_by_Alternating_Pages, ui->actionRegroup_by_Alternating_Pages_Reversed_Order }); regroupToolbar->addActions({ ui->actionRegroup_Even_Odd, ui->actionRegroup_by_Page_Pairs, ui->actionRegroup_by_Outline, ui->actionRegroup_by_Alternating_Pages, ui->actionRegroup_by_Alternating_Pages_Reversed_Order });
QToolBar* zoomToolbar = addToolBar(tr("Zoom")); QToolBar* zoomToolbar = addToolBar(tr("Zoom"));
zoomToolbar->setObjectName("zoom_toolbar"); zoomToolbar->setObjectName("zoom_toolbar");
zoomToolbar->addActions({ ui->actionZoom_In, ui->actionZoom_Out }); zoomToolbar->addActions({ ui->actionZoom_In, ui->actionZoom_Out });
@ -283,7 +283,7 @@ void MainWindow::onWorkspaceCustomContextMenuRequested(const QPoint& point)
regroupMenu->addAction(ui->actionRegroup_by_Alternating_Pages); regroupMenu->addAction(ui->actionRegroup_by_Alternating_Pages);
regroupMenu->addAction(ui->actionRegroup_by_Alternating_Pages_Reversed_Order); regroupMenu->addAction(ui->actionRegroup_by_Alternating_Pages_Reversed_Order);
regroupMenu->addAction(ui->actionRegroup_by_Page_Pairs); regroupMenu->addAction(ui->actionRegroup_by_Page_Pairs);
regroupMenu->addAction(ui->actionRegroup_by_Bookmarks); regroupMenu->addAction(ui->actionRegroup_by_Outline);
contextMenu->addSeparator(); contextMenu->addSeparator();
contextMenu->addAction(ui->actionGroup); contextMenu->addAction(ui->actionGroup);
contextMenu->addAction(ui->actionUngroup); contextMenu->addAction(ui->actionUngroup);
@ -482,7 +482,7 @@ bool MainWindow::canPerformOperation(Operation operation) const
case Operation::RegroupPaired: case Operation::RegroupPaired:
return !isModelEmpty && !selection.isEmpty(); return !isModelEmpty && !selection.isEmpty();
case Operation::RegroupBookmarks: case Operation::RegroupOutline:
{ {
PageItemModel::SelectionInfo info = m_model->getSelectionInfo(selection); PageItemModel::SelectionInfo info = m_model->getSelectionInfo(selection);
return info.isSingleDocument(); return info.isSingleDocument();
@ -916,7 +916,7 @@ void MainWindow::performOperation(Operation operation)
break; break;
} }
case Operation::RegroupBookmarks: case Operation::RegroupOutline:
{ {
QModelIndexList indexes = ui->documentItemsView->selectionModel()->selection().indexes(); QModelIndexList indexes = ui->documentItemsView->selectionModel()->selection().indexes();
@ -929,9 +929,9 @@ void MainWindow::performOperation(Operation operation)
if (it != documents.end()) if (it != documents.end())
{ {
const pdf::PDFDocument* document = &it->second.document; const pdf::PDFDocument* document = &it->second.document;
SelectBookmarksToRegroupDialog dialog(document, this); SelectOutlineToRegroupDialog dialog(document, this);
if (dialog.exec() == SelectBookmarksToRegroupDialog::Accepted) if (dialog.exec() == SelectOutlineToRegroupDialog::Accepted)
{ {
std::vector<pdf::PDFInteger> breakPageIndices; std::vector<pdf::PDFInteger> breakPageIndices;
std::vector<const pdf::PDFOutlineItem*> outlineItems = dialog.getSelectedOutlineItems(); std::vector<const pdf::PDFOutlineItem*> outlineItems = dialog.getSelectedOutlineItems();
@ -972,7 +972,7 @@ void MainWindow::performOperation(Operation operation)
std::sort(breakPageIndices.begin(), breakPageIndices.end()); std::sort(breakPageIndices.begin(), breakPageIndices.end());
breakPageIndices.erase(std::unique(breakPageIndices.begin(), breakPageIndices.end()), breakPageIndices.end()); breakPageIndices.erase(std::unique(breakPageIndices.begin(), breakPageIndices.end()), breakPageIndices.end());
m_model->regroupBookmarks(indexes, breakPageIndices); m_model->regroupOutline(indexes, breakPageIndices);
} }
} }
} }

View File

@ -91,7 +91,7 @@ public:
RegroupEvenOdd, RegroupEvenOdd,
RegroupPaired, RegroupPaired,
RegroupBookmarks, RegroupOutline,
RegroupAlternatingPages, RegroupAlternatingPages,
RegroupAlternatingPagesReversed, RegroupAlternatingPagesReversed,

View File

@ -138,7 +138,7 @@
</property> </property>
<addaction name="actionRegroup_Even_Odd"/> <addaction name="actionRegroup_Even_Odd"/>
<addaction name="actionRegroup_by_Page_Pairs"/> <addaction name="actionRegroup_by_Page_Pairs"/>
<addaction name="actionRegroup_by_Bookmarks"/> <addaction name="actionRegroup_by_Outline"/>
<addaction name="actionRegroup_by_Alternating_Pages"/> <addaction name="actionRegroup_by_Alternating_Pages"/>
<addaction name="actionRegroup_by_Alternating_Pages_Reversed_Order"/> <addaction name="actionRegroup_by_Alternating_Pages_Reversed_Order"/>
</widget> </widget>
@ -533,13 +533,13 @@
<string>Regroup by Page Pairs</string> <string>Regroup by Page Pairs</string>
</property> </property>
</action> </action>
<action name="actionRegroup_by_Bookmarks"> <action name="actionRegroup_by_Outline">
<property name="icon"> <property name="icon">
<iconset resource="resources.qrc"> <iconset resource="resources.qrc">
<normaloff>:/pdfdocpage/resources/regroup-bookmarks.svg</normaloff>:/pdfdocpage/resources/regroup-bookmarks.svg</iconset> <normaloff>:/pdfdocpage/resources/regroup-outline.svg</normaloff>:/pdfdocpage/resources/regroup-outline.svg</iconset>
</property> </property>
<property name="text"> <property name="text">
<string>Regroup by Bookmarks</string> <string>Regroup by Outline</string>
</property> </property>
</action> </action>
<action name="actionRegroup_by_Alternating_Pages"> <action name="actionRegroup_by_Alternating_Pages">

View File

@ -703,7 +703,7 @@ void PageItemModel::regroupPaired(const QModelIndexList& list)
} }
} }
void PageItemModel::regroupBookmarks(const QModelIndexList& list, const std::vector<pdf::PDFInteger>& indices) void PageItemModel::regroupOutline(const QModelIndexList& list, const std::vector<pdf::PDFInteger>& indices)
{ {
if (list.empty()) if (list.empty())
{ {

View File

@ -191,7 +191,7 @@ public:
void regroupEvenOdd(const QModelIndexList& list); void regroupEvenOdd(const QModelIndexList& list);
void regroupPaired(const QModelIndexList& list); void regroupPaired(const QModelIndexList& list);
void regroupBookmarks(const QModelIndexList& list, const std::vector<pdf::PDFInteger>& indices); void regroupOutline(const QModelIndexList& list, const std::vector<pdf::PDFInteger>& indices);
void regroupAlternatingPages(const QModelIndexList& list, bool reversed); void regroupAlternatingPages(const QModelIndexList& list, bool reversed);
bool canUndo() const { return !m_undoSteps.empty(); } bool canUndo() const { return !m_undoSteps.empty(); }

View File

@ -33,12 +33,12 @@
<file>resources/invert-selection.svg</file> <file>resources/invert-selection.svg</file>
<file>resources/regroup-alternating.svg</file> <file>resources/regroup-alternating.svg</file>
<file>resources/regroup-alternating-reversed.svg</file> <file>resources/regroup-alternating-reversed.svg</file>
<file>resources/regroup-bookmarks.svg</file>
<file>resources/regroup-even-odd.svg</file> <file>resources/regroup-even-odd.svg</file>
<file>resources/regroup-pairs.svg</file> <file>resources/regroup-pairs.svg</file>
<file>resources/undo.svg</file> <file>resources/undo.svg</file>
<file>resources/redo.svg</file> <file>resources/redo.svg</file>
<file>resources/bookmark.svg</file> <file>resources/bookmark.svg</file>
<file>resources/wallet.svg</file> <file>resources/wallet.svg</file>
<file>resources/regroup-outline.svg</file>
</qresource> </qresource>
</RCC> </RCC>

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -15,8 +15,8 @@
// You should have received a copy of the GNU Lesser General Public License // You should have received a copy of the GNU Lesser General Public License
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>. // along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
#include "selectbookmarkstoregroupdialog.h" #include "selectoutlinetoregroupdialog.h"
#include "ui_selectbookmarkstoregroupdialog.h" #include "ui_selectoutlinetoregroupdialog.h"
#include "pdfitemmodels.h" #include "pdfitemmodels.h"
#include "pdfwidgetutils.h" #include "pdfwidgetutils.h"
@ -26,9 +26,9 @@
namespace pdfdocpage namespace pdfdocpage
{ {
SelectBookmarksToRegroupDialog::SelectBookmarksToRegroupDialog(const pdf::PDFDocument* document, QWidget* parent) : SelectOutlineToRegroupDialog::SelectOutlineToRegroupDialog(const pdf::PDFDocument* document, QWidget* parent) :
QDialog(parent), QDialog(parent),
ui(new Ui::SelectBookmarksToRegroupDialog), ui(new Ui::SelectOutlineToRegroupDialog),
m_document(document), m_document(document),
m_model(nullptr) m_model(nullptr)
{ {
@ -36,53 +36,53 @@ SelectBookmarksToRegroupDialog::SelectBookmarksToRegroupDialog(const pdf::PDFDoc
QIcon bookmarkIcon(":/pdfdocpage/resources/bookmark.svg"); QIcon bookmarkIcon(":/pdfdocpage/resources/bookmark.svg");
m_model = new pdf::PDFSelectableOutlineTreeItemModel(qMove(bookmarkIcon), this); m_model = new pdf::PDFSelectableOutlineTreeItemModel(qMove(bookmarkIcon), this);
ui->bookmarksView->setModel(m_model); ui->outlineView->setModel(m_model);
ui->bookmarksView->header()->hide(); ui->outlineView->header()->hide();
m_model->setDocument(pdf::PDFModifiedDocument(const_cast<pdf::PDFDocument*>(document), nullptr)); m_model->setDocument(pdf::PDFModifiedDocument(const_cast<pdf::PDFDocument*>(document), nullptr));
ui->bookmarksView->expandToDepth(2); ui->outlineView->expandToDepth(2);
ui->bookmarksView->setContextMenuPolicy(Qt::CustomContextMenu); ui->outlineView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->bookmarksView, &QTreeView::customContextMenuRequested, this, &SelectBookmarksToRegroupDialog::onViewContextMenuRequested); connect(ui->outlineView, &QTreeView::customContextMenuRequested, this, &SelectOutlineToRegroupDialog::onViewContextMenuRequested);
QSize size = pdf::PDFWidgetUtils::scaleDPI(this, QSize(400, 600)); QSize size = pdf::PDFWidgetUtils::scaleDPI(this, QSize(400, 600));
setMinimumSize(size); setMinimumSize(size);
pdf::PDFWidgetUtils::style(this); pdf::PDFWidgetUtils::style(this);
} }
SelectBookmarksToRegroupDialog::~SelectBookmarksToRegroupDialog() SelectOutlineToRegroupDialog::~SelectOutlineToRegroupDialog()
{ {
delete ui; delete ui;
} }
std::vector<const pdf::PDFOutlineItem*> SelectBookmarksToRegroupDialog::getSelectedOutlineItems() const std::vector<const pdf::PDFOutlineItem*> SelectOutlineToRegroupDialog::getSelectedOutlineItems() const
{ {
return m_model->getSelectedItems(); return m_model->getSelectedItems();
} }
void SelectBookmarksToRegroupDialog::onViewContextMenuRequested(const QPoint& pos) void SelectOutlineToRegroupDialog::onViewContextMenuRequested(const QPoint& pos)
{ {
QMenu menu; QMenu menu;
menu.addAction(tr("Select All"), this, &SelectBookmarksToRegroupDialog::selectAll); menu.addAction(tr("Select All"), this, &SelectOutlineToRegroupDialog::selectAll);
menu.addAction(tr("Deselect All"), this, &SelectBookmarksToRegroupDialog::deselectAll); menu.addAction(tr("Deselect All"), this, &SelectOutlineToRegroupDialog::deselectAll);
menu.addAction(tr("Invert Selection"), this, &SelectBookmarksToRegroupDialog::invertSelection); menu.addAction(tr("Invert Selection"), this, &SelectOutlineToRegroupDialog::invertSelection);
menu.addSeparator(); menu.addSeparator();
menu.addAction(tr("Select Level 1"), this, &SelectBookmarksToRegroupDialog::selectLevel1); menu.addAction(tr("Select Level 1"), this, &SelectOutlineToRegroupDialog::selectLevel1);
menu.addAction(tr("Select Level 2"), this, &SelectBookmarksToRegroupDialog::selectLevel2); menu.addAction(tr("Select Level 2"), this, &SelectOutlineToRegroupDialog::selectLevel2);
QModelIndex index = ui->bookmarksView->indexAt(pos); QModelIndex index = ui->outlineView->indexAt(pos);
if (index.isValid()) if (index.isValid())
{ {
m_menuIndex = index; m_menuIndex = index;
menu.addSeparator(); menu.addSeparator();
menu.addAction(tr("Select subtree"), this, &SelectBookmarksToRegroupDialog::selectSubtree); menu.addAction(tr("Select subtree"), this, &SelectOutlineToRegroupDialog::selectSubtree);
menu.addAction(tr("Deselect subtree"), this, &SelectBookmarksToRegroupDialog::deselectSubtree); menu.addAction(tr("Deselect subtree"), this, &SelectOutlineToRegroupDialog::deselectSubtree);
} }
menu.exec(ui->bookmarksView->mapToGlobal(pos)); menu.exec(ui->outlineView->mapToGlobal(pos));
} }
void SelectBookmarksToRegroupDialog::manipulateTree(const QModelIndex& index, void SelectOutlineToRegroupDialog::manipulateTree(const QModelIndex& index,
const std::function<void (QModelIndex)>& manipulator) const std::function<void (QModelIndex)>& manipulator)
{ {
if (index.isValid()) if (index.isValid())
@ -98,7 +98,7 @@ void SelectBookmarksToRegroupDialog::manipulateTree(const QModelIndex& index,
} }
} }
std::function<void (QModelIndex)> SelectBookmarksToRegroupDialog::createCheckByDepthManipulator(int targetDepth) const std::function<void (QModelIndex)> SelectOutlineToRegroupDialog::createCheckByDepthManipulator(int targetDepth) const
{ {
auto manipulator = [this, targetDepth](QModelIndex index) auto manipulator = [this, targetDepth](QModelIndex index)
{ {
@ -119,42 +119,42 @@ std::function<void (QModelIndex)> SelectBookmarksToRegroupDialog::createCheckByD
return manipulator; return manipulator;
} }
void SelectBookmarksToRegroupDialog::selectAll() void SelectOutlineToRegroupDialog::selectAll()
{ {
manipulateTree(ui->bookmarksView->rootIndex(), [this](QModelIndex index) { m_model->setData(index, Qt::Checked, Qt::CheckStateRole); }); manipulateTree(ui->outlineView->rootIndex(), [this](QModelIndex index) { m_model->setData(index, Qt::Checked, Qt::CheckStateRole); });
} }
void SelectBookmarksToRegroupDialog::deselectAll() void SelectOutlineToRegroupDialog::deselectAll()
{ {
manipulateTree(ui->bookmarksView->rootIndex(), [this](QModelIndex index) { m_model->setData(index, Qt::Unchecked, Qt::CheckStateRole); }); manipulateTree(ui->outlineView->rootIndex(), [this](QModelIndex index) { m_model->setData(index, Qt::Unchecked, Qt::CheckStateRole); });
} }
void SelectBookmarksToRegroupDialog::invertSelection() void SelectOutlineToRegroupDialog::invertSelection()
{ {
auto manipulator = [this](QModelIndex index) auto manipulator = [this](QModelIndex index)
{ {
const bool isChecked = index.data(Qt::CheckStateRole).toInt() == Qt::Checked; const bool isChecked = index.data(Qt::CheckStateRole).toInt() == Qt::Checked;
m_model->setData(index, isChecked ? Qt::Unchecked : Qt::Checked, Qt::CheckStateRole); m_model->setData(index, isChecked ? Qt::Unchecked : Qt::Checked, Qt::CheckStateRole);
}; };
manipulateTree(ui->bookmarksView->rootIndex(), manipulator); manipulateTree(ui->outlineView->rootIndex(), manipulator);
} }
void SelectBookmarksToRegroupDialog::selectLevel1() void SelectOutlineToRegroupDialog::selectLevel1()
{ {
manipulateTree(ui->bookmarksView->rootIndex(), createCheckByDepthManipulator(1)); manipulateTree(ui->outlineView->rootIndex(), createCheckByDepthManipulator(1));
} }
void SelectBookmarksToRegroupDialog::selectLevel2() void SelectOutlineToRegroupDialog::selectLevel2()
{ {
manipulateTree(ui->bookmarksView->rootIndex(), createCheckByDepthManipulator(2)); manipulateTree(ui->outlineView->rootIndex(), createCheckByDepthManipulator(2));
} }
void SelectBookmarksToRegroupDialog::selectSubtree() void SelectOutlineToRegroupDialog::selectSubtree()
{ {
manipulateTree(m_menuIndex, [this](QModelIndex index) { m_model->setData(index, Qt::Checked, Qt::CheckStateRole); }); manipulateTree(m_menuIndex, [this](QModelIndex index) { m_model->setData(index, Qt::Checked, Qt::CheckStateRole); });
} }
void SelectBookmarksToRegroupDialog::deselectSubtree() void SelectOutlineToRegroupDialog::deselectSubtree()
{ {
manipulateTree(m_menuIndex, [this](QModelIndex index) { m_model->setData(index, Qt::Unchecked, Qt::CheckStateRole); }); manipulateTree(m_menuIndex, [this](QModelIndex index) { m_model->setData(index, Qt::Unchecked, Qt::CheckStateRole); });
} }

View File

@ -15,8 +15,8 @@
// You should have received a copy of the GNU Lesser General Public License // You should have received a copy of the GNU Lesser General Public License
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>. // along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
#ifndef PDFDOCPAGEORGANIZER_SELECTBOOKMARKSTOREGROUPDIALOG_H #ifndef PDFDOCPAGEORGANIZER_SELECTOUTLINETOREGROUPDIALOG_H
#define PDFDOCPAGEORGANIZER_SELECTBOOKMARKSTOREGROUPDIALOG_H #define PDFDOCPAGEORGANIZER_SELECTOUTLINETOREGROUPDIALOG_H
#include "pdfdocument.h" #include "pdfdocument.h"
@ -24,7 +24,7 @@
namespace Ui namespace Ui
{ {
class SelectBookmarksToRegroupDialog; class SelectOutlineToRegroupDialog;
} }
namespace pdf namespace pdf
@ -35,13 +35,13 @@ class PDFSelectableOutlineTreeItemModel;
namespace pdfdocpage namespace pdfdocpage
{ {
class SelectBookmarksToRegroupDialog : public QDialog class SelectOutlineToRegroupDialog : public QDialog
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit SelectBookmarksToRegroupDialog(const pdf::PDFDocument* document, QWidget* parent); explicit SelectOutlineToRegroupDialog(const pdf::PDFDocument* document, QWidget* parent);
virtual ~SelectBookmarksToRegroupDialog() override; virtual ~SelectOutlineToRegroupDialog() override;
std::vector<const pdf::PDFOutlineItem*> getSelectedOutlineItems() const; std::vector<const pdf::PDFOutlineItem*> getSelectedOutlineItems() const;
@ -62,7 +62,7 @@ private:
std::function<void (QModelIndex)> createCheckByDepthManipulator(int targetDepth) const; std::function<void (QModelIndex)> createCheckByDepthManipulator(int targetDepth) const;
Ui::SelectBookmarksToRegroupDialog* ui; Ui::SelectOutlineToRegroupDialog* ui;
const pdf::PDFDocument* m_document; const pdf::PDFDocument* m_document;
pdf::PDFSelectableOutlineTreeItemModel* m_model; pdf::PDFSelectableOutlineTreeItemModel* m_model;
QModelIndex m_menuIndex; QModelIndex m_menuIndex;
@ -70,4 +70,4 @@ private:
} // namespace pdfdocpage } // namespace pdfdocpage
#endif // PDFDOCPAGEORGANIZER_SELECTBOOKMARKSTOREGROUPDIALOG_H #endif // PDFDOCPAGEORGANIZER_SELECTOUTLINETOREGROUPDIALOG_H

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0"> <ui version="4.0">
<class>SelectBookmarksToRegroupDialog</class> <class>SelectOutlineToRegroupDialog</class>
<widget class="QDialog" name="SelectBookmarksToRegroupDialog"> <widget class="QDialog" name="SelectOutlineToRegroupDialog">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>0</x> <x>0</x>
@ -11,11 +11,11 @@
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>Select Bookmarks</string> <string>Select Outline</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<item> <item>
<widget class="QTreeView" name="bookmarksView"/> <widget class="QTreeView" name="outlineView"/>
</item> </item>
<item> <item>
<widget class="QDialogButtonBox" name="buttonBox"> <widget class="QDialogButtonBox" name="buttonBox">
@ -34,7 +34,7 @@
<connection> <connection>
<sender>buttonBox</sender> <sender>buttonBox</sender>
<signal>accepted()</signal> <signal>accepted()</signal>
<receiver>SelectBookmarksToRegroupDialog</receiver> <receiver>SelectOutlineToRegroupDialog</receiver>
<slot>accept()</slot> <slot>accept()</slot>
<hints> <hints>
<hint type="sourcelabel"> <hint type="sourcelabel">
@ -50,7 +50,7 @@
<connection> <connection>
<sender>buttonBox</sender> <sender>buttonBox</sender>
<signal>rejected()</signal> <signal>rejected()</signal>
<receiver>SelectBookmarksToRegroupDialog</receiver> <receiver>SelectOutlineToRegroupDialog</receiver>
<slot>reject()</slot> <slot>reject()</slot>
<hints> <hints>
<hint type="sourcelabel"> <hint type="sourcelabel">

View File

@ -86,9 +86,9 @@ void PDFDocumentSanitizer::sanitize()
performSanitizeMetadata(); performSanitizeMetadata();
} }
if (m_flags.testFlag(Bookmarks)) if (m_flags.testFlag(Outline))
{ {
performSanitizeBookmarks(); performSanitizeOutline();
} }
if (m_flags.testFlag(FileAttachments)) if (m_flags.testFlag(FileAttachments))
@ -163,7 +163,7 @@ void PDFDocumentSanitizer::performSanitizeMetadata()
Q_EMIT sanitizationProgress(tr("Metadata streams removed: %1").arg(counter)); Q_EMIT sanitizationProgress(tr("Metadata streams removed: %1").arg(counter));
} }
void PDFDocumentSanitizer::performSanitizeBookmarks() void PDFDocumentSanitizer::performSanitizeOutline()
{ {
PDFDocumentBuilder builder(m_storage, PDFVersion(2, 0)); PDFDocumentBuilder builder(m_storage, PDFVersion(2, 0));
PDFObject catalogObject = builder.getObjectByReference(builder.getCatalogReference()); PDFObject catalogObject = builder.getObjectByReference(builder.getCatalogReference());

View File

@ -38,7 +38,7 @@ public:
None = 0x0000, ///< No sanitization is performed None = 0x0000, ///< No sanitization is performed
DocumentInfo = 0x0001, ///< Remove document information DocumentInfo = 0x0001, ///< Remove document information
Metadata = 0x0002, ///< Remove all metadata streams in all objects Metadata = 0x0002, ///< Remove all metadata streams in all objects
Bookmarks = 0x0004, ///< Remove bookmarks Outline = 0x0004, ///< Remove outline
FileAttachments = 0x0008, ///< Remove file attachments FileAttachments = 0x0008, ///< Remove file attachments
EmbeddedSearchIndex = 0x0010, ///< Remove embedded search index EmbeddedSearchIndex = 0x0010, ///< Remove embedded search index
MarkupAnnotations = 0x0020, ///< Remove markup annotations from all pages MarkupAnnotations = 0x0020, ///< Remove markup annotations from all pages
@ -82,7 +82,7 @@ signals:
private: private:
void performSanitizeDocumentInfo(); void performSanitizeDocumentInfo();
void performSanitizeMetadata(); void performSanitizeMetadata();
void performSanitizeBookmarks(); void performSanitizeOutline();
void performSanitizeFileAttachments(); void performSanitizeFileAttachments();
void performSanitizeEmbeddedSearchIndex(); void performSanitizeEmbeddedSearchIndex();
void performSanitizeMarkupAnnotations(); void performSanitizeMarkupAnnotations();

View File

@ -69,6 +69,10 @@ add_library(Pdf4QtViewer SHARED
pdfcreatebitonaldocumentdialog.cpp pdfcreatebitonaldocumentdialog.cpp
pdfcreatebitonaldocumentdialog.h pdfcreatebitonaldocumentdialog.h
pdf4qtviewer.qrc pdf4qtviewer.qrc
pdfbookmarkmanager.h
pdfbookmarkmanager.cpp
pdfbookmarkui.h
pdfbookmarkui.cpp
) )
add_compile_definitions(QT_INSTALL_DIRECTORY="${QT6_INSTALL_PREFIX}") add_compile_definitions(QT_INSTALL_DIRECTORY="${QT6_INSTALL_PREFIX}")

View File

@ -15,7 +15,6 @@
<file>resources/settings.svg</file> <file>resources/settings.svg</file>
<file>resources/zoom-in.svg</file> <file>resources/zoom-in.svg</file>
<file>resources/zoom-out.svg</file> <file>resources/zoom-out.svg</file>
<file>resources/bookmark.svg</file>
<file>resources/security.svg</file> <file>resources/security.svg</file>
<file>resources/zoom-fit.svg</file> <file>resources/zoom-fit.svg</file>
<file>resources/zoom-fit-horizontal.svg</file> <file>resources/zoom-fit-horizontal.svg</file>
@ -104,5 +103,10 @@
<file>resources/sidebar-speech.svg</file> <file>resources/sidebar-speech.svg</file>
<file>resources/sidebar-thumbnails.svg</file> <file>resources/sidebar-thumbnails.svg</file>
<file>resources/sidebar-visibility.svg</file> <file>resources/sidebar-visibility.svg</file>
<file>resources/outline.svg</file>
<file>resources/sidebar-favourites.svg</file>
<file>resources/bookmark.svg</file>
<file>resources/bookmark-next.svg</file>
<file>resources/bookmark-previous.svg</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@ -0,0 +1,353 @@
// 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/>.
#include "pdfbookmarkmanager.h"
#include "pdfaction.h"
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonDocument>
namespace pdfviewer
{
class PDFBookmarkManagerHelper
{
public:
constexpr PDFBookmarkManagerHelper() = delete;
static QJsonObject convertBookmarkToJson(const PDFBookmarkManager::Bookmark& bookmark)
{
QJsonObject json;
json["isAuto"] = bookmark.isAuto;
json["name"] = bookmark.name;
json["pageIndex"] = static_cast<qint64>(bookmark.pageIndex);
return json;
}
static PDFBookmarkManager::Bookmark convertJsonToBookmark(const QJsonObject& json)
{
PDFBookmarkManager::Bookmark bookmark;
bookmark.isAuto = json["isAuto"].toBool();
bookmark.name = json["name"].toString();
bookmark.pageIndex = json["pageIndex"].toInteger();
return bookmark;
}
static QJsonObject convertBookmarksToJson(const PDFBookmarkManager::Bookmarks& bookmarks)
{
QJsonArray jsonArray;
for (const auto& bookmark : bookmarks.bookmarks)
{
jsonArray.append(convertBookmarkToJson(bookmark));
}
QJsonObject jsonObject;
jsonObject["bookmarks"] = jsonArray;
return jsonObject;
}
static PDFBookmarkManager::Bookmarks convertBookmarksFromJson(const QJsonObject& object)
{
PDFBookmarkManager::Bookmarks bookmarks;
QJsonArray jsonArray = object["bookmarks"].toArray();
for (const auto& jsonValue : jsonArray)
{
bookmarks.bookmarks.push_back(convertJsonToBookmark(jsonValue.toObject()));
}
return bookmarks;
}
static QJsonDocument convertBookmarksMapToJsonDocument(const std::map<QString, PDFBookmarkManager::Bookmarks>& bookmarksMap)
{
QJsonObject mainObject;
for (const auto& pair : bookmarksMap)
{
mainObject[pair.first] = convertBookmarksToJson(pair.second);
}
return QJsonDocument(mainObject);
}
static std::map<QString, PDFBookmarkManager::Bookmarks> convertBookmarksMapFromJsonDocument(const QJsonDocument &doc)
{
std::map<QString, PDFBookmarkManager::Bookmarks> container;
QJsonObject mainObject = doc.object();
for (auto it = mainObject.begin(); it != mainObject.end(); ++it)
{
container[it.key()] = convertBookmarksFromJson(it.value().toObject());
}
return container;
}
};
PDFBookmarkManager::PDFBookmarkManager(QObject* parent) :
BaseClass(parent)
{
}
void PDFBookmarkManager::setDocument(const pdf::PDFModifiedDocument& document)
{
Q_EMIT bookmarksAboutToBeChanged();
m_document = document.getDocument();
if (document.hasReset())
{
if (!document.hasPreserveView())
{
m_bookmarks.bookmarks.clear();
regenerateAutoBookmarks();
}
}
Q_EMIT bookmarksChanged();
}
void PDFBookmarkManager::saveToFile(QString fileName)
{
QJsonDocument doc(PDFBookmarkManagerHelper::convertBookmarksToJson(m_bookmarks));
// Příklad zápisu do souboru
QFile file(fileName);
if (file.open(QIODevice::WriteOnly))
{
file.write(doc.toJson());
file.close();
}
}
bool PDFBookmarkManager::loadFromFile(QString fileName)
{
QFile file(fileName);
if (file.open(QIODevice::ReadOnly))
{
QJsonDocument loadedDoc = QJsonDocument::fromJson(file.readAll());
file.close();
Q_EMIT bookmarksAboutToBeChanged();
m_bookmarks = PDFBookmarkManagerHelper::convertBookmarksFromJson(loadedDoc.object());
Q_EMIT bookmarksChanged();
return true;
}
return false;
}
bool PDFBookmarkManager::isEmpty() const
{
return m_bookmarks.bookmarks.empty();
}
int PDFBookmarkManager::getBookmarkCount() const
{
return static_cast<int>(m_bookmarks.bookmarks.size());
}
PDFBookmarkManager::Bookmark PDFBookmarkManager::getBookmark(int index) const
{
return m_bookmarks.bookmarks.at(index);
}
void PDFBookmarkManager::toggleBookmark(pdf::PDFInteger pageIndex)
{
Q_EMIT bookmarksAboutToBeChanged();
auto it = std::find_if(m_bookmarks.bookmarks.begin(), m_bookmarks.bookmarks.end(), [pageIndex](const auto& bookmark) { return bookmark.pageIndex == pageIndex; });
if (it != m_bookmarks.bookmarks.cend())
{
Bookmark& bookmark = *it;
if (bookmark.isAuto)
{
bookmark.isAuto = false;
}
else
{
m_bookmarks.bookmarks.erase(it);
}
}
else
{
Bookmark bookmark;
bookmark.isAuto = false;
bookmark.name = tr("User bookmark for page %1").arg(pageIndex + 1);
bookmark.pageIndex = pageIndex;
m_bookmarks.bookmarks.push_back(bookmark);
sortBookmarks();
}
Q_EMIT bookmarksChanged();
}
void PDFBookmarkManager::setGenerateBookmarksAutomatically(bool generateBookmarksAutomatically)
{
if (m_generateBookmarksAutomatically != generateBookmarksAutomatically)
{
Q_EMIT bookmarksAboutToBeChanged();
m_generateBookmarksAutomatically = generateBookmarksAutomatically;
regenerateAutoBookmarks();
Q_EMIT bookmarksChanged();
}
}
void PDFBookmarkManager::goToNextBookmark()
{
if (isEmpty())
{
return;
}
m_currentBookmark = (m_currentBookmark + 1) % getBookmarkCount();
goToCurrentBookmark();
}
void PDFBookmarkManager::goToPreviousBookmark()
{
if (isEmpty())
{
return;
}
if (m_currentBookmark <= 0)
{
m_currentBookmark = getBookmarkCount() - 1;
}
else
{
--m_currentBookmark;
}
goToCurrentBookmark();
}
void PDFBookmarkManager::goToCurrentBookmark()
{
if (isEmpty())
{
return;
}
if (m_currentBookmark >= 0 && m_currentBookmark < getBookmarkCount())
{
Q_EMIT bookmarkActivated(m_currentBookmark, m_bookmarks.bookmarks.at(m_currentBookmark));
}
}
void PDFBookmarkManager::goToBookmark(int index, bool force)
{
if (m_currentBookmark != index || force)
{
m_currentBookmark = index;
goToCurrentBookmark();
}
}
void PDFBookmarkManager::sortBookmarks()
{
auto predicate = [](const auto& l, const auto& r)
{
return l.pageIndex < r.pageIndex;
};
std::sort(m_bookmarks.bookmarks.begin(), m_bookmarks.bookmarks.end(), predicate);
}
void PDFBookmarkManager::regenerateAutoBookmarks()
{
if (!m_document)
{
return;
}
// Create bookmarks for all main chapters
Bookmarks& bookmarks = m_bookmarks;
for (auto it = bookmarks.bookmarks.begin(); it != bookmarks.bookmarks.end();)
{
if (it->isAuto)
{
it = bookmarks.bookmarks.erase(it);
}
else
{
++it;
}
}
if (!m_generateBookmarksAutomatically)
{
return;
}
if (auto outlineRoot = m_document->getCatalog()->getOutlineRootPtr())
{
size_t childCount = outlineRoot->getChildCount();
for (size_t i = 0; i < childCount; ++i)
{
Bookmark bookmark;
bookmark.isAuto = true;
bookmark.pageIndex = pdf::PDFCatalog::INVALID_PAGE_INDEX;
const pdf::PDFOutlineItem* child = outlineRoot->getChild(i);
const pdf::PDFAction* action = child->getAction();
if (action)
{
for (const pdf::PDFAction* currentAction : action->getActionList())
{
if (currentAction->getType() != pdf::ActionType::GoTo)
{
continue;
}
const pdf::PDFActionGoTo* typedAction = dynamic_cast<const pdf::PDFActionGoTo*>(currentAction);
pdf::PDFDestination destination = typedAction->getDestination();
if (destination.getDestinationType() == pdf::DestinationType::Named)
{
if (const pdf::PDFDestination* targetDestination = m_document->getCatalog()->getNamedDestination(destination.getName()))
{
destination = *targetDestination;
}
}
if (destination.getDestinationType() != pdf::DestinationType::Invalid &&
destination.getPageReference() != pdf::PDFObjectReference())
{
const size_t pageIndex = m_document->getCatalog()->getPageIndexFromPageReference(destination.getPageReference());
if (pageIndex != pdf::PDFCatalog::INVALID_PAGE_INDEX)
{
bookmark.pageIndex = pageIndex;
bookmark.name = child->getTitle();
}
}
}
}
if (bookmark.pageIndex != pdf::PDFCatalog::INVALID_PAGE_INDEX)
{
bookmarks.bookmarks.emplace_back(std::move(bookmark));
}
}
}
}
} // namespace pdf

View File

@ -0,0 +1,85 @@
// 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/>.
#ifndef PDFBOOKMARKMANAGER_H
#define PDFBOOKMARKMANAGER_H
#include "pdfdocument.h"
#include <QObject>
namespace pdfviewer
{
class PDFBookmarkManager : public QObject
{
Q_OBJECT
private:
using BaseClass = QObject;
public:
PDFBookmarkManager(QObject* parent);
void setDocument(const pdf::PDFModifiedDocument& document);
void saveToFile(QString fileName);
bool loadFromFile(QString fileName);
struct Bookmark
{
bool isAuto = false;
QString name;
pdf::PDFInteger pageIndex = -1;
};
bool isEmpty() const;
int getBookmarkCount() const;
Bookmark getBookmark(int index) const;
void toggleBookmark(pdf::PDFInteger pageIndex);
void setGenerateBookmarksAutomatically(bool generateBookmarksAutomatically);
void goToNextBookmark();
void goToPreviousBookmark();
void goToCurrentBookmark();
void goToBookmark(int index, bool force);
signals:
void bookmarksAboutToBeChanged();
void bookmarksChanged();
void bookmarkActivated(int index, Bookmark bookmark);
private:
friend class PDFBookmarkManagerHelper;
void sortBookmarks();
void regenerateAutoBookmarks();
struct Bookmarks
{
std::vector<Bookmark> bookmarks;
};
pdf::PDFDocument* m_document;
Bookmarks m_bookmarks;
int m_currentBookmark = -1;
bool m_generateBookmarksAutomatically = true;
};
} // namespace pdf
#endif // PDFBOOKMARKMANAGER_H

View File

@ -0,0 +1,198 @@
// 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/>.
#include "pdfbookmarkui.h"
#include "pdfwidgetutils.h"
#include "pdfpainterutils.h"
#include <QPainter>
#include <QPainterPath>
namespace pdfviewer
{
PDFBookmarkItemModel::PDFBookmarkItemModel(PDFBookmarkManager* bookmarkManager, QObject* parent) :
BaseClass(parent),
m_bookmarkManager(bookmarkManager)
{
connect(m_bookmarkManager, &PDFBookmarkManager::bookmarksAboutToBeChanged, this, &PDFBookmarkItemModel::beginResetModel);
connect(m_bookmarkManager, &PDFBookmarkManager::bookmarksChanged, this, &PDFBookmarkItemModel::endResetModel);
}
QModelIndex PDFBookmarkItemModel::index(int row, int column, const QModelIndex& parent) const
{
if (parent.isValid())
{
return QModelIndex();
}
return createIndex(row, column, nullptr);
}
QModelIndex PDFBookmarkItemModel::parent(const QModelIndex& child) const
{
Q_UNUSED(child);
return QModelIndex();
}
int PDFBookmarkItemModel::rowCount(const QModelIndex& parent) const
{
if (parent.isValid())
{
return 0;
}
return m_bookmarkManager ? m_bookmarkManager->getBookmarkCount() : 0;
}
int PDFBookmarkItemModel::columnCount(const QModelIndex& parent) const
{
if (parent.isValid())
{
return 0;
}
return 1;
}
QVariant PDFBookmarkItemModel::data(const QModelIndex& index, int role) const
{
if (role == Qt::DisplayRole)
{
return m_bookmarkManager->getBookmark(index.row()).name;
}
return QVariant();
}
PDFBookmarkItemDelegate::PDFBookmarkItemDelegate(PDFBookmarkManager* bookmarkManager, QObject* parent) :
BaseClass(parent),
m_bookmarkManager(bookmarkManager)
{
}
void PDFBookmarkItemDelegate::paint(QPainter* painter,
const QStyleOptionViewItem& option,
const QModelIndex& index) const
{
QStyleOptionViewItem options = option;
initStyleOption(&options, index);
PDFBookmarkManager::Bookmark bookmark = m_bookmarkManager->getBookmark(index.row());
options.text = QString();
options.widget->style()->drawControl(QStyle::CE_ItemViewItem, &options, painter);
const int margin = pdf::PDFWidgetUtils::scaleDPI_x(option.widget, MARGIN);
const int iconSize = pdf::PDFWidgetUtils::scaleDPI_x(option.widget, ICON_SIZE);
QRect rect = options.rect;
rect.marginsRemoved(QMargins(margin, margin, margin, margin));
QColor color = bookmark.isAuto ? QColor(0, 123, 255) : QColor(255, 159, 0);
if (options.state.testFlag(QStyle::State_Selected))
{
color = Qt::yellow;
}
QRect iconRect = rect;
iconRect.setWidth(iconSize);
iconRect.setHeight(iconSize);
iconRect.moveCenter(QPoint(rect.left() + iconSize / 2, rect.center().y()));
drawStar(*painter, iconRect.center(), iconRect.width() * 0.5, color);
QRect textRect = rect;
textRect.setLeft(iconRect.right() + margin);
textRect.moveTop(rect.top() + (rect.height() - 2 * options.fontMetrics.lineSpacing()) / 2);
textRect.setHeight(options.fontMetrics.lineSpacing());
QFont font = options.font;
font.setBold(true);
painter->setFont(font);
painter->drawText(textRect, getPageText(bookmark));
textRect.translate(0, textRect.height());
painter->setFont(options.font);
painter->drawText(textRect, bookmark.name);
}
QSize PDFBookmarkItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
{
PDFBookmarkManager::Bookmark bookmark = m_bookmarkManager->getBookmark(index.row());
const int textWidthLine1 = option.fontMetrics.horizontalAdvance(getPageText(bookmark));
const int textWidthLine2 = option.fontMetrics.horizontalAdvance(option.text);
const int textWidth = qMax(textWidthLine1, textWidthLine2);
const int textHeight = option.fontMetrics.lineSpacing() * 2;
const int margin = pdf::PDFWidgetUtils::scaleDPI_x(option.widget, MARGIN);
const int iconSize = pdf::PDFWidgetUtils::scaleDPI_x(option.widget, ICON_SIZE);
const int requiredWidth = 3 * margin + iconSize + textWidth;
const int requiredHeight = 2 * margin + qMax(iconSize, textHeight);
return QSize(requiredWidth, requiredHeight);
}
void PDFBookmarkItemDelegate::drawStar(QPainter& painter, const QPointF& center, double size, const QColor& color) const
{
pdf::PDFPainterStateGuard guard(&painter);
painter.setPen(Qt::NoPen);
painter.setBrush(color);
QPainterPath path;
double angle = M_PI / 5;
double phase = -M_PI / 10;
for (int i = 0; i < 10; ++i)
{
double radius = (i % 2 == 0) ? size : size / 2.5;
QPointF point(radius * cos(i * angle + phase), radius * sin(i * angle + phase));
point += center;
if (i == 0)
{
path.moveTo(point);
}
else
{
path.lineTo(point);
}
}
path.closeSubpath();
painter.drawPath(path);
}
QString PDFBookmarkItemDelegate::getPageText(const PDFBookmarkManager::Bookmark& bookmark) const
{
if (bookmark.isAuto)
{
return tr("Page %1 | Generated").arg(bookmark.pageIndex + 1);
}
else
{
return tr("Page %1").arg(bookmark.pageIndex + 1);
}
}
} // namespace pdfviewer

View File

@ -0,0 +1,76 @@
// 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/>.
#ifndef PDFBOOKMARKUI_H
#define PDFBOOKMARKUI_H
#include "pdfviewerglobal.h"
#include "pdfbookmarkmanager.h"
#include <QStyledItemDelegate>
#include <QAbstractItemModel>
namespace pdfviewer
{
class PDFBookmarkItemModel : public QAbstractItemModel
{
Q_OBJECT
private:
using BaseClass = QAbstractItemModel;
public:
PDFBookmarkItemModel(PDFBookmarkManager* bookmarkManager, QObject* parent);
virtual QModelIndex index(int row, int column, const QModelIndex& parent) const override;
virtual QModelIndex parent(const QModelIndex& child) const override;
virtual int rowCount(const QModelIndex& parent) const override;
virtual int columnCount(const QModelIndex& parent) const override;
virtual QVariant data(const QModelIndex& index, int role) const override;
private:
PDFBookmarkManager* m_bookmarkManager = nullptr;
};
class PDFBookmarkItemDelegate : public QStyledItemDelegate
{
Q_OBJECT
private:
using BaseClass = QStyledItemDelegate;
public:
PDFBookmarkItemDelegate(PDFBookmarkManager* bookmarkManager, QObject* parent);
virtual void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
virtual QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override;
private:
static constexpr int MARGIN = 6;
static constexpr int ICON_SIZE = 32;
void drawStar(QPainter& painter, const QPointF& center, double size, const QColor& color) const;
QString getPageText(const PDFBookmarkManager::Bookmark& bookmark) const;
PDFBookmarkManager* m_bookmarkManager = nullptr;
};
} // namespace pdfviewer
#endif // PDFBOOKMARKUI_H

View File

@ -210,6 +210,9 @@ void PDFActionManager::initActions(QSize iconSize, bool initializeStampActions)
setShortcut(GoToPreviousPage, QKeySequence::MoveToPreviousPage); setShortcut(GoToPreviousPage, QKeySequence::MoveToPreviousPage);
setShortcut(GoToNextLine, QKeySequence::MoveToNextLine); setShortcut(GoToNextLine, QKeySequence::MoveToNextLine);
setShortcut(GoToPreviousLine, QKeySequence::MoveToPreviousLine); setShortcut(GoToPreviousLine, QKeySequence::MoveToPreviousLine);
setShortcut(BookmarkPage, QKeySequence("Ctrl+M"));
setShortcut(BookmarkGoToNext, QKeySequence("Ctrl+."));
setShortcut(BookmarkGoToPrevious, QKeySequence("Ctrl+,"));
if (hasActions({ CreateStickyNoteComment, CreateStickyNoteHelp, CreateStickyNoteInsert, CreateStickyNoteKey, CreateStickyNoteNewParagraph, CreateStickyNoteNote, CreateStickyNoteParagraph })) if (hasActions({ CreateStickyNoteComment, CreateStickyNoteHelp, CreateStickyNoteInsert, CreateStickyNoteKey, CreateStickyNoteNewParagraph, CreateStickyNoteNote, CreateStickyNoteParagraph }))
{ {
@ -361,6 +364,7 @@ PDFProgramController::PDFProgramController(QObject* parent) :
m_toolManager(nullptr), m_toolManager(nullptr),
m_annotationManager(nullptr), m_annotationManager(nullptr),
m_formManager(nullptr), m_formManager(nullptr),
m_bookmarkManager(nullptr),
m_isBusy(false), m_isBusy(false),
m_isFactorySettingsBeingRestored(false), m_isFactorySettingsBeingRestored(false),
m_progress(nullptr) m_progress(nullptr)
@ -375,6 +379,9 @@ PDFProgramController::~PDFProgramController()
delete m_annotationManager; delete m_annotationManager;
m_annotationManager = nullptr; m_annotationManager = nullptr;
delete m_bookmarkManager;
m_bookmarkManager = nullptr;
} }
void PDFProgramController::initializeAnnotationManager() void PDFProgramController::initializeAnnotationManager()
@ -396,6 +403,13 @@ void PDFProgramController::initializeFormManager()
connect(m_formManager, &pdf::PDFFormManager::documentModified, this, &PDFProgramController::onDocumentModified); connect(m_formManager, &pdf::PDFFormManager::documentModified, this, &PDFProgramController::onDocumentModified);
} }
void PDFProgramController::initializeBookmarkManager()
{
m_bookmarkManager = new PDFBookmarkManager(this);
connect(m_bookmarkManager, &PDFBookmarkManager::bookmarkActivated, this, &PDFProgramController::onBookmarkActivated);
updateBookmarkSettings();
}
void PDFProgramController::initialize(Features features, void PDFProgramController::initialize(Features features,
QMainWindow* mainWindow, QMainWindow* mainWindow,
IMainWindow* mainWindowInterface, IMainWindow* mainWindowInterface,
@ -572,6 +586,30 @@ void PDFProgramController::initialize(Features features,
{ {
connect(action, &QAction::triggered, this, &PDFProgramController::onActionAutomaticDocumentRefresh); connect(action, &QAction::triggered, this, &PDFProgramController::onActionAutomaticDocumentRefresh);
} }
if (QAction* action = m_actionManager->getAction(PDFActionManager::BookmarkPage))
{
connect(action, &QAction::triggered, this, &PDFProgramController::onActionBookmarkPage);
}
if (QAction* action = m_actionManager->getAction(PDFActionManager::BookmarkGoToNext))
{
connect(action, &QAction::triggered, this, &PDFProgramController::onActionBookmarkGoToNext);
}
if (QAction* action = m_actionManager->getAction(PDFActionManager::BookmarkGoToPrevious))
{
connect(action, &QAction::triggered, this, &PDFProgramController::onActionBookmarkGoToPrevious);
}
if (QAction* action = m_actionManager->getAction(PDFActionManager::BookmarkExport))
{
connect(action, &QAction::triggered, this, &PDFProgramController::onActionBookmarkExport);
}
if (QAction* action = m_actionManager->getAction(PDFActionManager::BookmarkImport))
{
connect(action, &QAction::triggered, this, &PDFProgramController::onActionBookmarkImport);
}
if (QAction* action = m_actionManager->getAction(PDFActionManager::BookmarkGenerateAutomatically))
{
connect(action, &QAction::triggered, this, &PDFProgramController::onActionBookmarkGenerateAutomatically);
}
if (m_recentFileManager) if (m_recentFileManager)
{ {
@ -605,6 +643,7 @@ void PDFProgramController::initialize(Features features,
} }
initializeAnnotationManager(); initializeAnnotationManager();
initializeBookmarkManager();
if (features.testFlag(Forms)) if (features.testFlag(Forms))
{ {
@ -1549,6 +1588,8 @@ void PDFProgramController::readSettings(Settings settingsFlags)
{ {
m_formManager->setAppearanceFlags(m_settings->getSettings().m_formAppearanceFlags); m_formManager->setAppearanceFlags(m_settings->getSettings().m_formAppearanceFlags);
} }
updateBookmarkSettings();
} }
if (settingsFlags.testFlag(PluginsSettings)) if (settingsFlags.testFlag(PluginsSettings))
@ -1702,6 +1743,13 @@ void PDFProgramController::onFileChanged(const QString& fileName)
} }
} }
void PDFProgramController::onBookmarkActivated(int index, PDFBookmarkManager::Bookmark bookmark)
{
Q_UNUSED(index);
m_pdfWidget->getDrawWidgetProxy()->goToPage(bookmark.pageIndex);
}
void PDFProgramController::updateFileInfo(const QString& fileName) void PDFProgramController::updateFileInfo(const QString& fileName)
{ {
QFileInfo fileInfo(fileName); QFileInfo fileInfo(fileName);
@ -1915,6 +1963,11 @@ void PDFProgramController::setDocument(pdf::PDFModifiedDocument document, bool i
m_annotationManager->setDocument(document); m_annotationManager->setDocument(document);
} }
if (m_bookmarkManager)
{
m_bookmarkManager->setDocument(document);
}
if (m_formManager) if (m_formManager)
{ {
m_formManager->setDocument(document); m_formManager->setDocument(document);
@ -1981,6 +2034,22 @@ void PDFProgramController::updateRenderingOptionActions()
} }
} }
void PDFProgramController::updateBookmarkSettings()
{
const bool enable = m_settings->getSettings().m_autoGenerateBookmarks;
if (m_bookmarkManager)
{
m_bookmarkManager->setGenerateBookmarksAutomatically(enable);
}
QAction* action = m_actionManager->getAction(PDFActionManager::BookmarkGenerateAutomatically);
if (action)
{
action->setChecked(enable);
}
}
void PDFProgramController::updateTitle() void PDFProgramController::updateTitle()
{ {
if (m_pdfDocument) if (m_pdfDocument)
@ -2535,6 +2604,63 @@ void PDFProgramController::onActionAutomaticDocumentRefresh()
updateFileWatcher(); updateFileWatcher();
} }
void PDFProgramController::onActionBookmarkPage()
{
std::vector<pdf::PDFInteger> currentPages = m_pdfWidget->getDrawWidget()->getCurrentPages();
if (!currentPages.empty())
{
m_bookmarkManager->toggleBookmark(currentPages.front());
}
}
void PDFProgramController::onActionBookmarkGoToNext()
{
m_bookmarkManager->goToNextBookmark();
}
void PDFProgramController::onActionBookmarkGoToPrevious()
{
m_bookmarkManager->goToPreviousBookmark();
}
void PDFProgramController::onActionBookmarkExport()
{
if (!m_pdfDocument)
{
return;
}
QFileInfo fileInfo(m_fileInfo.originalFileName);
QString saveFileName = QFileDialog::getSaveFileName(m_mainWindow, tr("Export Bookmarks As"), fileInfo.dir().absoluteFilePath(m_fileInfo.originalFileName).replace(".pdf", ".json"), tr("JSON (*.json);;All files (*.*)"));
if (!saveFileName.isEmpty())
{
m_bookmarkManager->saveToFile(saveFileName);
}
}
void PDFProgramController::onActionBookmarkImport()
{
if (!m_pdfDocument)
{
return;
}
QFileInfo fileInfo(m_fileInfo.originalFileName);
QString fileName = QFileDialog::getOpenFileName(m_mainWindow, tr("Select PDF document"), fileInfo.dir().absolutePath(), tr("JSON (*.json)"));
if (!fileName.isEmpty())
{
m_bookmarkManager->loadFromFile(fileName);
}
}
void PDFProgramController::onActionBookmarkGenerateAutomatically(bool checked)
{
auto settings = m_settings->getSettings();
settings.m_autoGenerateBookmarks = checked;
m_settings->setSettings(settings);
m_bookmarkManager->setGenerateBookmarksAutomatically(checked);
}
void PDFProgramController::onPageRenderingErrorsChanged(pdf::PDFInteger pageIndex, int errorsCount) void PDFProgramController::onPageRenderingErrorsChanged(pdf::PDFInteger pageIndex, int errorsCount)
{ {
if (errorsCount > 0) if (errorsCount > 0)

View File

@ -24,6 +24,7 @@
#include "pdfdocumentreader.h" #include "pdfdocumentreader.h"
#include "pdfdocumentpropertiesdialog.h" #include "pdfdocumentpropertiesdialog.h"
#include "pdfplugin.h" #include "pdfplugin.h"
#include "pdfbookmarkmanager.h"
#include <QObject> #include <QObject>
#include <QAction> #include <QAction>
@ -177,6 +178,12 @@ public:
ToolScreenshot, ToolScreenshot,
ToolExtractImage, ToolExtractImage,
DeveloperCreateInstaller, DeveloperCreateInstaller,
BookmarkPage,
BookmarkGoToNext,
BookmarkGoToPrevious,
BookmarkExport,
BookmarkImport,
BookmarkGenerateAutomatically,
LastAction LastAction
}; };
@ -275,6 +282,7 @@ public:
PDFViewerSettings* getSettings() const { return m_settings; } PDFViewerSettings* getSettings() const { return m_settings; }
pdf::PDFDocument* getDocument() const { return m_pdfDocument.data(); } pdf::PDFDocument* getDocument() const { return m_pdfDocument.data(); }
pdf::PDFCertificateStore* getCertificateStore() const { return const_cast<pdf::PDFCertificateStore*>(&m_certificateStore); } pdf::PDFCertificateStore* getCertificateStore() const { return const_cast<pdf::PDFCertificateStore*>(&m_certificateStore); }
PDFBookmarkManager* getBookmarkManager() const { return m_bookmarkManager; }
PDFTextToSpeech* getTextToSpeech() const { return m_textToSpeech; } PDFTextToSpeech* getTextToSpeech() const { return m_textToSpeech; }
const std::vector<pdf::PDFSignatureVerificationResult>* getSignatures() const { return &m_signatures; } const std::vector<pdf::PDFSignatureVerificationResult>* getSignatures() const { return &m_signatures; }
@ -326,6 +334,7 @@ private:
void initializeToolManager(); void initializeToolManager();
void initializeAnnotationManager(); void initializeAnnotationManager();
void initializeFormManager(); void initializeFormManager();
void initializeBookmarkManager();
void onActionGoToDocumentStartTriggered(); void onActionGoToDocumentStartTriggered();
void onActionGoToDocumentEndTriggered(); void onActionGoToDocumentEndTriggered();
@ -364,6 +373,12 @@ private:
void onActionGetSource(); void onActionGetSource();
void onActionBecomeSponsor(); void onActionBecomeSponsor();
void onActionAutomaticDocumentRefresh(); void onActionAutomaticDocumentRefresh();
void onActionBookmarkPage();
void onActionBookmarkGoToNext();
void onActionBookmarkGoToPrevious();
void onActionBookmarkExport();
void onActionBookmarkImport();
void onActionBookmarkGenerateAutomatically(bool checked);
void onDrawSpaceChanged(); void onDrawSpaceChanged();
void onPageLayoutChanged(); void onPageLayoutChanged();
@ -374,6 +389,7 @@ private:
void onViewerSettingsChanged(); void onViewerSettingsChanged();
void onColorManagementSystemChanged(); void onColorManagementSystemChanged();
void onFileChanged(const QString& fileName); void onFileChanged(const QString& fileName);
void onBookmarkActivated(int index, PDFBookmarkManager::Bookmark bookmark);
void updateMagnifierToolSettings(); void updateMagnifierToolSettings();
void updateUndoRedoSettings(); void updateUndoRedoSettings();
@ -381,6 +397,7 @@ private:
void updateTitle(); void updateTitle();
void updatePageLayoutActions(); void updatePageLayoutActions();
void updateRenderingOptionActions(); void updateRenderingOptionActions();
void updateBookmarkSettings();
void setPageLayout(pdf::PageLayout pageLayout); void setPageLayout(pdf::PageLayout pageLayout);
void updateFileInfo(const QString& fileName); void updateFileInfo(const QString& fileName);
@ -422,6 +439,7 @@ private:
pdf::PDFToolManager* m_toolManager; pdf::PDFToolManager* m_toolManager;
pdf::PDFWidgetAnnotationManager* m_annotationManager; pdf::PDFWidgetAnnotationManager* m_annotationManager;
pdf::PDFWidgetFormManager* m_formManager; pdf::PDFWidgetFormManager* m_formManager;
PDFBookmarkManager* m_bookmarkManager;
PDFFileInfo m_fileInfo; PDFFileInfo m_fileInfo;
QFileSystemWatcher m_fileWatcher; QFileSystemWatcher m_fileWatcher;

View File

@ -51,7 +51,7 @@ PDFSanitizeDocumentDialog::PDFSanitizeDocumentDialog(const pdf::PDFDocument* doc
addCheckBox(tr("Remove document info"), pdf::PDFDocumentSanitizer::DocumentInfo); addCheckBox(tr("Remove document info"), pdf::PDFDocumentSanitizer::DocumentInfo);
addCheckBox(tr("Remove all metadata"), pdf::PDFDocumentSanitizer::Metadata); addCheckBox(tr("Remove all metadata"), pdf::PDFDocumentSanitizer::Metadata);
addCheckBox(tr("Remove outline (bookmarks)"), pdf::PDFDocumentSanitizer::Bookmarks); addCheckBox(tr("Remove outline"), pdf::PDFDocumentSanitizer::Outline);
addCheckBox(tr("Remove file attachments"), pdf::PDFDocumentSanitizer::FileAttachments); addCheckBox(tr("Remove file attachments"), pdf::PDFDocumentSanitizer::FileAttachments);
addCheckBox(tr("Remove embedded search index"), pdf::PDFDocumentSanitizer::EmbeddedSearchIndex); addCheckBox(tr("Remove embedded search index"), pdf::PDFDocumentSanitizer::EmbeddedSearchIndex);
addCheckBox(tr("Remove comments and other markup annotations"), pdf::PDFDocumentSanitizer::MarkupAnnotations); addCheckBox(tr("Remove comments and other markup annotations"), pdf::PDFDocumentSanitizer::MarkupAnnotations);

View File

@ -33,6 +33,7 @@
#include "pdfdrawspacecontroller.h" #include "pdfdrawspacecontroller.h"
#include "pdfdocumentbuilder.h" #include "pdfdocumentbuilder.h"
#include "pdfwidgetutils.h" #include "pdfwidgetutils.h"
#include "pdfbookmarkui.h"
#include <QMenu> #include <QMenu>
#include <QAction> #include <QAction>
@ -60,6 +61,7 @@ constexpr const char* STYLESHEET =
PDFSidebarWidget::PDFSidebarWidget(pdf::PDFDrawWidgetProxy* proxy, PDFSidebarWidget::PDFSidebarWidget(pdf::PDFDrawWidgetProxy* proxy,
PDFTextToSpeech* textToSpeech, PDFTextToSpeech* textToSpeech,
pdf::PDFCertificateStore* certificateStore, pdf::PDFCertificateStore* certificateStore,
PDFBookmarkManager* bookmarkManager,
PDFViewerSettings* settings, PDFViewerSettings* settings,
bool editableOutline, bool editableOutline,
QWidget* parent) : QWidget* parent) :
@ -68,10 +70,12 @@ PDFSidebarWidget::PDFSidebarWidget(pdf::PDFDrawWidgetProxy* proxy,
m_proxy(proxy), m_proxy(proxy),
m_textToSpeech(textToSpeech), m_textToSpeech(textToSpeech),
m_certificateStore(certificateStore), m_certificateStore(certificateStore),
m_bookmarkManager(bookmarkManager),
m_settings(settings), m_settings(settings),
m_outlineTreeModel(nullptr), m_outlineTreeModel(nullptr),
m_thumbnailsModel(nullptr), m_thumbnailsModel(nullptr),
m_optionalContentTreeModel(nullptr), m_optionalContentTreeModel(nullptr),
m_bookmarkItemModel(nullptr),
m_document(nullptr), m_document(nullptr),
m_optionalContentActivity(nullptr), m_optionalContentActivity(nullptr),
m_attachmentsTreeModel(nullptr) m_attachmentsTreeModel(nullptr)
@ -81,26 +85,26 @@ PDFSidebarWidget::PDFSidebarWidget(pdf::PDFDrawWidgetProxy* proxy,
setStyleSheet(STYLESHEET); setStyleSheet(STYLESHEET);
// Outline // Outline
QIcon bookmarkIcon(":/resources/bookmark.svg"); QIcon outlineIcon(":/resources/outline.svg");
m_outlineTreeModel = new pdf::PDFOutlineTreeItemModel(qMove(bookmarkIcon), editableOutline, this); m_outlineTreeModel = new pdf::PDFOutlineTreeItemModel(qMove(outlineIcon), editableOutline, this);
ui->bookmarksTreeView->setModel(m_outlineTreeModel); ui->outlineTreeView->setModel(m_outlineTreeModel);
ui->bookmarksTreeView->header()->hide(); ui->outlineTreeView->header()->hide();
if (editableOutline) if (editableOutline)
{ {
ui->bookmarksTreeView->setDragEnabled(true); ui->outlineTreeView->setDragEnabled(true);
ui->bookmarksTreeView->setAcceptDrops(true); ui->outlineTreeView->setAcceptDrops(true);
ui->bookmarksTreeView->setDropIndicatorShown(true); ui->outlineTreeView->setDropIndicatorShown(true);
ui->bookmarksTreeView->setDragDropMode(QAbstractItemView::InternalMove); ui->outlineTreeView->setDragDropMode(QAbstractItemView::InternalMove);
ui->bookmarksTreeView->setContextMenuPolicy(Qt::CustomContextMenu); ui->outlineTreeView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->bookmarksTreeView, &QTreeView::customContextMenuRequested, this, &PDFSidebarWidget::onBookmarksTreeViewContextMenuRequested); connect(ui->outlineTreeView, &QTreeView::customContextMenuRequested, this, &PDFSidebarWidget::onOutlineTreeViewContextMenuRequested);
connect(m_outlineTreeModel, &pdf::PDFOutlineTreeItemModel::dataChanged, this, &PDFSidebarWidget::onOutlineItemsChanged); connect(m_outlineTreeModel, &pdf::PDFOutlineTreeItemModel::dataChanged, this, &PDFSidebarWidget::onOutlineItemsChanged);
connect(m_outlineTreeModel, &pdf::PDFOutlineTreeItemModel::rowsInserted, this, &PDFSidebarWidget::onOutlineItemsChanged); connect(m_outlineTreeModel, &pdf::PDFOutlineTreeItemModel::rowsInserted, this, &PDFSidebarWidget::onOutlineItemsChanged);
connect(m_outlineTreeModel, &pdf::PDFOutlineTreeItemModel::rowsRemoved, this, &PDFSidebarWidget::onOutlineItemsChanged); connect(m_outlineTreeModel, &pdf::PDFOutlineTreeItemModel::rowsRemoved, this, &PDFSidebarWidget::onOutlineItemsChanged);
connect(m_outlineTreeModel, &pdf::PDFOutlineTreeItemModel::rowsMoved, this, &PDFSidebarWidget::onOutlineItemsChanged); connect(m_outlineTreeModel, &pdf::PDFOutlineTreeItemModel::rowsMoved, this, &PDFSidebarWidget::onOutlineItemsChanged);
} }
connect(ui->bookmarksTreeView, &QTreeView::clicked, this, &PDFSidebarWidget::onOutlineItemClicked); connect(ui->outlineTreeView, &QTreeView::clicked, this, &PDFSidebarWidget::onOutlineItemClicked);
// Thumbnails // Thumbnails
m_thumbnailsModel = new pdf::PDFThumbnailsItemModel(proxy, this); m_thumbnailsModel = new pdf::PDFThumbnailsItemModel(proxy, this);
@ -126,13 +130,22 @@ PDFSidebarWidget::PDFSidebarWidget(pdf::PDFDrawWidgetProxy* proxy,
ui->attachmentsTreeView->setContextMenuPolicy(Qt::CustomContextMenu); ui->attachmentsTreeView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->attachmentsTreeView, &QTreeView::customContextMenuRequested, this, &PDFSidebarWidget::onAttachmentCustomContextMenuRequested); connect(ui->attachmentsTreeView, &QTreeView::customContextMenuRequested, this, &PDFSidebarWidget::onAttachmentCustomContextMenuRequested);
// Bookmarks
m_bookmarkItemModel = new PDFBookmarkItemModel(bookmarkManager, this);
ui->bookmarksView->setModel(m_bookmarkItemModel);
ui->bookmarksView->setItemDelegate(new PDFBookmarkItemDelegate(bookmarkManager, this));
connect(m_bookmarkManager, &PDFBookmarkManager::bookmarkActivated, this, &PDFSidebarWidget::onBookmarkActivated);
connect(ui->bookmarksView->selectionModel(), &QItemSelectionModel::currentChanged, this, &PDFSidebarWidget::onBookmarsCurrentIndexChanged);
connect(ui->bookmarksView, &QListView::clicked, this, &PDFSidebarWidget::onBookmarkClicked);
m_pageInfo[Invalid] = { nullptr, ui->emptyPage }; m_pageInfo[Invalid] = { nullptr, ui->emptyPage };
m_pageInfo[OptionalContent] = { ui->optionalContentButton, ui->optionalContentPage }; m_pageInfo[OptionalContent] = { ui->optionalContentButton, ui->optionalContentPage };
m_pageInfo[Bookmarks] = { ui->bookmarksButton, ui->bookmarksPage }; m_pageInfo[Outline] = { ui->outlineButton, ui->outlinePage };
m_pageInfo[Thumbnails] = { ui->thumbnailsButton, ui->thumbnailsPage }; m_pageInfo[Thumbnails] = { ui->thumbnailsButton, ui->thumbnailsPage };
m_pageInfo[Attachments] = { ui->attachmentsButton, ui->attachmentsPage }; m_pageInfo[Attachments] = { ui->attachmentsButton, ui->attachmentsPage };
m_pageInfo[Speech] = { ui->speechButton, ui->speechPage }; m_pageInfo[Speech] = { ui->speechButton, ui->speechPage };
m_pageInfo[Signatures] = { ui->signaturesButton, ui->signaturesPage }; m_pageInfo[Signatures] = { ui->signaturesButton, ui->signaturesPage };
m_pageInfo[Bookmarks] = { ui->bookmarksButton, ui->bookmarksPage };
for (const auto& pageInfo : m_pageInfo) for (const auto& pageInfo : m_pageInfo)
{ {
@ -189,7 +202,7 @@ void PDFSidebarWidget::setDocument(const pdf::PDFModifiedDocument& document, con
switch (pageMode) switch (pageMode)
{ {
case pdf::PageMode::UseOutlines: case pdf::PageMode::UseOutlines:
preferred = Bookmarks; preferred = Outline;
break; break;
case pdf::PageMode::UseThumbnails: case pdf::PageMode::UseThumbnails:
@ -210,7 +223,7 @@ void PDFSidebarWidget::setDocument(const pdf::PDFModifiedDocument& document, con
switch (nonFullscreenPageMode) switch (nonFullscreenPageMode)
{ {
case pdf::PDFViewerPreferences::NonFullScreenPageMode::UseOutline: case pdf::PDFViewerPreferences::NonFullScreenPageMode::UseOutline:
preferred = Bookmarks; preferred = Outline;
break; break;
case pdf::PDFViewerPreferences::NonFullScreenPageMode::UseThumbnails: case pdf::PDFViewerPreferences::NonFullScreenPageMode::UseThumbnails:
@ -259,7 +272,7 @@ bool PDFSidebarWidget::isEmpty(Page page) const
case Invalid: case Invalid:
return true; return true;
case Bookmarks: case Outline:
return m_outlineTreeModel->isEmpty() && (!m_document || !m_outlineTreeModel->isEditable()); return m_outlineTreeModel->isEmpty() && (!m_document || !m_outlineTreeModel->isEditable());
case Thumbnails: case Thumbnails:
@ -271,6 +284,9 @@ bool PDFSidebarWidget::isEmpty(Page page) const
case Attachments: case Attachments:
return m_attachmentsTreeModel->isEmpty(); return m_attachmentsTreeModel->isEmpty();
case Bookmarks:
return !m_document || !m_bookmarkManager;
case Speech: case Speech:
return !m_textToSpeech->isValid(); return !m_textToSpeech->isValid();
@ -764,11 +780,11 @@ void PDFSidebarWidget::onSignatureCustomContextMenuRequested(const QPoint& pos)
} }
} }
void PDFSidebarWidget::onBookmarksTreeViewContextMenuRequested(const QPoint& pos) void PDFSidebarWidget::onOutlineTreeViewContextMenuRequested(const QPoint& pos)
{ {
QMenu contextMenu; QMenu contextMenu;
QModelIndex index = ui->bookmarksTreeView->indexAt(pos); QModelIndex index = ui->outlineTreeView->indexAt(pos);
auto onFollow = [this, index]() auto onFollow = [this, index]()
{ {
@ -779,22 +795,22 @@ void PDFSidebarWidget::onBookmarksTreeViewContextMenuRequested(const QPoint& pos
{ {
if (index.isValid()) if (index.isValid())
{ {
ui->bookmarksTreeView->model()->insertRow(index.row() + 1, index.parent()); ui->outlineTreeView->model()->insertRow(index.row() + 1, index.parent());
} }
else else
{ {
ui->bookmarksTreeView->model()->insertRow(ui->bookmarksTreeView->model()->rowCount()); ui->outlineTreeView->model()->insertRow(ui->outlineTreeView->model()->rowCount());
} }
}; };
auto onDelete = [this, index]() auto onDelete = [this, index]()
{ {
ui->bookmarksTreeView->model()->removeRow(index.row(), index.parent()); ui->outlineTreeView->model()->removeRow(index.row(), index.parent());
}; };
auto onRename = [this, index]() auto onRename = [this, index]()
{ {
ui->bookmarksTreeView->edit(index); ui->outlineTreeView->edit(index);
}; };
QAction* followAction = contextMenu.addAction(tr("Follow"), onFollow); QAction* followAction = contextMenu.addAction(tr("Follow"), onFollow);
@ -927,7 +943,7 @@ void PDFSidebarWidget::onBookmarksTreeViewContextMenuRequested(const QPoint& pos
submenu->addAction(tr("Fit Bounding Box Vertically"), createOnSetTarget(pdf::DestinationType::FitBV)); submenu->addAction(tr("Fit Bounding Box Vertically"), createOnSetTarget(pdf::DestinationType::FitBV));
submenu->addAction(tr("XYZ"), createOnSetTarget(pdf::DestinationType::XYZ)); submenu->addAction(tr("XYZ"), createOnSetTarget(pdf::DestinationType::XYZ));
contextMenu.exec(ui->bookmarksTreeView->mapToGlobal(pos)); contextMenu.exec(ui->outlineTreeView->mapToGlobal(pos));
} }
void PDFSidebarWidget::onOutlineItemsChanged() void PDFSidebarWidget::onOutlineItemsChanged()
@ -943,6 +959,46 @@ void PDFSidebarWidget::onOutlineItemsChanged()
} }
} }
void PDFSidebarWidget::onBookmarkActivated(int index, PDFBookmarkManager::Bookmark bookmark)
{
if (m_bookmarkChangeInProgress)
{
return;
}
pdf::PDFTemporaryValueChange<bool> guard(&m_bookmarkChangeInProgress, true);
QModelIndex currentIndex = m_bookmarkItemModel->index(index, 0, QModelIndex());
ui->bookmarksView->selectionModel()->select(currentIndex, QItemSelectionModel::SelectCurrent);
ui->bookmarksView->setCurrentIndex(currentIndex);
}
void PDFSidebarWidget::onBookmarsCurrentIndexChanged(const QModelIndex& current, const QModelIndex& previous)
{
Q_UNUSED(previous);
if (m_bookmarkChangeInProgress)
{
return;
}
pdf::PDFTemporaryValueChange<bool> guard(&m_bookmarkChangeInProgress, true);
m_bookmarkManager->goToBookmark(current.row(), false);
}
void PDFSidebarWidget::onBookmarkClicked(const QModelIndex& index)
{
if (m_bookmarkChangeInProgress)
{
return;
}
if (index == ui->bookmarksView->currentIndex())
{
pdf::PDFTemporaryValueChange<bool> guard(&m_bookmarkChangeInProgress, true);
m_bookmarkManager->goToCurrentBookmark();
}
}
void PDFSidebarWidget::paintEvent(QPaintEvent* event) void PDFSidebarWidget::paintEvent(QPaintEvent* event)
{ {
Q_UNUSED(event); Q_UNUSED(event);

View File

@ -20,6 +20,7 @@
#define PDFSIDEBARWIDGET_H #define PDFSIDEBARWIDGET_H
#include "pdfglobal.h" #include "pdfglobal.h"
#include "pdfbookmarkmanager.h"
#include <QWidget> #include <QWidget>
@ -52,6 +53,7 @@ namespace pdfviewer
{ {
class PDFTextToSpeech; class PDFTextToSpeech;
class PDFViewerSettings; class PDFViewerSettings;
class PDFBookmarkItemModel;
class PDFSidebarWidget : public QWidget class PDFSidebarWidget : public QWidget
{ {
@ -61,6 +63,7 @@ public:
explicit PDFSidebarWidget(pdf::PDFDrawWidgetProxy* proxy, explicit PDFSidebarWidget(pdf::PDFDrawWidgetProxy* proxy,
PDFTextToSpeech* textToSpeech, PDFTextToSpeech* textToSpeech,
pdf::PDFCertificateStore* certificateStore, pdf::PDFCertificateStore* certificateStore,
PDFBookmarkManager* bookmarkManager,
PDFViewerSettings* settings, PDFViewerSettings* settings,
bool editableOutline, bool editableOutline,
QWidget* parent); QWidget* parent);
@ -72,12 +75,13 @@ public:
{ {
Invalid, Invalid,
_BEGIN, _BEGIN,
Bookmarks = _BEGIN, Outline = _BEGIN,
Thumbnails, Thumbnails,
OptionalContent, OptionalContent,
Attachments, Attachments,
Speech, Speech,
Signatures, Signatures,
Bookmarks,
_END _END
}; };
@ -113,8 +117,11 @@ private:
void onAttachmentCustomContextMenuRequested(const QPoint& pos); void onAttachmentCustomContextMenuRequested(const QPoint& pos);
void onThumbnailClicked(const QModelIndex& index); void onThumbnailClicked(const QModelIndex& index);
void onSignatureCustomContextMenuRequested(const QPoint &pos); void onSignatureCustomContextMenuRequested(const QPoint &pos);
void onBookmarksTreeViewContextMenuRequested(const QPoint &pos); void onOutlineTreeViewContextMenuRequested(const QPoint &pos);
void onOutlineItemsChanged(); void onOutlineItemsChanged();
void onBookmarkActivated(int index, PDFBookmarkManager::Bookmark bookmark);
void onBookmarsCurrentIndexChanged(const QModelIndex& current, const QModelIndex& previous);
void onBookmarkClicked(const QModelIndex& index);
struct PageInfo struct PageInfo
{ {
@ -126,16 +133,19 @@ private:
pdf::PDFDrawWidgetProxy* m_proxy; pdf::PDFDrawWidgetProxy* m_proxy;
PDFTextToSpeech* m_textToSpeech; PDFTextToSpeech* m_textToSpeech;
pdf::PDFCertificateStore* m_certificateStore; pdf::PDFCertificateStore* m_certificateStore;
PDFBookmarkManager* m_bookmarkManager;
PDFViewerSettings* m_settings; PDFViewerSettings* m_settings;
pdf::PDFOutlineTreeItemModel* m_outlineTreeModel; pdf::PDFOutlineTreeItemModel* m_outlineTreeModel;
pdf::PDFThumbnailsItemModel* m_thumbnailsModel; pdf::PDFThumbnailsItemModel* m_thumbnailsModel;
pdf::PDFOptionalContentTreeItemModel* m_optionalContentTreeModel; pdf::PDFOptionalContentTreeItemModel* m_optionalContentTreeModel;
PDFBookmarkItemModel* m_bookmarkItemModel;
const pdf::PDFDocument* m_document; const pdf::PDFDocument* m_document;
pdf::PDFOptionalContentActivity* m_optionalContentActivity; pdf::PDFOptionalContentActivity* m_optionalContentActivity;
pdf::PDFAttachmentsTreeItemModel* m_attachmentsTreeModel; pdf::PDFAttachmentsTreeItemModel* m_attachmentsTreeModel;
std::map<Page, PageInfo> m_pageInfo; std::map<Page, PageInfo> m_pageInfo;
std::vector<pdf::PDFSignatureVerificationResult> m_signatures; std::vector<pdf::PDFSignatureVerificationResult> m_signatures;
std::vector<pdf::PDFCertificateInfo> m_certificateInfos; std::vector<pdf::PDFCertificateInfo> m_certificateInfos;
bool m_bookmarkChangeInProgress = false;
}; };
} // namespace pdfviewer } // namespace pdfviewer

View File

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>339</width> <width>388</width>
<height>584</height> <height>681</height>
</rect> </rect>
</property> </property>
<layout class="QHBoxLayout" name="sidebarLayout"> <layout class="QHBoxLayout" name="sidebarLayout">
@ -29,7 +29,7 @@
<item> <item>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<item> <item>
<widget class="QToolButton" name="bookmarksButton"> <widget class="QToolButton" name="outlineButton">
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
<width>96</width> <width>96</width>
@ -42,7 +42,7 @@
</font> </font>
</property> </property>
<property name="text"> <property name="text">
<string>Bookmarks</string> <string>Outline</string>
</property> </property>
<property name="icon"> <property name="icon">
<iconset resource="pdf4qtviewer.qrc"> <iconset resource="pdf4qtviewer.qrc">
@ -225,6 +225,35 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QToolButton" name="bookmarksButton">
<property name="minimumSize">
<size>
<width>96</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Bookmarks</string>
</property>
<property name="icon">
<iconset resource="pdf4qtviewer.qrc">
<normaloff>:/resources/sidebar-favourites.svg</normaloff>:/resources/sidebar-favourites.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>64</width>
<height>64</height>
</size>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextUnderIcon</enum>
</property>
</widget>
</item>
<item> <item>
<spacer name="buttonSpacer"> <spacer name="buttonSpacer">
<property name="orientation"> <property name="orientation">
@ -243,11 +272,11 @@
<item> <item>
<widget class="QStackedWidget" name="stackedWidget"> <widget class="QStackedWidget" name="stackedWidget">
<property name="currentIndex"> <property name="currentIndex">
<number>2</number> <number>5</number>
</property> </property>
<widget class="QWidget" name="emptyPage"/> <widget class="QWidget" name="emptyPage"/>
<widget class="QWidget" name="bookmarksPage"> <widget class="QWidget" name="outlinePage">
<layout class="QVBoxLayout" name="bookmarksPageLayout"> <layout class="QVBoxLayout" name="outlinePageLayout">
<property name="spacing"> <property name="spacing">
<number>0</number> <number>0</number>
</property> </property>
@ -264,7 +293,7 @@
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
<widget class="QTreeView" name="bookmarksTreeView"/> <widget class="QTreeView" name="outlineTreeView"/>
</item> </item>
</layout> </layout>
</widget> </widget>
@ -383,6 +412,32 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="bookmarksPage">
<layout class="QVBoxLayout" name="verticalLayout_3">
<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>
<item>
<widget class="QListView" name="bookmarksView">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="speechPage"> <widget class="QWidget" name="speechPage">
<layout class="QVBoxLayout" name="verticalLayout_2"> <layout class="QVBoxLayout" name="verticalLayout_2">
<item> <item>

View File

@ -194,6 +194,12 @@ PDFViewerMainWindow::PDFViewerMainWindow(QWidget* parent) :
m_actionManager->setAction(PDFActionManager::ToolScreenshot, ui->actionScreenshot); m_actionManager->setAction(PDFActionManager::ToolScreenshot, ui->actionScreenshot);
m_actionManager->setAction(PDFActionManager::ToolExtractImage, ui->actionExtractImage); m_actionManager->setAction(PDFActionManager::ToolExtractImage, ui->actionExtractImage);
m_actionManager->setAction(PDFActionManager::DeveloperCreateInstaller, ui->actionDeveloperCreateInstaller); m_actionManager->setAction(PDFActionManager::DeveloperCreateInstaller, ui->actionDeveloperCreateInstaller);
m_actionManager->setAction(PDFActionManager::BookmarkPage, ui->actionBookmarkPage);
m_actionManager->setAction(PDFActionManager::BookmarkGoToNext, ui->actionGotoNextBookmark);
m_actionManager->setAction(PDFActionManager::BookmarkGoToPrevious, ui->actionGotoPreviousBookmark);
m_actionManager->setAction(PDFActionManager::BookmarkExport, ui->actionBookmarkExport);
m_actionManager->setAction(PDFActionManager::BookmarkImport, ui->actionBookmarkImport);
m_actionManager->setAction(PDFActionManager::BookmarkGenerateAutomatically, ui->actionBookmarkAutoGenerate);
m_actionManager->initActions(pdf::PDFWidgetUtils::scaleDPI(this, QSize(24, 24)), true); m_actionManager->initActions(pdf::PDFWidgetUtils::scaleDPI(this, QSize(24, 24)), true);
for (QAction* action : m_programController->getRecentFileManager()->getActions()) for (QAction* action : m_programController->getRecentFileManager()->getActions())
@ -269,7 +275,7 @@ PDFViewerMainWindow::PDFViewerMainWindow(QWidget* parent) :
setCentralWidget(m_programController->getPdfWidget()); setCentralWidget(m_programController->getPdfWidget());
setFocusProxy(m_programController->getPdfWidget()); setFocusProxy(m_programController->getPdfWidget());
m_sidebarWidget = new PDFSidebarWidget(m_programController->getPdfWidget()->getDrawWidgetProxy(), m_programController->getTextToSpeech(), m_programController->getCertificateStore(), m_programController->getSettings(), true, this); m_sidebarWidget = new PDFSidebarWidget(m_programController->getPdfWidget()->getDrawWidgetProxy(), m_programController->getTextToSpeech(), m_programController->getCertificateStore(), m_programController->getBookmarkManager(), m_programController->getSettings(), true, this);
m_sidebarDockWidget = new QDockWidget(tr("Sidebar"), this); m_sidebarDockWidget = new QDockWidget(tr("Sidebar"), this);
m_sidebarDockWidget->setObjectName("SidebarDockWidget"); m_sidebarDockWidget->setObjectName("SidebarDockWidget");
m_sidebarDockWidget->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); m_sidebarDockWidget->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);

View File

@ -46,12 +46,25 @@
<property name="title"> <property name="title">
<string>Go To</string> <string>Go To</string>
</property> </property>
<widget class="QMenu" name="menuBookmarkSettings">
<property name="title">
<string>Bookmark Settings</string>
</property>
<addaction name="actionBookmarkExport"/>
<addaction name="actionBookmarkImport"/>
<addaction name="actionBookmarkAutoGenerate"/>
</widget>
<addaction name="actionGoToDocumentStart"/> <addaction name="actionGoToDocumentStart"/>
<addaction name="actionGoToDocumentEnd"/> <addaction name="actionGoToDocumentEnd"/>
<addaction name="actionGoToPreviousPage"/> <addaction name="actionGoToPreviousPage"/>
<addaction name="actionGoToNextPage"/> <addaction name="actionGoToNextPage"/>
<addaction name="actionGoToPreviousLine"/> <addaction name="actionGoToPreviousLine"/>
<addaction name="actionGoToNextLine"/> <addaction name="actionGoToNextLine"/>
<addaction name="separator"/>
<addaction name="actionBookmarkPage"/>
<addaction name="actionGotoPreviousBookmark"/>
<addaction name="actionGotoNextBookmark"/>
<addaction name="menuBookmarkSettings"/>
</widget> </widget>
<widget class="QMenu" name="menuView"> <widget class="QMenu" name="menuView">
<property name="title"> <property name="title">
@ -1051,6 +1064,99 @@
<string>Convert the colored images to monochromatic to create a bitonal document.</string> <string>Convert the colored images to monochromatic to create a bitonal document.</string>
</property> </property>
</action> </action>
<action name="actionBookmarkPage">
<property name="icon">
<iconset resource="pdf4qtviewer.qrc">
<normaloff>:/resources/bookmark.svg</normaloff>:/resources/bookmark.svg</iconset>
</property>
<property name="text">
<string>Bookmark Page</string>
</property>
<property name="toolTip">
<string>Bookmark Page</string>
</property>
<property name="statusTip">
<string>Bookmark page for fast navigation.</string>
</property>
</action>
<action name="actionGotoNextBookmark">
<property name="icon">
<iconset resource="pdf4qtviewer.qrc">
<normaloff>:/resources/bookmark-next.svg</normaloff>:/resources/bookmark-next.svg</iconset>
</property>
<property name="text">
<string>Go to Next Bookmark</string>
</property>
<property name="toolTip">
<string>Go to Next Bookmark</string>
</property>
<property name="statusTip">
<string>Navigates to the next bookmarked page.</string>
</property>
</action>
<action name="actionGotoPreviousBookmark">
<property name="icon">
<iconset resource="pdf4qtviewer.qrc">
<normaloff>:/resources/bookmark-previous.svg</normaloff>:/resources/bookmark-previous.svg</iconset>
</property>
<property name="text">
<string>Go to Previous Bookmark</string>
</property>
<property name="toolTip">
<string>Go to Previous Bookmark</string>
</property>
<property name="statusTip">
<string>Navigates to the previous bookmarked page.</string>
</property>
</action>
<action name="actionBookmarkExport">
<property name="icon">
<iconset resource="pdf4qtviewer.qrc">
<normaloff>:/resources/bookmark.svg</normaloff>:/resources/bookmark.svg</iconset>
</property>
<property name="text">
<string>Export Bookmarks</string>
</property>
<property name="toolTip">
<string>Export Bookmarks</string>
</property>
<property name="statusTip">
<string>Export bookmarks to the file.</string>
</property>
</action>
<action name="actionBookmarkImport">
<property name="icon">
<iconset resource="pdf4qtviewer.qrc">
<normaloff>:/resources/bookmark.svg</normaloff>:/resources/bookmark.svg</iconset>
</property>
<property name="text">
<string>Import Bookmarks</string>
</property>
<property name="toolTip">
<string>Import Bookmarks</string>
</property>
<property name="statusTip">
<string>Import bookmarks from the file.</string>
</property>
</action>
<action name="actionBookmarkAutoGenerate">
<property name="checkable">
<bool>true</bool>
</property>
<property name="icon">
<iconset resource="pdf4qtviewer.qrc">
<normaloff>:/resources/bookmark.svg</normaloff>:/resources/bookmark.svg</iconset>
</property>
<property name="text">
<string>Generate Bookmarks Automatically</string>
</property>
<property name="toolTip">
<string>Generate Bookmarks Automatically</string>
</property>
<property name="statusTip">
<string>If checked, bookmarks for main document chapters are generated automatically.</string>
</property>
</action>
</widget> </widget>
<layoutdefault spacing="6" margin="11"/> <layoutdefault spacing="6" margin="11"/>
<resources> <resources>

View File

@ -151,6 +151,12 @@ PDFViewerMainWindowLite::PDFViewerMainWindowLite(QWidget* parent) :
m_actionManager->setAction(PDFActionManager::PageLayoutTwoPages, ui->actionPageLayoutTwoPages); m_actionManager->setAction(PDFActionManager::PageLayoutTwoPages, ui->actionPageLayoutTwoPages);
m_actionManager->setAction(PDFActionManager::PageLayoutTwoColumns, ui->actionPageLayoutTwoColumns); m_actionManager->setAction(PDFActionManager::PageLayoutTwoColumns, ui->actionPageLayoutTwoColumns);
m_actionManager->setAction(PDFActionManager::PageLayoutFirstPageOnRightSide, ui->actionFirstPageOnRightSide); m_actionManager->setAction(PDFActionManager::PageLayoutFirstPageOnRightSide, ui->actionFirstPageOnRightSide);
m_actionManager->setAction(PDFActionManager::BookmarkPage, ui->actionBookmarkPage);
m_actionManager->setAction(PDFActionManager::BookmarkGoToNext, ui->actionGotoNextBookmark);
m_actionManager->setAction(PDFActionManager::BookmarkGoToPrevious, ui->actionGotoPreviousBookmark);
m_actionManager->setAction(PDFActionManager::BookmarkExport, ui->actionBookmarkExport);
m_actionManager->setAction(PDFActionManager::BookmarkImport, ui->actionBookmarkImport);
m_actionManager->setAction(PDFActionManager::BookmarkGenerateAutomatically, ui->actionBookmarkAutoGenerate);
m_actionManager->initActions(pdf::PDFWidgetUtils::scaleDPI(this, QSize(24, 24)), true); m_actionManager->initActions(pdf::PDFWidgetUtils::scaleDPI(this, QSize(24, 24)), true);
for (QAction* action : m_programController->getRecentFileManager()->getActions()) for (QAction* action : m_programController->getRecentFileManager()->getActions())
@ -202,7 +208,7 @@ PDFViewerMainWindowLite::PDFViewerMainWindowLite(QWidget* parent) :
setCentralWidget(m_programController->getPdfWidget()); setCentralWidget(m_programController->getPdfWidget());
setFocusProxy(m_programController->getPdfWidget()); setFocusProxy(m_programController->getPdfWidget());
m_sidebarWidget = new PDFSidebarWidget(m_programController->getPdfWidget()->getDrawWidgetProxy(), m_programController->getTextToSpeech(), m_programController->getCertificateStore(), m_programController->getSettings(), false, this); m_sidebarWidget = new PDFSidebarWidget(m_programController->getPdfWidget()->getDrawWidgetProxy(), m_programController->getTextToSpeech(), m_programController->getCertificateStore(), m_programController->getBookmarkManager(), m_programController->getSettings(), false, this);
m_sidebarDockWidget = new QDockWidget(tr("Sidebar"), this); m_sidebarDockWidget = new QDockWidget(tr("Sidebar"), this);
m_sidebarDockWidget->setObjectName("SidebarDockWidget"); m_sidebarDockWidget->setObjectName("SidebarDockWidget");
m_sidebarDockWidget->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); m_sidebarDockWidget->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);

View File

@ -42,12 +42,26 @@
<property name="title"> <property name="title">
<string>Go To</string> <string>Go To</string>
</property> </property>
<widget class="QMenu" name="menuBookmarkSettings">
<property name="title">
<string>Bookmark Settings</string>
</property>
<addaction name="actionBookmarkExport"/>
<addaction name="actionBookmarkImport"/>
<addaction name="actionBookmarkAutoGenerate"/>
</widget>
<addaction name="actionGoToDocumentStart"/> <addaction name="actionGoToDocumentStart"/>
<addaction name="actionGoToDocumentEnd"/> <addaction name="actionGoToDocumentEnd"/>
<addaction name="actionGoToPreviousPage"/> <addaction name="actionGoToPreviousPage"/>
<addaction name="actionGoToNextPage"/> <addaction name="actionGoToNextPage"/>
<addaction name="actionGoToPreviousLine"/> <addaction name="actionGoToPreviousLine"/>
<addaction name="actionGoToNextLine"/> <addaction name="actionGoToNextLine"/>
<addaction name="separator"/>
<addaction name="actionBookmarkPage"/>
<addaction name="actionGotoPreviousBookmark"/>
<addaction name="actionGotoNextBookmark"/>
<addaction name="menuBookmarkSettings"/>
</widget> </widget>
<widget class="QMenu" name="menuView"> <widget class="QMenu" name="menuView">
<property name="title"> <property name="title">
@ -543,6 +557,100 @@
<string>Become a Sponsor</string> <string>Become a Sponsor</string>
</property> </property>
</action> </action>
<action name="actionBookmarkPage">
<property name="icon">
<iconset resource="pdf4qtviewer.qrc">
<normaloff>:/resources/bookmark.svg</normaloff>:/resources/bookmark.svg</iconset>
</property>
<property name="text">
<string>Bookmark Page</string>
</property>
<property name="toolTip">
<string>Bookmark Page</string>
</property>
<property name="statusTip">
<string>Bookmark page for fast navigation.</string>
</property>
</action>
<action name="actionGotoNextBookmark">
<property name="icon">
<iconset resource="pdf4qtviewer.qrc">
<normaloff>:/resources/bookmark-next.svg</normaloff>:/resources/bookmark-next.svg</iconset>
</property>
<property name="text">
<string>Go to Next Bookmark</string>
</property>
<property name="toolTip">
<string>Go to Next Bookmark</string>
</property>
<property name="statusTip">
<string>Navigates to the next bookmarked page.</string>
</property>
</action>
<action name="actionGotoPreviousBookmark">
<property name="icon">
<iconset resource="pdf4qtviewer.qrc">
<normaloff>:/resources/bookmark-previous.svg</normaloff>:/resources/bookmark-previous.svg</iconset>
</property>
<property name="text">
<string>Go to Previous Bookmark</string>
</property>
<property name="toolTip">
<string>Go to Previous Bookmark</string>
</property>
<property name="statusTip">
<string>Navigates to the previous bookmarked page.</string>
</property>
</action>
<action name="actionBookmarkExport">
<property name="icon">
<iconset resource="pdf4qtviewer.qrc">
<normaloff>:/resources/bookmark.svg</normaloff>:/resources/bookmark.svg</iconset>
</property>
<property name="text">
<string>Export Bookmarks</string>
</property>
<property name="toolTip">
<string>Export Bookmarks</string>
</property>
<property name="statusTip">
<string>Export bookmarks to the file.</string>
</property>
</action>
<action name="actionBookmarkImport">
<property name="icon">
<iconset resource="pdf4qtviewer.qrc">
<normaloff>:/resources/bookmark.svg</normaloff>:/resources/bookmark.svg</iconset>
</property>
<property name="text">
<string>Import Bookmarks</string>
</property>
<property name="toolTip">
<string>Import Bookmarks</string>
</property>
<property name="statusTip">
<string>Import bookmarks from the file.</string>
</property>
</action>
<action name="actionBookmarkAutoGenerate">
<property name="checkable">
<bool>true</bool>
</property>
<property name="icon">
<iconset resource="pdf4qtviewer.qrc">
<normaloff>:/resources/bookmark.svg</normaloff>:/resources/bookmark.svg</iconset>
</property>
<property name="text">
<string>Generate Bookmarks Automatically</string>
</property>
<property name="toolTip">
<string>Generate Bookmarks Automatically</string>
</property>
<property name="statusTip">
<string>If checked, bookmarks for main document chapters are generated automatically.</string>
</property>
</action>
</widget> </widget>
<layoutdefault spacing="6" margin="11"/> <layoutdefault spacing="6" margin="11"/>
<resources> <resources>

View File

@ -104,6 +104,10 @@ void PDFViewerSettings::readSettings(QSettings& settings, const pdf::PDFCMSSetti
m_settings.m_signatureUseSystemStore = settings.value("signatureUseSystemStore", defaultSettings.m_signatureUseSystemStore).toBool(); m_settings.m_signatureUseSystemStore = settings.value("signatureUseSystemStore", defaultSettings.m_signatureUseSystemStore).toBool();
settings.endGroup(); settings.endGroup();
settings.beginGroup("Bookmarks");
m_settings.m_autoGenerateBookmarks = settings.value("autoGenerateBookmarks", defaultSettings.m_autoGenerateBookmarks).toBool();
settings.endGroup();
Q_EMIT settingsChanged(); Q_EMIT settingsChanged();
} }
@ -174,6 +178,10 @@ void PDFViewerSettings::writeSettings(QSettings& settings)
settings.setValue("signatureIgnoreCertificateValidityTime", m_settings.m_signatureIgnoreCertificateValidityTime); settings.setValue("signatureIgnoreCertificateValidityTime", m_settings.m_signatureIgnoreCertificateValidityTime);
settings.setValue("signatureUseSystemStore", m_settings.m_signatureUseSystemStore); settings.setValue("signatureUseSystemStore", m_settings.m_signatureUseSystemStore);
settings.endGroup(); settings.endGroup();
settings.beginGroup("Bookmarks");
settings.setValue("autoGenerateBookmarks", m_settings.m_autoGenerateBookmarks);
settings.endGroup();
} }
QString PDFViewerSettings::getDirectory() const QString PDFViewerSettings::getDirectory() const
@ -287,7 +295,8 @@ PDFViewerSettings::Settings::Settings() :
m_signatureVerificationEnabled(true), m_signatureVerificationEnabled(true),
m_signatureTreatWarningsAsErrors(false), m_signatureTreatWarningsAsErrors(false),
m_signatureIgnoreCertificateValidityTime(false), m_signatureIgnoreCertificateValidityTime(false),
m_signatureUseSystemStore(true) m_signatureUseSystemStore(true),
m_autoGenerateBookmarks(true)
{ {
} }

View File

@ -91,6 +91,9 @@ public:
bool m_signatureTreatWarningsAsErrors; bool m_signatureTreatWarningsAsErrors;
bool m_signatureIgnoreCertificateValidityTime; bool m_signatureIgnoreCertificateValidityTime;
bool m_signatureUseSystemStore; bool m_signatureUseSystemStore;
// Bookmarks settings
bool m_autoGenerateBookmarks;
}; };
const Settings& getSettings() const { return m_settings; } const Settings& getSettings() const { return m_settings; }

View File

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="svg815"
width="512"
height="512"
viewBox="0 0 512 512"
sodipodi:docname="bookmark-next.svg"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
<metadata
id="metadata821">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs819" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="3840"
inkscape:window-height="2035"
id="namedview817"
showgrid="false"
inkscape:zoom="2.4279174"
inkscape:cx="137.55859"
inkscape:cy="137.21551"
inkscape:window-x="-13"
inkscape:window-y="-13"
inkscape:window-maximized="1"
inkscape:current-layer="svg815" />
<path
sodipodi:type="star"
style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:26.45669291;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="path839"
sodipodi:sides="5"
sodipodi:cx="259.20364"
sodipodi:cy="269.65189"
sodipodi:r1="203.30641"
sodipodi:r2="101.65321"
sodipodi:arg1="-1.585122"
sodipodi:arg2="-0.95680346"
inkscape:flatsided="false"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 256.29124,66.366336 61.47844,120.198874 133.87003,17.49813 -95.31804,95.61296 24.7264,132.72518 L 260.65984,371.29467 142.07158,435.82522 162.9856,302.44616 64.967615,209.60305 198.28142,188.27708 Z"
inkscape:transform-center-x="0.89998123"
inkscape:transform-center-y="-18.55611" />
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#000000;stroke-width:31.68950462;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 220.58404,237.11509 v 70.93287 c 0,14.53213 13.81864,23.63982 24.86768,16.38716 l 26.95074,-17.71983 26.95662,-17.78679 c 11.03729,-7.24597 11.03729,-25.42788 0,-32.68725 l -26.95662,-17.7734 -26.95074,-17.72652 c -11.04904,-7.27945 -24.86768,1.76127 -24.86768,16.37376 z"
id="path1701" />
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="svg815"
width="512"
height="512"
viewBox="0 0 512 512"
sodipodi:docname="bookmark-previous.svg"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
<metadata
id="metadata821">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs819" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="3840"
inkscape:window-height="2035"
id="namedview817"
showgrid="false"
inkscape:zoom="0.21459961"
inkscape:cx="-2504.7782"
inkscape:cy="317.10796"
inkscape:window-x="-13"
inkscape:window-y="-13"
inkscape:window-maximized="1"
inkscape:current-layer="svg815" />
<path
sodipodi:type="star"
style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:26.45669291;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="path839"
sodipodi:sides="5"
sodipodi:cx="259.20364"
sodipodi:cy="269.65189"
sodipodi:r1="203.30641"
sodipodi:r2="101.65321"
sodipodi:arg1="-1.585122"
sodipodi:arg2="-0.95680346"
inkscape:flatsided="false"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 256.29124,66.366336 61.47844,120.198874 133.87003,17.49813 -95.31804,95.61296 24.7264,132.72518 L 260.65984,371.29467 142.07158,435.82522 162.9856,302.44616 64.967615,209.60305 198.28142,188.27708 Z"
inkscape:transform-center-x="0.89998123"
inkscape:transform-center-y="-18.55611" />
<path
inkscape:connector-curvature="0"
style="fill:none;stroke:#000000;stroke-width:31.68950462;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 292.61298,235.87946 v 70.93287 c 0,14.53213 -13.81864,23.63982 -24.86768,16.38716 l -26.95074,-17.71983 -26.95662,-17.78679 c -11.03729,-7.24597 -11.03729,-25.42788 0,-32.68725 l 26.95662,-17.7734 26.95074,-17.72652 c 11.04904,-7.27945 24.86768,1.76127 24.86768,16.37376 z"
id="path1701" />
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -1,10 +1,70 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> <!-- Created with Inkscape (http://www.inkscape.org/) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Vrstva_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" <svg
width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve"> xmlns:dc="http://purl.org/dc/elements/1.1/"
<g> xmlns:cc="http://creativecommons.org/ns#"
<path fill="#CCCED0" stroke="#292D32" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" d="M20.264,22.624 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
L12,16.722l-8.264,5.902V3.737c0-1.304,1.057-2.361,2.361-2.361h11.805c1.305,0,2.361,1.057,2.361,2.361V22.624z"/> xmlns:svg="http://www.w3.org/2000/svg"
</g> xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="svg815"
width="512"
height="512"
viewBox="0 0 512 512"
sodipodi:docname="sidebar-favourites.svg"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
<metadata
id="metadata821">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs819" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="3840"
inkscape:window-height="2035"
id="namedview817"
showgrid="false"
inkscape:zoom="1.2139587"
inkscape:cx="535.31725"
inkscape:cy="132.65123"
inkscape:window-x="-13"
inkscape:window-y="-13"
inkscape:window-maximized="1"
inkscape:current-layer="svg815" />
<path
sodipodi:type="star"
style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:26.45669291;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="path839"
sodipodi:sides="5"
sodipodi:cx="259.20364"
sodipodi:cy="269.65189"
sodipodi:r1="203.30641"
sodipodi:r2="101.65321"
sodipodi:arg1="-1.585122"
sodipodi:arg2="-0.95680346"
inkscape:flatsided="false"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 256.29124,66.366336 61.47844,120.198874 133.87003,17.49813 -95.31804,95.61296 24.7264,132.72518 L 260.65984,371.29467 142.07158,435.82522 162.9856,302.44616 64.967615,209.60305 198.28142,188.27708 Z"
inkscape:transform-center-x="0.89998123"
inkscape:transform-center-y="-18.55611" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 718 B

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Vrstva_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<g>
<path fill="#CCCED0" stroke="#292D32" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" d="M20.264,22.624
L12,16.722l-8.264,5.902V3.737c0-1.304,1.057-2.361,2.361-2.361h11.805c1.305,0,2.361,1.057,2.361,2.361V22.624z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 718 B

View File

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="svg815"
width="512"
height="512"
viewBox="0 0 512 512"
sodipodi:docname="sidebar-favourites.svg"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
<metadata
id="metadata821">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs819" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="3840"
inkscape:window-height="2035"
id="namedview817"
showgrid="false"
inkscape:zoom="1.2139587"
inkscape:cx="535.31725"
inkscape:cy="132.65123"
inkscape:window-x="-13"
inkscape:window-y="-13"
inkscape:window-maximized="1"
inkscape:current-layer="svg815" />
<path
sodipodi:type="star"
style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:26.45669291;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
id="path839"
sodipodi:sides="5"
sodipodi:cx="259.20364"
sodipodi:cy="269.65189"
sodipodi:r1="203.30641"
sodipodi:r2="101.65321"
sodipodi:arg1="-1.585122"
sodipodi:arg2="-0.95680346"
inkscape:flatsided="false"
inkscape:rounded="0"
inkscape:randomized="0"
d="m 256.29124,66.366336 61.47844,120.198874 133.87003,17.49813 -95.31804,95.61296 24.7264,132.72518 L 260.65984,371.29467 142.07158,435.82522 162.9856,302.44616 64.967615,209.60305 198.28142,188.27708 Z"
inkscape:transform-center-x="0.89998123"
inkscape:transform-center-y="-18.55611" />
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB