Object content viewer

This commit is contained in:
Jakub Melka 2021-06-13 18:54:47 +02:00
parent c0b0fb6010
commit f4ea513208
11 changed files with 283 additions and 6 deletions

View File

@ -307,7 +307,7 @@ using PDFColorComponentMatrix_3x3 = PDFColorComponentMatrix<3, 3>;
/// Represents PDF's color space (abstract class). Contains functions for parsing
/// color spaces.
class PDFAbstractColorSpace
class Pdf4QtLIBSHARED_EXPORT PDFAbstractColorSpace
{
public:
explicit PDFAbstractColorSpace() = default;

View File

@ -519,4 +519,19 @@ qint64 PDFDocumentWriter::getDocumentFileSize(const PDFDocument* document)
return -1;
}
QByteArray PDFDocumentWriter::getSerializedObject(const PDFObject& object)
{
QBuffer buffer;
if (buffer.open(QBuffer::WriteOnly))
{
PDFWriteObjectVisitor visitor(&buffer);
object.accept(&visitor);
buffer.close();
}
return buffer.data();
}
} // namespace pdf

View File

@ -63,6 +63,10 @@ public:
/// \param document Document
static qint64 getDocumentFileSize(const PDFDocument* document);
/// Writes an object to byte array, without object header/footer
/// \param object Object to be written
static QByteArray getSerializedObject(const PDFObject& object);
private:
void writeCRLF(QIODevice* device);
void writeObjectHeader(QIODevice* device, PDFObjectReference reference);

View File

