diff --git a/PdfForQtLib/sources/pdfcatalog.h b/PdfForQtLib/sources/pdfcatalog.h index 74846cf..9460e46 100644 --- a/PdfForQtLib/sources/pdfcatalog.h +++ b/PdfForQtLib/sources/pdfcatalog.h @@ -628,6 +628,7 @@ public: bool isXFANeedsRendering() const { return m_xfaNeedsRendering; } const PDFObject& getAssociatedFiles() const { return m_associatedFiles; } const PDFObject& getDocumentPartRoot() const { return m_documentPartRoot; } + const std::map& getNamedDestinations() const { return m_namedDestinations; } /// Is document marked to have structure tree conforming to tagged document convention? bool isLogicalStructureMarked() const { return m_markInfoFlags.testFlag(MarkInfo_Marked); } diff --git a/PdfTool/PdfTool.pro b/PdfTool/PdfTool.pro index 6aa0ff6..06e0b6e 100644 --- a/PdfTool/PdfTool.pro +++ b/PdfTool/PdfTool.pro @@ -45,6 +45,7 @@ SOURCES += \ pdftoolattachments.cpp \ pdftoolinfo.cpp \ pdftoolinfojavascript.cpp \ + pdftoolinfonameddestinations.cpp \ pdftoolverifysignatures.cpp \ pdftoolxml.cpp @@ -63,5 +64,6 @@ HEADERS += \ pdftoolattachments.h \ pdftoolinfo.h \ pdftoolinfojavascript.h \ + pdftoolinfonameddestinations.h \ pdftoolverifysignatures.h \ pdftoolxml.h diff --git a/PdfTool/pdftoolabstractapplication.cpp b/PdfTool/pdftoolabstractapplication.cpp index 317c5f6..eb00bb0 100644 --- a/PdfTool/pdftoolabstractapplication.cpp +++ b/PdfTool/pdftoolabstractapplication.cpp @@ -401,7 +401,7 @@ PDFToolApplicationStorage* PDFToolApplicationStorage::getInstance() return &storage; } -std::vector PDFToolOptions::getPageRange(pdf::PDFInteger pageCount, QString& errorMessage) const +std::vector PDFToolOptions::getPageRange(pdf::PDFInteger pageCount, QString& errorMessage, bool zeroBased) const { QStringList parts; @@ -433,8 +433,15 @@ std::vector PDFToolOptions::getPageRange(pdf::PDFInteger pageCo } QString partsString = parts.join(","); - pdf::PDFClosedIntervalSet result = pdf::PDFClosedIntervalSet::parse(0, pageCount, partsString, &errorMessage); - return result.unfold(); + pdf::PDFClosedIntervalSet result = pdf::PDFClosedIntervalSet::parse(1, pageCount, partsString, &errorMessage); + std::vector pageIndices = result.unfold(); + + if (zeroBased) + { + std::for_each(pageIndices.begin(), pageIndices.end(), [](auto& index) { --index; }); + } + + return pageIndices; } } // pdftool diff --git a/PdfTool/pdftoolabstractapplication.h b/PdfTool/pdftoolabstractapplication.h index c163f1d..3a471ac 100644 --- a/PdfTool/pdftoolabstractapplication.h +++ b/PdfTool/pdftoolabstractapplication.h @@ -81,7 +81,10 @@ struct PDFToolOptions QString pageSelectorSelection; /// Returns page range. If page range is invalid, then \p errorMessage is empty. - std::vector getPageRange(pdf::PDFInteger pageCount, QString& errorMessage) const; + /// \param pageCount Page count + /// \param[out] errorMessage Error message + /// \param zeroBased Convert to zero based page range? + std::vector getPageRange(pdf::PDFInteger pageCount, QString& errorMessage, bool zeroBased) const; }; /// Base class for all applications diff --git a/PdfTool/pdftoolinfo.cpp b/PdfTool/pdftoolinfo.cpp index 7d553cf..e49e4d5 100644 --- a/PdfTool/pdftoolinfo.cpp +++ b/PdfTool/pdftoolinfo.cpp @@ -211,45 +211,50 @@ int PDFToolInfoApplication::execute(const PDFToolOptions& options) writeProperty("encryption", PDFToolTranslationContext::tr("Encryption"), modeString); writeProperty("authorized-as", PDFToolTranslationContext::tr("Authorization"), authorizationMode); + QStringList permissions; if (securityHandler->getAuthorizationResult() != pdf::PDFSecurityHandler::AuthorizationResult::NoAuthorizationRequired) { writeProperty("metadata-encrypted", PDFToolTranslationContext::tr("Metadata encrypted"), securityHandler->isMetadataEncrypted() ? PDFToolTranslationContext::tr("Yes") : PDFToolTranslationContext::tr("No")); writeProperty("version", PDFToolTranslationContext::tr("Version"), locale.toString(securityHandler->getVersion())); + + if (securityHandler->isAllowed(pdf::PDFSecurityHandler::Permission::PrintLowResolution)) + { + permissions << PDFToolTranslationContext::tr("Print (low resolution)"); + } + if (securityHandler->isAllowed(pdf::PDFSecurityHandler::Permission::PrintHighResolution)) + { + permissions << PDFToolTranslationContext::tr("Print"); + } + if (securityHandler->isAllowed(pdf::PDFSecurityHandler::Permission::CopyContent)) + { + permissions << PDFToolTranslationContext::tr("Copy content"); + } + if (securityHandler->isAllowed(pdf::PDFSecurityHandler::Permission::Accessibility)) + { + permissions << PDFToolTranslationContext::tr("Accessibility"); + } + if (securityHandler->isAllowed(pdf::PDFSecurityHandler::Permission::Assemble)) + { + permissions << PDFToolTranslationContext::tr("Page assembling"); + } + if (securityHandler->isAllowed(pdf::PDFSecurityHandler::Permission::Modify)) + { + permissions << PDFToolTranslationContext::tr("Modify content"); + } + if (securityHandler->isAllowed(pdf::PDFSecurityHandler::Permission::ModifyInteractiveItems)) + { + permissions << PDFToolTranslationContext::tr("Modify interactive items"); + } + if (securityHandler->isAllowed(pdf::PDFSecurityHandler::Permission::ModifyFormFields)) + { + permissions << PDFToolTranslationContext::tr("Form filling"); + } + } + else + { + permissions << PDFToolTranslationContext::tr("All"); } - QStringList permissions; - if (securityHandler->isAllowed(pdf::PDFSecurityHandler::Permission::PrintLowResolution)) - { - permissions << PDFToolTranslationContext::tr("Print (low resolution)"); - } - if (securityHandler->isAllowed(pdf::PDFSecurityHandler::Permission::PrintHighResolution)) - { - permissions << PDFToolTranslationContext::tr("Print"); - } - if (securityHandler->isAllowed(pdf::PDFSecurityHandler::Permission::CopyContent)) - { - permissions << PDFToolTranslationContext::tr("Copy content"); - } - if (securityHandler->isAllowed(pdf::PDFSecurityHandler::Permission::Accessibility)) - { - permissions << PDFToolTranslationContext::tr("Accessibility"); - } - if (securityHandler->isAllowed(pdf::PDFSecurityHandler::Permission::Assemble)) - { - permissions << PDFToolTranslationContext::tr("Page assembling"); - } - if (securityHandler->isAllowed(pdf::PDFSecurityHandler::Permission::Modify)) - { - permissions << PDFToolTranslationContext::tr("Modify content"); - } - if (securityHandler->isAllowed(pdf::PDFSecurityHandler::Permission::ModifyInteractiveItems)) - { - permissions << PDFToolTranslationContext::tr("Modify interactive items"); - } - if (securityHandler->isAllowed(pdf::PDFSecurityHandler::Permission::ModifyFormFields)) - { - permissions << PDFToolTranslationContext::tr("Form filling"); - } writeProperty("permissions", PDFToolTranslationContext::tr("Permissions"), permissions.join(", ")); formatter.endTable(); diff --git a/PdfTool/pdftoolinfojavascript.cpp b/PdfTool/pdftoolinfojavascript.cpp index ca36ebf..2d38f47 100644 --- a/PdfTool/pdftoolinfojavascript.cpp +++ b/PdfTool/pdftoolinfojavascript.cpp @@ -23,7 +23,7 @@ namespace pdftool static PDFToolInfoJavaScriptApplication s_infoJavaScriptApplication; -QString PDFToolInfoJavaScriptApplication::getStandardString(PDFToolAbstractApplication::StandardString standardString) const +QString PDFToolInfoJavaScriptApplication::getStandardString(StandardString standardString) const { switch (standardString) { @@ -54,7 +54,7 @@ int PDFToolInfoJavaScriptApplication::execute(const PDFToolOptions& options) } QString parseError; - std::vector pages = options.getPageRange(document.getCatalog()->getPageCount(), parseError); + std::vector pages = options.getPageRange(document.getCatalog()->getPageCount(), parseError, true); if (!parseError.isEmpty()) { @@ -116,8 +116,8 @@ int PDFToolInfoJavaScriptApplication::execute(const PDFToolOptions& options) formatter.writeTableColumn("no", locale.toString(ref), Qt::AlignRight); formatter.writeTableColumn("context", contextText); - formatter.writeTableColumn("page-number", (entry.pageIndex != -1) ? locale.toString(entry.pageIndex + 1) : QString()); - formatter.writeTableColumn("code-size", locale.toString(entry.javaScript.size())); + formatter.writeTableColumn("page-number", (entry.pageIndex != -1) ? locale.toString(entry.pageIndex + 1) : QString(), Qt::AlignRight); + formatter.writeTableColumn("code-size", locale.toString(entry.javaScript.size()), Qt::AlignRight); formatter.writeTableColumn("code-snippet", entry.javaScript.left(64)); formatter.endTableRow(); diff --git a/PdfTool/pdftoolinfonameddestinations.cpp b/PdfTool/pdftoolinfonameddestinations.cpp new file mode 100644 index 0000000..174e292 --- /dev/null +++ b/PdfTool/pdftoolinfonameddestinations.cpp @@ -0,0 +1,243 @@ +// Copyright (C) 2020 Jakub Melka +// +// This file is part of PdfForQt. +// +// PdfForQt 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 +// (at your option) any later version. +// +// PdfForQt 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 PDFForQt. If not, see . + +#include "pdftoolinfonameddestinations.h" +#include "pdfencoding.h" + +namespace pdftool +{ + +static PDFToolInfoNamedDestinationsApplication s_infoNamedDestinationsApplication; + +QString PDFToolInfoNamedDestinationsApplication::getStandardString(StandardString standardString) const +{ + switch (standardString) + { + case Command: + return "info-dests"; + + case Name: + return PDFToolTranslationContext::tr("Info (Named Destinations)"); + + case Description: + return PDFToolTranslationContext::tr("Retrieve informations about named destinations in a document."); + + default: + Q_ASSERT(false); + break; + } + + return QString(); +} + +int PDFToolInfoNamedDestinationsApplication::execute(const PDFToolOptions& options) +{ + pdf::PDFDocument document; + QByteArray sourceData; + if (!readDocument(options, document, &sourceData)) + { + return ErrorDocumentReading; + } + + QString parseError; + std::vector pages = options.getPageRange(document.getCatalog()->getPageCount(), parseError, false); + + if (!parseError.isEmpty()) + { + PDFConsole::writeError(parseError, options.outputCodec); + return ErrorInvalidArguments; + } + + PDFOutputFormatter formatter(options.outputStyle, options.outputCodec); + formatter.beginDocument("info-named-destinations", PDFToolTranslationContext::tr("Named destinations used in document %1").arg(options.document)); + formatter.endl(); + + formatter.beginTable("named-dests", PDFToolTranslationContext::tr("Named Destinations")); + + formatter.beginTableHeaderRow("header"); + formatter.writeTableHeaderColumn("no", PDFToolTranslationContext::tr("No."), Qt::AlignLeft); + formatter.writeTableHeaderColumn("page-number", PDFToolTranslationContext::tr("Page Number"), Qt::AlignLeft); + formatter.writeTableHeaderColumn("type", PDFToolTranslationContext::tr("Type"), Qt::AlignLeft); + formatter.writeTableHeaderColumn("left", PDFToolTranslationContext::tr("Left"), Qt::AlignLeft); + formatter.writeTableHeaderColumn("top", PDFToolTranslationContext::tr("Top"), Qt::AlignLeft); + formatter.writeTableHeaderColumn("right", PDFToolTranslationContext::tr("Right"), Qt::AlignLeft); + formatter.writeTableHeaderColumn("bottom", PDFToolTranslationContext::tr("Bottom"), Qt::AlignLeft); + formatter.writeTableHeaderColumn("zoom", PDFToolTranslationContext::tr("Zoom"), Qt::AlignLeft); + formatter.writeTableHeaderColumn("name", PDFToolTranslationContext::tr("Name"), Qt::AlignLeft); + formatter.endTableHeaderRow(); + + QLocale locale; + + struct DestinationItem + { + pdf::PDFInteger pageIndex = 0; + QString name; + pdf::PDFDestination destination; + }; + std::vector destinationItems; + + for (const auto& destinationItem : document.getCatalog()->getNamedDestinations()) + { + const QByteArray& name = destinationItem.first; + const pdf::PDFDestination& destination = destinationItem.second; + + if (destination.getDestinationType() == pdf::DestinationType::Invalid) + { + continue; + } + + // Search for page... + pdf::PDFInteger pageIndex = destination.getPageIndex(); + pdf::PDFObjectReference pageReference = destination.getPageReference(); + if (pageReference.isValid()) + { + pageIndex = document.getCatalog()->getPageIndexFromPageReference(pageReference); + } + + ++pageIndex; + if (!std::binary_search(pages.cbegin(), pages.cend(), pageIndex)) + { + // This page is skipped + continue; + } + + DestinationItem item; + item.pageIndex = pageIndex; + item.name = pdf::PDFEncoding::convertSmartFromByteStringToUnicode(name, nullptr); + item.destination = destination; + destinationItems.emplace_back(qMove(item)); + } + + std::stable_sort(destinationItems.begin(), destinationItems.end(), [](const auto& l, const auto& r) { return l.pageIndex < r.pageIndex; }); + + int ref = 1; + for (const auto& destinationItem : destinationItems) + { + QString type, left, top, right, bottom, zoom; + + const pdf::PDFDestination& destination = destinationItem.destination; + switch (destination.getDestinationType()) + { + case pdf::DestinationType::Invalid: + { + type = PDFToolTranslationContext::tr("Invalid"); + break; + } + + case pdf::DestinationType::Named: + { + type = PDFToolTranslationContext::tr("Named"); + break; + } + + case pdf::DestinationType::XYZ: + { + type = PDFToolTranslationContext::tr("XYZ"); + left = locale.toString(destination.getLeft()); + top = locale.toString(destination.getTop()); + if (!qFuzzyIsNull(destination.getZoom())) + { + zoom = locale.toString(destination.getZoom()); + } + break; + } + + case pdf::DestinationType::Fit: + { + type = PDFToolTranslationContext::tr("Fit"); + break; + } + + case pdf::DestinationType::FitH: + { + type = PDFToolTranslationContext::tr("FitH"); + top = locale.toString(destination.getTop()); + break; + } + + case pdf::DestinationType::FitV: + { + type = PDFToolTranslationContext::tr("FitV"); + left = locale.toString(destination.getLeft()); + break; + } + + case pdf::DestinationType::FitR: + { + type = PDFToolTranslationContext::tr("FitR"); + left = locale.toString(destination.getLeft()); + top = locale.toString(destination.getTop()); + bottom = locale.toString(destination.getBottom()); + right = locale.toString(destination.getRight()); + break; + } + + case pdf::DestinationType::FitB: + { + type = PDFToolTranslationContext::tr("FitB"); + break; + } + + case pdf::DestinationType::FitBH: + { + type = PDFToolTranslationContext::tr("FitBH"); + top = locale.toString(destination.getTop()); + break; + } + + case pdf::DestinationType::FitBV: + { + type = PDFToolTranslationContext::tr("FitBV"); + left = locale.toString(destination.getLeft()); + break; + } + + default: + Q_ASSERT(false); + break; + }; + + formatter.beginTableRow("destination", ref); + + formatter.writeTableColumn("no", locale.toString(ref), Qt::AlignRight); + formatter.writeTableColumn("page-number", locale.toString(destinationItem.pageIndex), Qt::AlignRight); + formatter.writeTableColumn("type", type, Qt::AlignRight); + formatter.writeTableColumn("left", left, Qt::AlignRight); + formatter.writeTableColumn("top", top, Qt::AlignRight); + formatter.writeTableColumn("right", right, Qt::AlignRight); + formatter.writeTableColumn("bottom", bottom, Qt::AlignRight); + formatter.writeTableColumn("zoom", zoom, Qt::AlignRight); + formatter.writeTableColumn("name", destinationItem.name); + + formatter.endTableRow(); + ++ref; + } + + formatter.endTable(); + + formatter.endDocument(); + PDFConsole::writeText(formatter.getString(), options.outputCodec); + + return ExitSuccess; +} + +PDFToolAbstractApplication::Options PDFToolInfoNamedDestinationsApplication::getOptionsFlags() const +{ + return ConsoleFormat | OpenDocument | PageSelector; +} + +} // namespace pdftool diff --git a/PdfTool/pdftoolinfonameddestinations.h b/PdfTool/pdftoolinfonameddestinations.h new file mode 100644 index 0000000..d3d42f2 --- /dev/null +++ b/PdfTool/pdftoolinfonameddestinations.h @@ -0,0 +1,36 @@ +// Copyright (C) 2020 Jakub Melka +// +// This file is part of PdfForQt. +// +// PdfForQt 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 +// (at your option) any later version. +// +// PdfForQt 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 PDFForQt. If not, see . + +#ifndef PDFTOOLINFONAMEDDESTINATIONS_H +#define PDFTOOLINFONAMEDDESTINATIONS_H + +#include "pdftoolabstractapplication.h" + +namespace pdftool +{ + +class PDFToolInfoNamedDestinationsApplication : public PDFToolAbstractApplication +{ +public: + virtual QString getStandardString(StandardString standardString) const override; + virtual int execute(const PDFToolOptions& options) override; + virtual Options getOptionsFlags() const override; +}; + +} // namespace pdftool + +#endif // PDFTOOLINFONAMEDDESTINATIONS_H