PDF4QT/Pdf4QtViewerPlugins/ObjectInspectorPlugin/pdfobjectinspectortreeitemmodel.cpp
2021-08-10 19:22:56 +02:00

505 lines
18 KiB
C++

// Copyright (C) 2021 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 "pdfobjectinspectortreeitemmodel.h"
#include "pdfdocument.h"
#include "pdfvisitor.h"
#include "pdfencoding.h"
#include <stack>
#include <QLocale>
namespace pdfplugin
{
class PDFObjectInspectorTreeItem : public pdf::PDFTreeItem
{
public:
inline explicit PDFObjectInspectorTreeItem() = default;
inline explicit PDFObjectInspectorTreeItem(PDFObjectInspectorTreeItem* parent) : pdf::PDFTreeItem(parent) { }
inline explicit PDFObjectInspectorTreeItem(pdf::PDFObject object, PDFObjectInspectorTreeItem* parent) : pdf::PDFTreeItem(parent), m_object(std::move(object)) { }
inline explicit PDFObjectInspectorTreeItem(QByteArray dictionaryKey, pdf::PDFObject object, PDFObjectInspectorTreeItem* parent) : pdf::PDFTreeItem(parent), m_dictionaryKey(std::move(dictionaryKey)), m_object(std::move(object)) { }
inline explicit PDFObjectInspectorTreeItem(pdf::PDFObjectReference reference, pdf::PDFObject object, PDFObjectInspectorTreeItem* parent) : pdf::PDFTreeItem(parent), m_reference(std::move(reference)), m_object(std::move(object)) { }
virtual ~PDFObjectInspectorTreeItem() override { }
QByteArray getDictionaryKey() const;
void setDictionaryKey(const QByteArray& dictionaryKey);
pdf::PDFObjectReference getReference() const;
void setReference(const pdf::PDFObjectReference& reference);
const pdf::PDFObject& getObject() const;
void setObject(const pdf::PDFObject& object);
private:
QByteArray m_dictionaryKey;
pdf::PDFObjectReference m_reference;
pdf::PDFObject m_object;
};
QByteArray PDFObjectInspectorTreeItem::getDictionaryKey() const
{
return m_dictionaryKey;
}
void PDFObjectInspectorTreeItem::setDictionaryKey(const QByteArray& dictionaryKey)
{
m_dictionaryKey = dictionaryKey;
}
pdf::PDFObjectReference PDFObjectInspectorTreeItem::getReference() const
{
return m_reference;
}
void PDFObjectInspectorTreeItem::setReference(const pdf::PDFObjectReference& reference)
{
m_reference = reference;
}
const pdf::PDFObject& PDFObjectInspectorTreeItem::getObject() const
{
return m_object;
}
void PDFObjectInspectorTreeItem::setObject(const pdf::PDFObject& object)
{
m_object = object;
}
PDFObjectInspectorTreeItemModel::PDFObjectInspectorTreeItemModel(const pdf::PDFObjectClassifier* classifier, QObject* parent) :
pdf::PDFTreeItemModel(parent),
m_classifier(classifier)
{
}
QVariant PDFObjectInspectorTreeItemModel::headerData(int section, Qt::Orientation orientation, int role) const
{
Q_UNUSED(section);
Q_UNUSED(orientation);
Q_UNUSED(role);
return QVariant();
}
int PDFObjectInspectorTreeItemModel::columnCount(const QModelIndex& parent) const
{
Q_UNUSED(parent);
return 1;
}
QVariant PDFObjectInspectorTreeItemModel::data(const QModelIndex& index, int role) const
{
if (!index.isValid())
{
return QVariant();
}
if (role != Qt::DisplayRole)
{
return QVariant();
}
const PDFObjectInspectorTreeItem* item = static_cast<const PDFObjectInspectorTreeItem*>(index.internalPointer());
const PDFObjectInspectorTreeItem* parent = static_cast<const PDFObjectInspectorTreeItem*>(index.parent().internalPointer());
QStringList data;
if (item->getReference().isValid() && (!parent || (parent && !parent->getReference().isValid())))
{
data << QString("%1 %2 R").arg(item->getReference().objectNumber).arg(item->getReference().generation);
}
QByteArray dictionaryKey = item->getDictionaryKey();
if (!dictionaryKey.isEmpty())
{
data << QString("/%1").arg(QString::fromLatin1(dictionaryKey.toPercentEncoding()));
}
QLocale locale;
const pdf::PDFObject& object = item->getObject();
switch (object.getType())
{
case pdf::PDFObject::Type::Null:
data << tr("null");
break;
case pdf::PDFObject::Type::Bool:
data << (object.getBool() ? tr("true") : tr("false"));
break;
case pdf::PDFObject::Type::Int:
data << locale.toString(object.getInteger());
break;
case pdf::PDFObject::Type::Real:
data << locale.toString(object.getReal());
break;
case pdf::PDFObject::Type::String:
data << QString("\"%1\"").arg(pdf::PDFEncoding::convertSmartFromByteStringToRepresentableQString(object.getString()));
break;
case pdf::PDFObject::Type::Name:
data << QString("/%1").arg(QString::fromLatin1(object.getString().toPercentEncoding()));
break;
case pdf::PDFObject::Type::Array:
data << tr("Array [%1 items]").arg(locale.toString(object.getArray()->getCount()));
break;
case pdf::PDFObject::Type::Dictionary:
data << tr("Dictionary [%1 items]").arg(locale.toString(object.getDictionary()->getCount()));
break;
case pdf::PDFObject::Type::Stream:
data << tr("Stream [%1 items, %2 data bytes]").arg(locale.toString(object.getStream()->getDictionary()->getCount()), locale.toString(object.getStream()->getContent()->size()));
break;
case pdf::PDFObject::Type::Reference:
data << QString("%1 %2 R").arg(object.getReference().objectNumber).arg(object.getReference().generation);
break;
default:
Q_ASSERT(false);
break;
}
return data.join(" ");
}
void PDFObjectInspectorTreeItemModel::update()
{
beginResetModel();
m_rootItem.reset();
if (m_document)
{
std::set<pdf::PDFObjectReference> usedReferences;
auto createObjectsFromClassifier = [this, &usedReferences](pdf::PDFObjectClassifier::Type type)
{
m_rootItem.reset(new PDFObjectInspectorTreeItem());
for (pdf::PDFObjectReference reference : m_classifier->getObjectsByType(type))
{
pdf::PDFObject object = m_document->getStorage().getObjectByReference(reference);
createObjectItem(getRootItem(), reference, object, true, usedReferences);
}
};
switch (m_mode)
{
case pdfplugin::PDFObjectInspectorTreeItemModel::Document:
{
m_rootItem.reset(new PDFObjectInspectorTreeItem());
const pdf::PDFObjectStorage& storage = m_document->getStorage();
createObjectItem(getRootItem(), pdf::PDFObjectReference(), storage.getTrailerDictionary(), true, usedReferences);
break;
}
case pdfplugin::PDFObjectInspectorTreeItemModel::Page:
{
m_rootItem.reset(new PDFObjectInspectorTreeItem());
const size_t pageCount = m_document->getCatalog()->getPageCount();
for (size_t i = 0; i < pageCount; ++i)
{
if (const pdf::PDFPage* page = m_document->getCatalog()->getPage(i))
{
pdf::PDFObjectReference reference = page->getPageReference();
pdf::PDFObject object = m_document->getStorage().getObjectByReference(reference);
createObjectItem(getRootItem(), reference, object, true, usedReferences);
}
}
break;
}
case ContentStream:
createObjectsFromClassifier(pdf::PDFObjectClassifier::ContentStream);
break;
case GraphicState:
createObjectsFromClassifier(pdf::PDFObjectClassifier::GraphicState);
break;
case ColorSpace:
createObjectsFromClassifier(pdf::PDFObjectClassifier::ColorSpace);
break;
case Pattern:
createObjectsFromClassifier(pdf::PDFObjectClassifier::Pattern);
break;
case Shading:
createObjectsFromClassifier(pdf::PDFObjectClassifier::Shading);
break;
case Image:
createObjectsFromClassifier(pdf::PDFObjectClassifier::Image);
break;
case Form:
createObjectsFromClassifier(pdf::PDFObjectClassifier::Form);
break;
case Font:
createObjectsFromClassifier(pdf::PDFObjectClassifier::Font);
break;
case Action:
createObjectsFromClassifier(pdf::PDFObjectClassifier::Action);
break;
case Annotation:
createObjectsFromClassifier(pdf::PDFObjectClassifier::Annotation);
break;
case pdfplugin::PDFObjectInspectorTreeItemModel::List:
{
m_rootItem.reset(new PDFObjectInspectorTreeItem());
const pdf::PDFObjectStorage& storage = m_document->getStorage();
createObjectItem(getRootItem(), pdf::PDFObjectReference(), storage.getTrailerDictionary(), false, usedReferences);
const pdf::PDFObjectStorage::PDFObjects& objects = storage.getObjects();
for (size_t i = 0; i < objects.size(); ++i)
{
pdf::PDFObjectReference reference(i, objects[i].generation);
pdf::PDFObject object = objects[i].object;
if (object.isNull())
{
// We skip null objects
continue;
}
createObjectItem(getRootItem(), reference, object, false, usedReferences);
}
break;
}
default:
Q_ASSERT(false);
break;
}
}
endResetModel();
}
void PDFObjectInspectorTreeItemModel::setMode(Mode mode)
{
if (m_mode != mode)
{
m_mode = mode;
update();
}
}
pdf::PDFObject PDFObjectInspectorTreeItemModel::getObjectFromIndex(const QModelIndex& index) const
{
if (!index.isValid())
{
return pdf::PDFObject();
}
const PDFObjectInspectorTreeItem* item = static_cast<const PDFObjectInspectorTreeItem*>(index.internalPointer());
return item->getObject();
}
pdf::PDFObjectReference PDFObjectInspectorTreeItemModel::getObjectReferenceFromIndex(const QModelIndex& index) const
{
if (!index.isValid())
{
return pdf::PDFObjectReference();
}
const PDFObjectInspectorTreeItem* item = static_cast<const PDFObjectInspectorTreeItem*>(index.internalPointer());
return item->getReference();
}
bool PDFObjectInspectorTreeItemModel::isRootObject(const QModelIndex& index) const
{
return index.isValid() && !index.parent().isValid();
}
class PDFCreateObjectInspectorTreeItemFromObjectVisitor : public pdf::PDFAbstractVisitor
{
public:
explicit PDFCreateObjectInspectorTreeItemFromObjectVisitor(std::set<pdf::PDFObjectReference>* usedReferences,
const pdf::PDFDocument* document,
bool followReferences,
pdf::PDFObjectReference reference,
PDFObjectInspectorTreeItem* parent) :
m_usedReferences(usedReferences),
m_document(document),
m_followReferences(followReferences),
m_reference(reference)
{
m_parents.push(parent);
}
virtual ~PDFCreateObjectInspectorTreeItemFromObjectVisitor() override
{
m_parents.pop();
Q_ASSERT(m_parents.empty());
}
virtual void visitNull() override;
virtual void visitBool(bool value) override;
virtual void visitInt(pdf::PDFInteger value) override;
virtual void visitReal(pdf::PDFReal value) override;
virtual void visitString(pdf::PDFStringRef string) override;
virtual void visitName(pdf::PDFStringRef name) override;
virtual void visitArray(const pdf::PDFArray* array) override;
virtual void visitDictionary(const pdf::PDFDictionary* dictionary) override;
virtual void visitStream(const pdf::PDFStream* stream) override;
virtual void visitReference(const pdf::PDFObjectReference reference) override;
private:
std::set<pdf::PDFObjectReference>* m_usedReferences;
const pdf::PDFDocument* m_document;
bool m_followReferences;
pdf::PDFObjectReference m_reference;
std::stack<PDFObjectInspectorTreeItem*> m_parents;
};
void PDFCreateObjectInspectorTreeItemFromObjectVisitor::visitNull()
{
m_parents.top()->addCreatedChild(new PDFObjectInspectorTreeItem(m_reference, pdf::PDFObject::createNull(), m_parents.top()));
}
void PDFCreateObjectInspectorTreeItemFromObjectVisitor::visitBool(bool value)
{
m_parents.top()->addCreatedChild(new PDFObjectInspectorTreeItem(m_reference, pdf::PDFObject::createBool(value), m_parents.top()));
}
void PDFCreateObjectInspectorTreeItemFromObjectVisitor::visitInt(pdf::PDFInteger value)
{
m_parents.top()->addCreatedChild(new PDFObjectInspectorTreeItem(m_reference, pdf::PDFObject::createInteger(value), m_parents.top()));
}
void PDFCreateObjectInspectorTreeItemFromObjectVisitor::visitReal(pdf::PDFReal value)
{
m_parents.top()->addCreatedChild(new PDFObjectInspectorTreeItem(m_reference, pdf::PDFObject::createReal(value), m_parents.top()));
}
void PDFCreateObjectInspectorTreeItemFromObjectVisitor::visitString(pdf::PDFStringRef string)
{
m_parents.top()->addCreatedChild(new PDFObjectInspectorTreeItem(m_reference, pdf::PDFObject::createString(string.getString()), m_parents.top()));
}
void PDFCreateObjectInspectorTreeItemFromObjectVisitor::visitName(pdf::PDFStringRef name)
{
m_parents.top()->addCreatedChild(new PDFObjectInspectorTreeItem(m_reference, pdf::PDFObject::createName(name), m_parents.top()));
}
void PDFCreateObjectInspectorTreeItemFromObjectVisitor::visitArray(const pdf::PDFArray* array)
{
PDFObjectInspectorTreeItem* arrayRoot = new PDFObjectInspectorTreeItem(m_reference, pdf::PDFObject::createArray(std::make_shared<pdf::PDFArray>(*array)), m_parents.top());
m_parents.top()->addCreatedChild(arrayRoot);
m_parents.push(arrayRoot);
acceptArray(array);
m_parents.pop();
}
void PDFCreateObjectInspectorTreeItemFromObjectVisitor::visitDictionary(const pdf::PDFDictionary* dictionary)
{
PDFObjectInspectorTreeItem* dictionaryRoot = new PDFObjectInspectorTreeItem(m_reference, pdf::PDFObject::createDictionary(std::make_shared<pdf::PDFDictionary>(*dictionary)), m_parents.top());
m_parents.top()->addCreatedChild(dictionaryRoot);
m_parents.push(dictionaryRoot);
acceptDictionary(dictionary);
Q_ASSERT(dictionaryRoot->getChildCount() == dictionary->getCount());
for (size_t i = 0, count = dictionary->getCount(); i < count; ++i)
{
PDFObjectInspectorTreeItem* child = static_cast<PDFObjectInspectorTreeItem*>(dictionaryRoot->getChild(int(i)));
child->setDictionaryKey(dictionary->getKey(i).getString());
}
m_parents.pop();
}
void PDFCreateObjectInspectorTreeItemFromObjectVisitor::visitStream(const pdf::PDFStream* stream)
{
PDFObjectInspectorTreeItem* streamRoot = new PDFObjectInspectorTreeItem(m_reference, pdf::PDFObject::createStream(std::make_shared<pdf::PDFStream>(*stream)), m_parents.top());
m_parents.top()->addCreatedChild(streamRoot);
m_parents.push(streamRoot);
const pdf::PDFDictionary* dictionary = stream->getDictionary();
acceptDictionary(dictionary);
Q_ASSERT(streamRoot->getChildCount() == dictionary->getCount());
for (size_t i = 0, count = dictionary->getCount(); i < count; ++i)
{
PDFObjectInspectorTreeItem* child = static_cast<PDFObjectInspectorTreeItem*>(streamRoot->getChild(int(i)));
child->setDictionaryKey(dictionary->getKey(i).getString());
}
m_parents.pop();
}
void PDFCreateObjectInspectorTreeItemFromObjectVisitor::visitReference(const pdf::PDFObjectReference reference)
{
PDFObjectInspectorTreeItem* referenceRoot = new PDFObjectInspectorTreeItem(m_reference, pdf::PDFObject::createReference(reference), m_parents.top());
m_parents.top()->addCreatedChild(referenceRoot);
if (m_followReferences && reference.isValid())
{
Q_ASSERT(m_usedReferences);
if (m_usedReferences->count(reference))
{
// Reference already followed
return;
}
m_usedReferences->insert(reference);
m_parents.push(referenceRoot);
const pdf::PDFObject& object = m_document->getObjectByReference(reference);
object.accept(this);
m_parents.pop();
}
}
void PDFObjectInspectorTreeItemModel::createObjectItem(PDFObjectInspectorTreeItem* parent,
pdf::PDFObjectReference reference,
pdf::PDFObject object,
bool followRef,
std::set<pdf::PDFObjectReference>& usedReferences) const
{
PDFCreateObjectInspectorTreeItemFromObjectVisitor visitor(&usedReferences, m_document, followRef, reference, parent);
object.accept(&visitor);
}
PDFObjectInspectorTreeItem* PDFObjectInspectorTreeItemModel::getRootItem()
{
return static_cast<PDFObjectInspectorTreeItem*>(m_rootItem.get());
}
} // namespace pdfplugin