PDF4QT/Pdf4QtViewer/pdfbookmarkmanager.cpp

355 lines
9.7 KiB
C++

// 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 <QFile>
#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