// Copyright (C) 2020-2022 Jakub Melka // // This file is part of PDF4QT. // // PDF4QT is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // with the written consent of the copyright owner, any later version. // // PDF4QT is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with PDF4QT. If not, see . #include "pdfpagenavigation.h" #include "pdfdbgheap.h" namespace pdf { PDFPageNavigation::PDFPageNavigation(const PDFDocument* document, QObject* parent) : QObject(parent), m_currentPageIndex(0), m_document(document) { if (m_document && m_document->getCatalog()->getPageCount() > 0) { navigateToPage(0); } } void PDFPageNavigation::navigateToPage(size_t pageIndex) { if (m_currentPageIndex == pageIndex) { // Jakub Melka: We are at the same page! Do nothing. return; } const bool isForward = pageIndex > m_currentPageIndex; const PDFObjectStorage* const storage = &m_document->getStorage(); const PDFPage* page = m_document->getCatalog()->getPage(pageIndex); // We will perform steps as in PDF 2.0 specification, chapter 12.4.4.2 // Step a) navigate to page and set current navigation node m_currentPageIndex = pageIndex; m_transition = PDFPageTransition::parse(storage, page->getTransition(storage)); m_currentNavigationNode = PDFNavigationNode::parse(storage, page->getFirstSubpageNavigationNode(storage)); // Step b) Execute initially page navigation node. If we are navigating forward, // then "next" action should be executed and current node should be changed to next. // If we are navigating backward, then "previous" action should be executed // and current node should be changed to previous. executeNavigationNode(isForward ? Direction::Forward : Direction::Backward); // Step c) Navigate to new page - send navigation request. Q_EMIT pageChangeRequest(m_currentPageIndex, &m_transition); } void PDFPageNavigation::navigateForward() { if (m_currentNavigationNode) { executeNavigationNode(Direction::Forward); } else if (m_currentPageIndex + 1 < m_document->getCatalog()->getPageCount()) { navigateToPage(m_currentPageIndex + 1); } } void PDFPageNavigation::navigateBackward() { if (m_currentNavigationNode) { executeNavigationNode(Direction::Backward); } else if (m_currentPageIndex > 0) { navigateToPage(m_currentPageIndex - 1); } } void PDFPageNavigation::executeNavigationNode(Direction direction) { if (!m_currentNavigationNode) { return; } // We will make a copy of object, because this object can change, // when actions are executed. PDFNavigationNode copy = *m_currentNavigationNode; m_currentNavigationNode = PDFNavigationNode::parse(&m_document->getStorage(), m_document->getObjectByReference((direction == Direction::Forward) ? copy.getNextNode() : copy.getPreviousNode())); switch (direction) { case Direction::Forward: { if (const PDFAction* action = copy.getNextAction().get()) { for (const PDFAction* currentAction : action->getActionList()) { Q_EMIT actionTriggered(currentAction); } } break; } case Direction::Backward: { if (const PDFAction* action = copy.getPreviousAction().get()) { for (const PDFAction* currentAction : action->getActionList()) { Q_EMIT actionTriggered(currentAction); } } break; } default: Q_ASSERT(false); break; } } PDFActionPtr PDFNavigationNode::getPreviousAction() const { return m_previousAction; } PDFActionPtr PDFNavigationNode::getNextAction() const { return m_nextAction; } PDFObjectReference PDFNavigationNode::getPreviousNode() const { return m_previousNode; } PDFObjectReference PDFNavigationNode::getNextNode() const { return m_nextNode; } PDFReal PDFNavigationNode::getDuration() const { return m_duration; } std::optional PDFNavigationNode::parse(const PDFObjectStorage* storage, PDFObject object) { std::optional result; if (const PDFDictionary* dictionary = storage->getDictionaryFromObject(object)) { PDFDocumentDataLoaderDecorator loader(storage); result = PDFNavigationNode(); result->m_nextAction = PDFAction::parse(storage, dictionary->get("NA")); result->m_previousAction = PDFAction::parse(storage, dictionary->get("PA")); result->m_previousNode = loader.readReferenceFromDictionary(dictionary, "Prev"); result->m_nextNode = loader.readReferenceFromDictionary(dictionary, "Next"); result->m_duration = loader.readNumberFromDictionary(dictionary, "Dur", 0.0); } return result; } } // namespace pdf