2023-12-16 20:23:53 +01:00
|
|
|
// 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"
|
|
|
|
|
2023-12-21 14:25:41 +01:00
|
|
|
#include <QFile>
|
2023-12-16 20:23:53 +01:00
|
|
|
#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;
|
2023-12-17 18:38:45 +01:00
|
|
|
json["pageIndex"] = static_cast<qint64>(bookmark.pageIndex);
|
2023-12-16 20:23:53 +01:00
|
|
|
return json;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PDFBookmarkManager::Bookmark convertJsonToBookmark(const QJsonObject& json)
|
|
|
|
{
|
|
|
|
PDFBookmarkManager::Bookmark bookmark;
|
|
|
|
bookmark.isAuto = json["isAuto"].toBool();
|
|
|
|
bookmark.name = json["name"].toString();
|
2023-12-17 18:38:45 +01:00
|
|
|
bookmark.pageIndex = json["pageIndex"].toInteger();
|
2023-12-16 20:23:53 +01:00
|
|
|
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();
|
|
|
|
|
2023-12-17 18:11:49 +01:00
|
|
|
if (document.hasReset())
|
2023-12-16 20:23:53 +01:00
|
|
|
{
|
2023-12-17 18:11:49 +01:00
|
|
|
if (!document.hasPreserveView())
|
2023-12-16 20:23:53 +01:00
|
|
|
{
|
2023-12-17 18:11:49 +01:00
|
|
|
m_bookmarks.bookmarks.clear();
|
|
|
|
regenerateAutoBookmarks();
|
2023-12-16 20:23:53 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Q_EMIT bookmarksChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
void PDFBookmarkManager::saveToFile(QString fileName)
|
|
|
|
{
|
2023-12-17 18:11:49 +01:00
|
|
|
QJsonDocument doc(PDFBookmarkManagerHelper::convertBookmarksToJson(m_bookmarks));
|
2023-12-16 20:23:53 +01:00
|
|
|
|
|
|
|
// 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();
|
|
|
|
|
2023-12-17 18:11:49 +01:00
|
|
|
Q_EMIT bookmarksAboutToBeChanged();
|
|
|
|
m_bookmarks = PDFBookmarkManagerHelper::convertBookmarksFromJson(loadedDoc.object());
|
|
|
|
Q_EMIT bookmarksChanged();
|
2023-12-16 20:23:53 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-12-17 18:11:49 +01:00
|
|
|
bool PDFBookmarkManager::isEmpty() const
|
|
|
|
{
|
|
|
|
return m_bookmarks.bookmarks.empty();
|
|
|
|
}
|
|
|
|
|
2023-12-16 20:23:53 +01:00
|
|
|
int PDFBookmarkManager::getBookmarkCount() const
|
|
|
|
{
|
2023-12-17 18:11:49 +01:00
|
|
|
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())
|
2023-12-16 20:23:53 +01:00
|
|
|
{
|
2023-12-17 18:11:49 +01:00
|
|
|
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();
|
2023-12-16 20:23:53 +01:00
|
|
|
}
|
|
|
|
|
2023-12-17 18:11:49 +01:00
|
|
|
Q_EMIT bookmarksChanged();
|
2023-12-16 20:23:53 +01:00
|
|
|
}
|
|
|
|
|
2023-12-17 18:11:49 +01:00
|
|
|
void PDFBookmarkManager::setGenerateBookmarksAutomatically(bool generateBookmarksAutomatically)
|
2023-12-16 20:23:53 +01:00
|
|
|
{
|
2023-12-17 18:11:49 +01:00
|
|
|
if (m_generateBookmarksAutomatically != generateBookmarksAutomatically)
|
2023-12-16 20:23:53 +01:00
|
|
|
{
|
2023-12-17 18:11:49 +01:00
|
|
|
Q_EMIT bookmarksAboutToBeChanged();
|
|
|
|
m_generateBookmarksAutomatically = generateBookmarksAutomatically;
|
|
|
|
regenerateAutoBookmarks();
|
|
|
|
Q_EMIT bookmarksChanged();
|
2023-12-16 20:23:53 +01:00
|
|
|
}
|
2023-12-17 18:11:49 +01:00
|
|
|
}
|
2023-12-16 20:23:53 +01:00
|
|
|
|
2023-12-17 18:11:49 +01:00
|
|
|
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);
|
2023-12-16 20:23:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void PDFBookmarkManager::regenerateAutoBookmarks()
|
|
|
|
{
|
|
|
|
if (!m_document)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create bookmarks for all main chapters
|
2023-12-17 18:11:49 +01:00
|
|
|
Bookmarks& bookmarks = m_bookmarks;
|
2023-12-16 20:23:53 +01:00
|
|
|
|
|
|
|
for (auto it = bookmarks.bookmarks.begin(); it != bookmarks.bookmarks.end();)
|
|
|
|
{
|
|
|
|
if (it->isAuto)
|
|
|
|
{
|
|
|
|
it = bookmarks.bookmarks.erase(it);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-17 18:11:49 +01:00
|
|
|
if (!m_generateBookmarksAutomatically)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-12-16 20:23:53 +01:00
|
|
|
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
|