@ -54,7 +54,7 @@ private:
bool m_defaultForPrinting = false;
};
class PDFImage
class Pdf4QtLIBSHARED_EXPORT PDFImage
{
public:

View File

@ -26,9 +26,10 @@
namespace pdfplugin
{
ObjectInspectorDialog::ObjectInspectorDialog(const pdf::PDFDocument* document, QWidget* parent) :
ObjectInspectorDialog::ObjectInspectorDialog(const pdf::PDFCMS* cms, const pdf::PDFDocument* document, QWidget* parent) :
QDialog(parent, Qt::Dialog | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint),
ui(new Ui::ObjectInspectorDialog),
m_cms(cms),
m_document(document),
m_model(nullptr)
{
@ -36,6 +37,9 @@ ObjectInspectorDialog::ObjectInspectorDialog(const pdf::PDFDocument* document, Q
m_objectClassifier.classify(document);
ui->currentObjectWidget->setCms(cms);
ui->currentObjectWidget->setDocument(document);
ui->modeComboBox->addItem(tr("Document"), int(PDFObjectInspectorTreeItemModel::Document));
ui->modeComboBox->addItem(tr("Pages"), int(PDFObjectInspectorTreeItemModel::Page));
@ -98,6 +102,8 @@ ObjectInspectorDialog::ObjectInspectorDialog(const pdf::PDFDocument* document, Q
ui->splitter->setCollapsible(1, true);
ui->splitter->setSizes(QList<int>() << pdf::PDFWidgetUtils::scaleDPI_x(this, 300) << pdf::PDFWidgetUtils::scaleDPI_x(this, 200));
connect(ui->objectTreeView->selectionModel(), &QItemSelectionModel::currentChanged, this, &ObjectInspectorDialog::onCurrentIndexChanged);
ui->objectTreeView->setMinimumWidth(pdf::PDFWidgetUtils::scaleDPI_x(this, 200));
setMinimumSize(pdf::PDFWidgetUtils::scaleDPI(this, QSize(800, 600)));
}
@ -113,4 +119,22 @@ void ObjectInspectorDialog::onModeChanged()
m_model->setMode(mode);
}
void ObjectInspectorDialog::onCurrentIndexChanged(const QModelIndex& current, const QModelIndex& previous)
{
Q_UNUSED(previous);
pdf::PDFObject object = m_model->getObjectFromIndex(current);
pdf::PDFObjectReference reference = m_model->getObjectReferenceFromIndex(current);
bool isRoot = m_model->isRootObject(current);
if (!isRoot && object.isReference())
{
reference = object.getReference();
object = m_document->getObjectByReference(reference);
isRoot = true;
}
ui->currentObjectWidget->setData(reference, qMove(object), isRoot);
}
} // namespace pdfplugin

View File

@ -20,6 +20,7 @@
#include "pdfdocument.h"
#include "pdfobjectutils.h"
#include "pdfcms.h"
#include <QDialog>
@ -37,13 +38,15 @@ class ObjectInspectorDialog : public QDialog
Q_OBJECT
public:
explicit ObjectInspectorDialog(const pdf::PDFDocument* document, QWidget* parent);
explicit ObjectInspectorDialog(const pdf::PDFCMS* cms, const pdf::PDFDocument* document, QWidget* parent);
virtual ~ObjectInspectorDialog() override;
private:
void onModeChanged();
void onCurrentIndexChanged(const QModelIndex& current, const QModelIndex& previous);
Ui::ObjectInspectorDialog* ui;
const pdf::PDFCMS* m_cms;
const pdf::PDFDocument* m_document;
pdf::PDFObjectClassifier m_objectClassifier;
PDFObjectInspectorTreeItemModel* m_model;

View File

@ -72,7 +72,8 @@ std::vector<QAction*> ObjectInspectorPlugin::getActions() const
void ObjectInspectorPlugin::onObjectInspectorTriggered()
{
ObjectInspectorDialog dialog(m_document, m_widget);
pdf::PDFCMSPointer cms = m_cmsManager->getCurrentCMS();
ObjectInspectorDialog dialog(cms.data(), m_document, m_widget);
dialog.exec();
}

View File

@ -18,6 +18,11 @@
#include "objectviewerwidget.h"
#include "ui_objectviewerwidget.h"
#include "pdfimage.h"
#include "pdfencoding.h"
#include "pdfdocumentwriter.h"
#include "pdfexception.h"
namespace pdfplugin
{
@ -31,6 +36,8 @@ ObjectViewerWidget::ObjectViewerWidget(bool isPinned, QWidget* parent) :
QWidget(parent),
ui(new Ui::ObjectViewerWidget),
m_isPinned(isPinned),
m_cms(nullptr),
m_document(nullptr),
m_isRootObject(false)
{
ui->setupUi(this);
@ -60,7 +67,7 @@ void ObjectViewerWidget::setData(pdf::PDFObjectReference currentReference, pdf::
{
if (m_currentReference != currentReference ||
m_currentObject != currentObject ||
m_isRootObject = isRootObject)
m_isRootObject != isRootObject)
{
m_currentReference = currentReference;
m_currentObject = currentObject;
@ -70,10 +77,193 @@ void ObjectViewerWidget::setData(pdf::PDFObjectReference currentReference, pdf::
}
}
void ObjectViewerWidget::updateUi()
{
if (m_currentReference.isValid())
{
QString referenceText = tr("%1 %2 R").arg(m_currentReference.objectNumber).arg(m_currentReference.generation);
if (m_isRootObject)
{
ui->referenceEdit->setText(referenceText);
}
else
{
ui->referenceEdit->setText(tr("Part of object %1").arg(referenceText));
}
}
else
{
ui->referenceEdit->setText(tr("<none>"));
}
switch (m_currentObject.getType())
{
case pdf::PDFObject::Type::Null:
ui->typeEdit->setText(tr("Null"));
break;
case pdf::PDFObject::Type::Bool:
ui->typeEdit->setText(tr("Bool"));
break;
case pdf::PDFObject::Type::Int:
ui->typeEdit->setText(tr("Integer"));
break;
case pdf::PDFObject::Type::Real:
ui->typeEdit->setText(tr("Real"));
break;
case pdf::PDFObject::Type::String:
ui->typeEdit->setText(tr("String"));
break;
case pdf::PDFObject::Type::Name:
ui->typeEdit->setText(tr("Name"));
break;
case pdf::PDFObject::Type::Array:
ui->typeEdit->setText(tr("Array"));
break;
case pdf::PDFObject::Type::Dictionary:
ui->typeEdit->setText(tr("Dictionary"));
break;
case pdf::PDFObject::Type::Stream:
ui->typeEdit->setText(tr("Stream"));
break;
case pdf::PDFObject::Type::Reference:
ui->typeEdit->setText(tr("Reference"));
break;
default:
Q_ASSERT(false);
break;
}
QLocale locale;
switch (m_currentObject.getType())
{
case pdf::PDFObject::Type::Null:
ui->descriptionEdit->setText(tr("null"));
break;
case pdf::PDFObject::Type::Bool:
ui->descriptionEdit->setText(m_currentObject.getBool() ? tr("true") : tr("false"));
break;
case pdf::PDFObject::Type::Int:
ui->descriptionEdit->setText(locale.toString(m_currentObject.getInteger()));
break;
case pdf::PDFObject::Type::Real:
ui->descriptionEdit->setText(locale.toString(m_currentObject.getReal()));
break;
case pdf::PDFObject::Type::String:
ui->descriptionEdit->setText(QString("\"%1\"").arg(pdf::PDFEncoding::convertSmartFromByteStringToRepresentableQString(m_currentObject.getString())));
break;
case pdf::PDFObject::Type::Name:
ui->descriptionEdit->setText(QString("/%1").arg(QString::fromLatin1(m_currentObject.getString().toPercentEncoding())));
break;
case pdf::PDFObject::Type::Array:
ui->descriptionEdit->setText(tr("Array [%1 items]").arg(locale.toString(m_currentObject.getArray()->getCount())));
break;
case pdf::PDFObject::Type::Dictionary:
ui->descriptionEdit->setText(tr("Dictionary [%1 items]").arg(locale.toString(m_currentObject.getDictionary()->getCount())));
break;
case pdf::PDFObject::Type::Stream:
ui->descriptionEdit->setText(tr("Stream [%1 items, %2 data bytes]").arg(locale.toString(m_currentObject.getStream()->getDictionary()->getCount()), locale.toString(m_currentObject.getStream()->getContent()->size())));
break;
case pdf::PDFObject::Type::Reference:
ui->descriptionEdit->setText(QString("%1 %2 R").arg(m_currentObject.getReference().objectNumber).arg(m_currentObject.getReference().generation));
break;
default:
Q_ASSERT(false);
break;
}
if (m_currentObject.isStream())
{
try
{
pdf::PDFDocumentDataLoaderDecorator loader(m_document);
const pdf::PDFStream* stream = m_currentObject.getStream();
const pdf::PDFDictionary* dictionary = stream->getDictionary();
if (loader.readNameFromDictionary(dictionary, "Type") == "XObject" &&
loader.readNameFromDictionary(dictionary, "Subtype") == "Image")
{
pdf::PDFColorSpacePointer colorSpace;
if (dictionary->hasKey("ColorSpace"))
{
const pdf::PDFObject& colorSpaceObject = m_document->getObject(dictionary->get("ColorSpace"));
if (colorSpaceObject.isName() || colorSpaceObject.isArray())
{
pdf::PDFDictionary dummyColorSpaceDictionary;
colorSpace = pdf::PDFAbstractColorSpace::createColorSpace(&dummyColorSpaceDictionary, m_document, colorSpaceObject);
}
else if (!colorSpaceObject.isNull())
{
throw pdf::PDFException(tr("Invalid color space of the image."));
}
}
pdf::PDFRenderErrorReporterDummy dummyErrorReporter;
pdf::PDFImage pdfImage = pdf::PDFImage::createImage(m_document, stream, qMove(colorSpace), false, pdf::RenderingIntent::Perceptual, &dummyErrorReporter);
QImage image = pdfImage.getImage(m_cms, &dummyErrorReporter);
ui->stackedWidget->setCurrentWidget(ui->imageBrowserPage);
ui->imageBrowser->setPixmap(QPixmap::fromImage(image));
}
else
{
QByteArray data = m_document->getDecodedStream(stream);
QByteArray percentEncodedData = data.toPercentEncoding(" \n");
ui->contentTextBrowser->setText(QString::fromLatin1(percentEncodedData));
ui->stackedWidget->setCurrentWidget(ui->contentTextBrowserPage);
}
}
catch (pdf::PDFException exception)
{
ui->contentTextBrowser->setText(exception.getMessage());
ui->stackedWidget->setCurrentWidget(ui->contentTextBrowserPage);
}
}
else
{
QByteArray serializedObject = pdf::PDFDocumentWriter::getSerializedObject(m_currentObject);
QByteArray percentEncodedData = serializedObject.toPercentEncoding(" \n");
ui->contentTextBrowser->setText(QString::fromLatin1(percentEncodedData));
ui->stackedWidget->setCurrentWidget(ui->contentTextBrowserPage);
}
}
void ObjectViewerWidget::updatePinnedUi()
{
ui->pinButton->setEnabled(!m_isPinned);
ui->unpinButton->setEnabled(m_isPinned);
}
const pdf::PDFCMS* ObjectViewerWidget::getCms() const
{
return m_cms;
}
void ObjectViewerWidget::setCms(const pdf::PDFCMS* cms)
{
m_cms = cms;
}
const pdf::PDFDocument* ObjectViewerWidget::getDocument() const
{
return m_document;
}
void ObjectViewerWidget::setDocument(const pdf::PDFDocument* document)
{
m_document = document;
}
} // namespace pdfplugin

View File

@ -19,6 +19,7 @@
#define OBJECTVIEWERWIDGET_H
#include "pdfdocument.h"
#include "pdfcms.h"
#include <QWidget>
@ -42,6 +43,12 @@ public:
void setPinned(bool isPinned);
void setData(pdf::PDFObjectReference currentReference, pdf::PDFObject currentObject, bool isRootObject);
const pdf::PDFDocument* getDocument() const;
void setDocument(const pdf::PDFDocument* document);
const pdf::PDFCMS* getCms() const;
void setCms(const pdf::PDFCMS* cms);
signals:
void pinRequest();
void unpinRequest();
@ -51,6 +58,8 @@ private:
void updatePinnedUi();
Ui::ObjectViewerWidget* ui;
const pdf::PDFCMS* m_cms;
const pdf::PDFDocument* m_document;
bool m_isPinned;
pdf::PDFObjectReference m_currentReference;

View File

@ -318,6 +318,33 @@ void PDFObjectInspectorTreeItemModel::setMode(Mode mode)
}
}
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:

View File

@ -60,6 +60,10 @@ public:
void setMode(Mode mode);
pdf::PDFObject getObjectFromIndex(const QModelIndex& index) const;
pdf::PDFObjectReference getObjectReferenceFromIndex(const QModelIndex& index) const;
bool isRootObject(const QModelIndex& index) const;
private:
void createObjectItem(PDFObjectInspectorTreeItem* parent,
pdf::PDFObjectReference reference,