Issue #159: Renaming applications

This commit is contained in:
Jakub Melka
2024-03-16 17:02:44 +01:00
parent a70f45c8a9
commit 28807b4f12
450 changed files with 1440 additions and 1416 deletions

View File

@@ -0,0 +1,40 @@
# Copyright (C) 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 <https://www.gnu.org/licenses/>.
add_library(ObjectInspectorPlugin SHARED
objectinspectordialog.cpp
objectinspectorplugin.cpp
objectstatisticsdialog.cpp
objectviewerwidget.cpp
pdfobjectinspectortreeitemmodel.cpp
statisticsgraphwidget.cpp
objectinspectordialog.ui
objectstatisticsdialog.ui
objectviewerwidget.ui
statisticsgraphwidget.ui
icons.qrc
)
target_link_libraries(ObjectInspectorPlugin PRIVATE Pdf4QtLibCore Pdf4QtLibWidgets Qt6::Core Qt6::Gui Qt6::Widgets)
set_target_properties(ObjectInspectorPlugin PROPERTIES
VERSION ${PDF4QT_VERSION}
SOVERSION ${PDF4QT_VERSION}
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${PDF4QT_PLUGINS_DIR}
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${PDF4QT_PLUGINS_DIR})
install(TARGETS ObjectInspectorPlugin RUNTIME DESTINATION ${PDF4QT_PLUGINS_DIR} LIBRARY DESTINATION ${PDF4QT_PLUGINS_DIR})

View File

@@ -0,0 +1,7 @@
{
"Name" : "ObjectInspector",
"Author" : "Jakub Melka",
"Version" : "1.0.0",
"License" : "LGPL v3",
"Description" : "Explore internal structure of a document. View decompressed streams and images. Modify objects directly."
}

View File

@@ -0,0 +1,6 @@
<RCC>
<qresource prefix="/pdfplugins/objectinspector">
<file>object-inspector.svg</file>
<file>object-statistics.svg</file>
</qresource>
</RCC>

View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Vrstva_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<path fill-rule="evenodd" clip-rule="evenodd" fill="#292D32" d="M2.956,12.744c-0.24,0.508-0.247,0.919-0.178,1.217
c0.016,0.056,0.029,0.113,0.048,0.166c0.013,0.032,0.025,0.071,0.039,0.103c0.418,0.888,1.606,1.187,2.397,0.492
c0.079-0.079,0.142-0.048,0.205,0c0.395,0.268,0.806,0.521,1.199,0.788c0.172,0.093,0.33,0.202,0.503,0.329
c-0.031,0.078-0.063,0.142-0.094,0.22c-0.649,1.4-0.316,2.998,0.671,4.062c1.272,1.165,2.513,1.254,3.348,1.115
c0.155-0.028,0.309-0.058,0.455-0.101c0.033-0.013,0.069-0.02,0.099-0.03c0.669-0.221,1.246-0.617,1.697-1.2
c0.031-0.03,0.048-0.062,0.063-0.096c0.236-0.313,0.41-0.676,0.63-1.039h0.016c0.427,0.096,0.852,0.189,1.261,0.299
c0.048,0.017,0.096,0.112,0.096,0.175c0.094,0.929,0.849,1.543,1.78,1.417c0.992-0.125,1.607-1.245,1.182-2.158
c-0.268-0.551-0.726-0.884-1.356-0.897c-0.613-0.034-1.07,0.253-1.386,0.754c-0.063,0.113-0.109,0.128-0.22,0.096
c-0.237-0.062-0.475-0.109-0.727-0.174c-0.173-0.048-0.361-0.094-0.551-0.142c0.142-1.182-0.188-2.206-0.96-3.088
c0.41-0.442,0.803-0.881,1.212-1.323c1.183,0.836,2.443,0.977,3.798,0.442c0.172,0.299,0.331,0.6,0.505,0.881
c0.046,0.096,0.03,0.158-0.048,0.236c-0.079,0.096-0.158,0.189-0.22,0.301c-0.41,0.662-0.222,1.56,0.44,2.03
c0.615,0.442,1.544,0.333,2.033-0.235c0.075-0.087,0.126-0.188,0.182-0.285c0.22-0.418,0.199-0.876,0.169-1.082
c-0.104-0.454-0.355-0.803-0.776-1.026c-0.379-0.206-0.773-0.237-1.183-0.11c-0.188-0.347-0.377-0.679-0.567-1.009
c2.192-1.465,2.253-4.885-0.188-6.319c0.283-0.599,0.566-1.197,0.85-1.781c0.693,0.111,1.229-0.078,1.606-0.598
c0.043-0.061,0.069-0.134,0.102-0.204c0.351-0.752,0.035-1.395-0.163-1.682c-0.238-0.325-0.605-0.498-1.011-0.572h-0.359
c0,0-0.019,0.015-0.033,0.015c-0.647,0.126-1.087,0.505-1.262,1.15c-0.155,0.6,0.019,1.119,0.474,1.528
c0.08,0.079,0.064,0.142,0.032,0.205c-0.22,0.441-0.427,0.867-0.63,1.292c-0.049,0.125-0.11,0.252-0.158,0.362
c-2.002-0.567-3.529,0.031-4.633,1.764c-1.118-0.473-2.252-0.961-3.387-1.45c0.221-0.708,0.236-1.418,0.046-2.142
c0.331-0.126,0.664-0.252,0.994-0.363c0.031,0.047,0.064,0.08,0.094,0.127c0.52,0.677,1.496,0.803,2.144,0.283
c0.709-0.567,0.817-1.528,0.266-2.221c-0.266-0.315-0.63-0.473-1.023-0.551h-0.346c-0.899,0.125-1.45,0.914-1.372,1.685
c0.016,0.048-0.032,0.111-0.063,0.126c-0.3,0.11-0.599,0.221-0.898,0.331C9.769,4.81,9.737,4.778,9.721,4.73
c-0.394-0.756-0.992-1.34-1.78-1.686C7.784,2.966,7.484,2.856,7.105,2.792C6.239,2.651,4.916,2.715,3.639,3.975
C3.553,4.07,3.484,4.186,3.408,4.294c-0.105,0.155-0.2,0.31-0.277,0.464c-0.032,0.063-0.062,0.129-0.09,0.196
C2.674,5.815,2.7,6.641,2.805,7.208c0.397,1.828,2.1,3.078,3.892,2.912c0.22-0.016,0.457-0.047,0.694-0.094
c0.503,1.402,1.007,2.79,1.513,4.192c-0.553,0.267-1.01,0.63-1.386,1.101c-0.569-0.377-1.121-0.738-1.687-1.101
c-0.111-0.065-0.125-0.144-0.096-0.254c0.189-0.629-0.078-1.339-0.629-1.686c-0.552-0.378-1.292-0.348-1.812,0.063
C3.144,12.457,3.044,12.598,2.956,12.744z M11.375,4.258c0-0.503,0.426-0.93,0.93-0.93c0.504,0,0.931,0.426,0.931,0.93
c0,0.52-0.411,0.93-0.931,0.93C11.802,5.188,11.393,4.778,11.375,4.258z M18.828,4.258c0-0.503,0.41-0.93,0.914-0.93
c0.506,0,0.945,0.426,0.93,0.93c0,0.504-0.409,0.93-0.93,0.93C19.238,5.188,18.828,4.778,18.828,4.258z M18.828,16.359
c0-0.521,0.41-0.929,0.914-0.929c0.506-0.017,0.93,0.395,0.93,0.896c0.016,0.521-0.409,0.948-0.914,0.948
C19.238,17.274,18.828,16.864,18.828,16.359z M16.04,19.147c-0.017-0.521,0.41-0.943,0.931-0.929c0.502,0,0.913,0.407,0.913,0.929
c0,0.504-0.426,0.929-0.929,0.929C16.45,20.062,16.04,19.651,16.04,19.147z M9.486,13.994c-0.504-1.4-1.009-2.787-1.514-4.189
c0.757-0.362,1.34-0.898,1.734-1.638c1.149,0.488,2.268,0.96,3.402,1.434c-0.392,1.386-0.126,2.615,0.819,3.702
c-0.393,0.426-0.772,0.851-1.149,1.262c-0.017,0.031-0.048,0.045-0.08,0.079c-0.454-0.349-0.959-0.583-1.512-0.695
C10.62,13.823,10.052,13.87,9.486,13.994z M3.325,13.569c0-0.503,0.41-0.929,0.914-0.944c0.52,0,0.945,0.424,0.929,0.944
c0,0.505-0.409,0.915-0.914,0.915C3.735,14.484,3.325,14.074,3.325,13.569z"/>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Vrstva_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="24px" height="24px" viewBox="0 0 24 24" enable-background="new 0 0 24 24" xml:space="preserve">
<g>
<path fill="#292D32" d="M8.215,20.134c0-0.603,0-1.19,0-1.778c0-0.674-0.002-1.344,0.001-2.016
c0.001-0.365,0.221-0.589,0.584-0.591c0.686-0.002,1.37-0.001,2.055,0c0.388,0.002,0.605,0.218,0.605,0.602
c0.002,1.186,0.001,2.37,0.001,3.55c0,0.072,0,0.146,0,0.229c0.354,0,0.706,0,1.08,0c0-0.066,0-0.135,0-0.206
c0-2.607,0-5.212,0-7.819c0-0.492,0.189-0.683,0.688-0.683c0.637,0,1.273,0,1.908,0c0.445,0.001,0.647,0.205,0.647,0.657
c0,2.608,0,5.222,0,7.834c0,0.07,0,0.142,0,0.224c0.362,0,0.704,0,1.08,0c0-0.076,0-0.153,0-0.225c0-3.692,0-7.387,0-11.082
c0-0.351,0.137-0.565,0.414-0.631c0.076-0.016,0.156-0.016,0.233-0.016c0.65-0.001,1.298-0.001,1.945-0.001
c0.446,0.001,0.651,0.202,0.651,0.648c0,3.693,0,7.39,0,11.082c0,0.068,0,0.141,0,0.233c0.163,0,0.318,0,0.47,0
c0.398,0,0.649,0.214,0.646,0.547c-0.003,0.323-0.25,0.533-0.638,0.533c-1.992,0-3.987,0-5.983,0c-3.728,0-7.458,0-11.187,0.001
c-0.264,0-0.475-0.073-0.592-0.322c-0.163-0.346,0.084-0.739,0.478-0.757c0.19-0.009,0.381-0.002,0.589-0.002
c0-0.091,0-0.161,0-0.23c0-1.885,0-3.771,0-5.657c0-0.479,0.191-0.672,0.679-0.672c0.636,0,1.274,0,1.909,0
c0.457,0.002,0.655,0.201,0.655,0.661c0,1.887,0,3.774,0,5.657c0,0.072,0,0.143,0,0.227C7.495,20.134,7.844,20.134,8.215,20.134z"
/>
<path fill="#292D32" d="M19.343,3.03c-0.6-0.352-1.213-0.352-1.779,0.048c-0.574,0.401-0.793,0.975-0.652,1.67
c0.021,0.096,0.013,0.149-0.067,0.211c-0.559,0.415-1.118,0.833-1.671,1.253c-0.087,0.065-0.146,0.056-0.241,0.017
c-0.075-0.032-0.153-0.06-0.229-0.087c-0.176-0.057-0.542-0.139-0.933-0.054c-0.118,0.03-0.23,0.065-0.337,0.119
c-0.038,0.021-0.079,0.043-0.112,0.063c-0.016,0.011-0.034,0.021-0.045,0.028c-0.646,0.416-0.89,1.175-0.625,1.924
c0.018,0.054,0.009,0.15-0.028,0.186c-0.669,0.681-1.345,1.355-2.025,2.026c-0.03,0.033-0.112,0.05-0.153,0.036
c-0.604-0.231-1.151-0.128-1.655,0.266c-0.039,0.032-0.127,0.036-0.173,0.013c-0.469-0.228-0.934-0.461-1.401-0.7
c-0.043-0.022-0.086-0.097-0.086-0.148c0.011-0.574-0.202-1.039-0.653-1.392C5.964,8.106,5.189,8.084,4.62,8.453
c-0.563,0.369-0.854,1.118-0.676,1.749c0.332,1.162,1.619,1.59,2.582,0.853c0.084-0.064,0.147-0.068,0.238-0.023
c0.442,0.229,0.89,0.453,1.334,0.67c0.096,0.045,0.127,0.094,0.124,0.205c-0.031,0.655,0.344,1.253,0.958,1.537
c0.571,0.27,1.279,0.146,1.773-0.309c0.457-0.416,0.625-1.121,0.398-1.718c-0.043-0.114-0.023-0.182,0.059-0.262
c0.65-0.642,1.294-1.29,1.937-1.937c0.083-0.084,0.146-0.105,0.264-0.063c0.615,0.22,1.18,0.104,1.654-0.337
c0.438-0.409,0.601-0.929,0.469-1.521c-0.025-0.111,0.001-0.161,0.085-0.224c0.555-0.409,1.105-0.823,1.657-1.241
c0.083-0.064,0.144-0.069,0.237-0.015c1.009,0.545,2.221-0.076,2.372-1.219C20.168,3.981,19.863,3.333,19.343,3.03z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -0,0 +1,173 @@
// 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 "objectinspectordialog.h"
#include "ui_objectinspectordialog.h"
#include "pdfwidgetutils.h"
#include "pdfobjectinspectortreeitemmodel.h"
#include <QSplitter>
namespace pdfplugin
{
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),
m_viewerWidget(new ObjectViewerWidget(this))
{
ui->setupUi(this);
m_objectClassifier.classify(document);
m_viewerWidget->setCms(cms);
m_viewerWidget->setDocument(document);
ui->currentObjectTabLayout->addWidget(m_viewerWidget);
ui->modeComboBox->addItem(tr("Document"), int(PDFObjectInspectorTreeItemModel::Document));
ui->modeComboBox->addItem(tr("Pages"), int(PDFObjectInspectorTreeItemModel::Page));
if (m_objectClassifier.hasType(pdf::PDFObjectClassifier::ContentStream))
{
ui->modeComboBox->addItem(tr("Content streams"), int(PDFObjectInspectorTreeItemModel::ContentStream));
}
if (m_objectClassifier.hasType(pdf::PDFObjectClassifier::GraphicState))
{
ui->modeComboBox->addItem(tr("Graphic states"), int(PDFObjectInspectorTreeItemModel::GraphicState));
}
if (m_objectClassifier.hasType(pdf::PDFObjectClassifier::ColorSpace))
{
ui->modeComboBox->addItem(tr("Color spaces"), int(PDFObjectInspectorTreeItemModel::ColorSpace));
}
if (m_objectClassifier.hasType(pdf::PDFObjectClassifier::Pattern))
{
ui->modeComboBox->addItem(tr("Patterns"), int(PDFObjectInspectorTreeItemModel::Pattern));
}
if (m_objectClassifier.hasType(pdf::PDFObjectClassifier::Shading))
{
ui->modeComboBox->addItem(tr("Shadings"), int(PDFObjectInspectorTreeItemModel::Shading));
}
if (m_objectClassifier.hasType(pdf::PDFObjectClassifier::Image))
{
ui->modeComboBox->addItem(tr("Images"), int(PDFObjectInspectorTreeItemModel::Image));
}
if (m_objectClassifier.hasType(pdf::PDFObjectClassifier::Form))
{
ui->modeComboBox->addItem(tr("Forms"), int(PDFObjectInspectorTreeItemModel::Form));
}
if (m_objectClassifier.hasType(pdf::PDFObjectClassifier::Font))
{
ui->modeComboBox->addItem(tr("Fonts"), int(PDFObjectInspectorTreeItemModel::Font));
}
if (m_objectClassifier.hasType(pdf::PDFObjectClassifier::Action))
{
ui->modeComboBox->addItem(tr("Actions"), int(PDFObjectInspectorTreeItemModel::Action));
}
if (m_objectClassifier.hasType(pdf::PDFObjectClassifier::Annotation))
{
ui->modeComboBox->addItem(tr("Annotations"), int(PDFObjectInspectorTreeItemModel::Annotation));
}
ui->modeComboBox->addItem(tr("Object List"), int(PDFObjectInspectorTreeItemModel::List));
ui->modeComboBox->setCurrentIndex(ui->modeComboBox->findData(int(PDFObjectInspectorTreeItemModel::Document)));
connect(ui->modeComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ObjectInspectorDialog::onModeChanged);
m_model = new PDFObjectInspectorTreeItemModel(&m_objectClassifier, this);
onModeChanged();
m_model->setDocument(pdf::PDFModifiedDocument(const_cast<pdf::PDFDocument*>(document), nullptr, pdf::PDFModifiedDocument::Reset));
ui->objectTreeView->setRootIsDecorated(true);
ui->objectTreeView->setModel(m_model);
ui->splitter->setStretchFactor(0, 0);
ui->splitter->setStretchFactor(1, 1);
ui->splitter->setCollapsible(0, true);
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);
connect(m_viewerWidget, &ObjectViewerWidget::pinRequest, this, &ObjectInspectorDialog::onPinRequest);
connect(m_viewerWidget, &ObjectViewerWidget::unpinRequest, this, &ObjectInspectorDialog::onUnpinRequest);
ui->objectTreeView->setMinimumWidth(pdf::PDFWidgetUtils::scaleDPI_x(this, 200));
setMinimumSize(pdf::PDFWidgetUtils::scaleDPI(this, QSize(800, 600)));
pdf::PDFWidgetUtils::style(this);
}
ObjectInspectorDialog::~ObjectInspectorDialog()
{
delete ui;
}
void ObjectInspectorDialog::onModeChanged()
{
const PDFObjectInspectorTreeItemModel::Mode mode = static_cast<const PDFObjectInspectorTreeItemModel::Mode>(ui->modeComboBox->currentData().toInt());
m_model->setMode(mode);
}
void ObjectInspectorDialog::onPinRequest()
{
ObjectViewerWidget* source = qobject_cast<ObjectViewerWidget*>(sender());
if (!source || source != m_viewerWidget)
{
return;
}
ObjectViewerWidget* cloned = m_viewerWidget->clone(true, this);
connect(cloned, &ObjectViewerWidget::pinRequest, this, &ObjectInspectorDialog::onPinRequest);
connect(cloned, &ObjectViewerWidget::unpinRequest, this, &ObjectInspectorDialog::onUnpinRequest);
ui->tabWidget->addTab(cloned, cloned->getTitleText());
}
void ObjectInspectorDialog::onUnpinRequest()
{
ObjectViewerWidget* source = qobject_cast<ObjectViewerWidget*>(sender());
if (!source || source == m_viewerWidget)
{
return;
}
ui->tabWidget->removeTab(ui->tabWidget->indexOf(source));
source->deleteLater();
}
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;
}
m_viewerWidget->setData(reference, qMove(object), isRoot);
}
} // namespace pdfplugin

View File

@@ -0,0 +1,61 @@
// 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/>.
#ifndef OBJECTINSPECTORDIALOG_H
#define OBJECTINSPECTORDIALOG_H
#include "pdfdocument.h"
#include "pdfobjectutils.h"
#include "pdfcms.h"
#include "objectviewerwidget.h"
#include <QDialog>
namespace Ui
{
class ObjectInspectorDialog;
}
namespace pdfplugin
{
class PDFObjectInspectorTreeItemModel;
class ObjectInspectorDialog : public QDialog
{
Q_OBJECT
public:
explicit ObjectInspectorDialog(const pdf::PDFCMS* cms, const pdf::PDFDocument* document, QWidget* parent);
virtual ~ObjectInspectorDialog() override;
private:
void onModeChanged();
void onPinRequest();
void onUnpinRequest();
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;
ObjectViewerWidget* m_viewerWidget;
};
} // namespace pdfplugin
#endif // OBJECTINSPECTORDIALOG_H

View File

@@ -0,0 +1,102 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ObjectInspectorDialog</class>
<widget class="QDialog" name="ObjectInspectorDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>742</width>
<height>666</height>
</rect>
</property>
<property name="windowTitle">
<string>Object Inspector</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QGroupBox" name="objectViewGroupBox">
<property name="title">
<string>Objects</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QComboBox" name="modeComboBox"/>
</item>
<item>
<widget class="QTreeView" name="objectTreeView">
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
</layout>
</widget>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<property name="movable">
<bool>true</bool>
</property>
<widget class="QWidget" name="currentObjectTab">
<attribute name="title">
<string>Current Object</string>
</attribute>
<layout class="QVBoxLayout" name="currentObjectTabLayout"/>
</widget>
</widget>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>ObjectInspectorDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>ObjectInspectorDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@@ -0,0 +1,100 @@
// 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 "objectinspectorplugin.h"
#include "pdfcms.h"
#include "pdfutils.h"
#include "pdfdrawwidget.h"
#include "objectinspectordialog.h"
#include "objectstatisticsdialog.h"
#include <QAction>
namespace pdfplugin
{
ObjectInspectorPlugin::ObjectInspectorPlugin() :
pdf::PDFPlugin(nullptr),
m_objectInspectorAction(nullptr),
m_objectStatisticsAction(nullptr)
{
}
void ObjectInspectorPlugin::setWidget(pdf::PDFWidget* widget)
{
Q_ASSERT(!m_widget);
BaseClass::setWidget(widget);
m_objectInspectorAction = new QAction(QIcon(":/pdfplugins/objectinspector/object-inspector.svg"), tr("Object Inspector"), this);
m_objectInspectorAction->setCheckable(false);
m_objectInspectorAction->setObjectName("actionObjectInspector_ObjectInspector");
connect(m_objectInspectorAction, &QAction::triggered, this, &ObjectInspectorPlugin::onObjectInspectorTriggered);
m_objectStatisticsAction = new QAction(QIcon(":/pdfplugins/objectinspector/object-statistics.svg"), tr("Object Statistics"), this);
m_objectStatisticsAction->setCheckable(false);
m_objectStatisticsAction->setObjectName("actionObjectInspector_ObjectStatistics");
connect(m_objectStatisticsAction, &QAction::triggered, this, &ObjectInspectorPlugin::onObjectStatisticsTriggered);
updateActions();
}
void ObjectInspectorPlugin::setCMSManager(pdf::PDFCMSManager* manager)
{
BaseClass::setCMSManager(manager);
}
void ObjectInspectorPlugin::setDocument(const pdf::PDFModifiedDocument& document)
{
BaseClass::setDocument(document);
if (document.hasReset())
{
updateActions();
}
}
std::vector<QAction*> ObjectInspectorPlugin::getActions() const
{
return { m_objectInspectorAction, m_objectStatisticsAction };
}
void ObjectInspectorPlugin::onObjectInspectorTriggered()
{
pdf::PDFCMSPointer cms = m_cmsManager->getCurrentCMS();
ObjectInspectorDialog dialog(cms.data(), m_document, m_widget);
dialog.exec();
}
void ObjectInspectorPlugin::onObjectStatisticsTriggered()
{
ObjectStatisticsDialog dialog(m_document, m_widget);
dialog.exec();
}
void ObjectInspectorPlugin::updateActions()
{
m_objectInspectorAction->setEnabled(m_widget && m_document);
m_objectStatisticsAction->setEnabled(m_widget && m_document);
}
}

View File

@@ -0,0 +1,56 @@
// 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/>.
#ifndef OBJECTINSPECTORPLUGIN_H
#define OBJECTINSPECTORPLUGIN_H
#include "pdfplugin.h"
#include <QObject>
namespace pdfplugin
{
class ObjectInspectorPlugin : public pdf::PDFPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "PDF4QT.ObjectInspectorPlugin" FILE "ObjectInspectorPlugin.json")
private:
using BaseClass = pdf::PDFPlugin;
public:
ObjectInspectorPlugin();
virtual void setWidget(pdf::PDFWidget* widget) override;
virtual void setCMSManager(pdf::PDFCMSManager* manager) override;
virtual void setDocument(const pdf::PDFModifiedDocument& document) override;
virtual std::vector<QAction*> getActions() const override;
private:
void onObjectInspectorTriggered();
void onObjectStatisticsTriggered();
void updateActions();
QAction* m_objectInspectorAction;
QAction* m_objectStatisticsAction;
};
} // namespace pdfplugin
#endif // OBJECTINSPECTORPLUGIN_H

View File

@@ -0,0 +1,172 @@
// 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 "objectstatisticsdialog.h"
#include "ui_objectstatisticsdialog.h"
#include "pdfwidgetutils.h"
namespace pdfplugin
{
ObjectStatisticsDialog::ObjectStatisticsDialog(const pdf::PDFDocument* document, QWidget *parent) :
QDialog(parent, Qt::Dialog | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint),
ui(new Ui::ObjectStatisticsDialog),
m_document(document),
m_statisticsGraphWidget(new StatisticsGraphWidget(this))
{
ui->setupUi(this);
ui->dialogLayout->addWidget(m_statisticsGraphWidget);
ui->comboBox->addItem(tr("Statistics by Object Function"), int(ByObjectClass));
ui->comboBox->addItem(tr("Statistics by Object Type"), int(ByObjectType));
ui->comboBox->setCurrentIndex(ui->comboBox->findData(int(ByObjectClass), Qt::UserRole, Qt::MatchExactly));
connect(ui->comboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ObjectStatisticsDialog::updateStatisticsWidget);
pdf::PDFObjectClassifier classifier;
classifier.classify(document);
m_statistics = classifier.calculateStatistics(document);
updateStatisticsWidget();
pdf::PDFWidgetUtils::style(this);
}
ObjectStatisticsDialog::~ObjectStatisticsDialog()
{
delete ui;
}
void ObjectStatisticsDialog::updateStatisticsWidget()
{
StatisticsGraphWidget::Statistics statistics;
QLocale locale;
std::array colors = {
Qt::green,
Qt::red,
Qt::blue,
Qt::cyan,
Qt::magenta,
Qt::yellow,
Qt::darkGreen,
Qt::darkRed,
Qt::darkBlue,
Qt::darkCyan,
Qt::darkMagenta,
Qt::darkYellow
};
const StatisticsType statisticsType = static_cast<StatisticsType>(ui->comboBox->currentData().toInt());
switch (statisticsType)
{
case pdfplugin::ObjectStatisticsDialog::ByObjectClass:
{
statistics.title = tr("Statistics by Object Class");
statistics.headers = QStringList() << tr("Class") << tr("Percentage [%]") << tr("Count [#]") << tr("Space Usage [bytes]");
size_t colorId = 0;
qint64 totalBytesCount = 0;
for (const auto& item : m_statistics.statistics)
{
totalBytesCount += item.second.bytes;
}
auto addItem = [&, this](pdf::PDFObjectClassifier::Type type, QString classText)
{
auto it = m_statistics.statistics.find(type);
if (it == m_statistics.statistics.cend())
{
// Jakub Melka: no type found
return;
}
const pdf::PDFObjectClassifier::StatisticsItem& statisticsItem = it->second;
qreal percentage = qreal(100.0) * qreal(statisticsItem.bytes) / qreal(totalBytesCount);
StatisticsGraphWidget::StatisticsItem item;
item.portion = percentage / qreal(100.0);
item.color = colors[colorId++ % colors.size()];
item.texts = QStringList() << classText << locale.toString(percentage, 'f', 2) << locale.toString(statisticsItem.count) << locale.toString(statisticsItem.bytes);
statistics.items.emplace_back(qMove(item));
};
addItem(pdf::PDFObjectClassifier::Page, tr("Page"));
addItem(pdf::PDFObjectClassifier::ContentStream, tr("Content Stream"));
addItem(pdf::PDFObjectClassifier::GraphicState, tr("Graphic State"));
addItem(pdf::PDFObjectClassifier::ColorSpace, tr("Color Space"));
addItem(pdf::PDFObjectClassifier::Pattern, tr("Pattern"));
addItem(pdf::PDFObjectClassifier::Shading, tr("Shading"));
addItem(pdf::PDFObjectClassifier::Image, tr("Image"));
addItem(pdf::PDFObjectClassifier::Form, tr("Form"));
addItem(pdf::PDFObjectClassifier::Font, tr("Font"));
addItem(pdf::PDFObjectClassifier::Action, tr("Action"));
addItem(pdf::PDFObjectClassifier::Annotation, tr("Annotation"));
addItem(pdf::PDFObjectClassifier::None, tr("Other"));
break;
}
case pdfplugin::ObjectStatisticsDialog::ByObjectType:
{
statistics.title = tr("Statistics by Object Type");
statistics.headers = QStringList() << tr("Type") << tr("Percentage [%]") << tr("Count [#]");
qint64 totalObjectCount = 0;
for (pdf::PDFObject::Type type : pdf::PDFObject::getTypes())
{
const qint64 currentObjectCount = m_statistics.objectCountByType[size_t(type)];
totalObjectCount += currentObjectCount;
}
if (totalObjectCount > 0)
{
size_t colorId = 0;
for (pdf::PDFObject::Type type : pdf::PDFObject::getTypes())
{
const qint64 currentObjectCount = m_statistics.objectCountByType[size_t(type)];
if (currentObjectCount == 0)
{
continue;
}
qreal percentage = qreal(100.0) * qreal(currentObjectCount) / qreal(totalObjectCount);
StatisticsGraphWidget::StatisticsItem item;
item.portion = percentage / qreal(100.0);
item.color = colors[colorId++ % colors.size()];
item.texts = QStringList() << pdf::PDFObjectUtils::getObjectTypeName(type) << locale.toString(percentage, 'f', 2) << locale.toString(currentObjectCount);
statistics.items.emplace_back(qMove(item));
}
}
break;
}
default:
Q_ASSERT(false);
break;
}
m_statisticsGraphWidget->setStatistics(qMove(statistics));
}
} // namespace pdfplugin

View File

@@ -0,0 +1,61 @@
// 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/>.
#ifndef OBJECTSTATISTICSDIALOG_H
#define OBJECTSTATISTICSDIALOG_H
#include "pdfdocument.h"
#include "pdfobjectutils.h"
#include "statisticsgraphwidget.h"
#include <QDialog>
namespace Ui
{
class ObjectStatisticsDialog;
}
namespace pdfplugin
{
class ObjectStatisticsDialog : public QDialog
{
Q_OBJECT
public:
explicit ObjectStatisticsDialog(const pdf::PDFDocument* document, QWidget* parent);
virtual ~ObjectStatisticsDialog() override;
private:
Ui::ObjectStatisticsDialog* ui;
enum StatisticsType
{
ByObjectClass,
ByObjectType
};
void updateStatisticsWidget();
const pdf::PDFDocument* m_document;
pdf::PDFObjectClassifier::Statistics m_statistics;
StatisticsGraphWidget* m_statisticsGraphWidget;
};
} // namespace pdfplugin
#endif // OBJECTSTATISTICSDIALOG_H

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ObjectStatisticsDialog</class>
<widget class="QDialog" name="ObjectStatisticsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>924</width>
<height>504</height>
</rect>
</property>
<property name="windowTitle">
<string>Object Statistics</string>
</property>
<layout class="QVBoxLayout" name="dialogLayout">
<item>
<widget class="QComboBox" name="comboBox"/>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,315 @@
// 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 "objectviewerwidget.h"
#include "ui_objectviewerwidget.h"
#include "pdfimage.h"
#include "pdfencoding.h"
#include "pdfdocumentwriter.h"
#include "pdfexception.h"
namespace pdfplugin
{
ObjectViewerWidget::ObjectViewerWidget(QWidget *parent) :
ObjectViewerWidget(false, parent)
{
}
ObjectViewerWidget::ObjectViewerWidget(bool isPinned, QWidget* parent) :
QWidget(parent),
ui(new Ui::ObjectViewerWidget),
m_cms(nullptr),
m_document(nullptr),
m_isPinned(isPinned),
m_isRootObject(false)
{
ui->setupUi(this);
m_printableCharacters = pdf::PDFEncoding::getPrintableCharacters();
m_printableCharacters.push_back('\n');
connect(ui->pinButton, &QPushButton::clicked, this, &ObjectViewerWidget::pinRequest);
connect(ui->unpinButton, &QPushButton::clicked, this, &ObjectViewerWidget::unpinRequest);
updateUi();
updatePinnedUi();
}
ObjectViewerWidget::~ObjectViewerWidget()
{
delete ui;
}
ObjectViewerWidget* ObjectViewerWidget::clone(bool isPinned, QWidget* parent)
{
ObjectViewerWidget* cloned = new ObjectViewerWidget(isPinned, parent);
cloned->setDocument(m_document);
cloned->setCms(m_cms);
cloned->setData(m_currentReference, m_currentObject, m_isRootObject);
return cloned;
}
void ObjectViewerWidget::setPinned(bool isPinned)
{
if (m_isPinned != isPinned)
{
m_isPinned = isPinned;
updatePinnedUi();
}
}
void ObjectViewerWidget::setData(pdf::PDFObjectReference currentReference, pdf::PDFObject currentObject, bool isRootObject)
{
if (m_currentReference != currentReference ||
m_currentObject != currentObject ||
m_isRootObject != isRootObject)
{
m_currentReference = currentReference;
m_currentObject = currentObject;
m_isRootObject = isRootObject;
updateUi();
}
}
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(m_printableCharacters))));
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, nullptr);
ui->stackedWidget->setCurrentWidget(ui->imageBrowserPage);
ui->imageBrowser->setPixmap(QPixmap::fromImage(image));
const int width = image.width();
const int height = image.height();
const int depth = loader.readIntegerFromDictionary(dictionary, "BitsPerComponent", 8);
QString textDescription = tr("Image Stream [%1 items, %2 data bytes, %3 x %4 pixels, %5 bits per component]");
ui->descriptionEdit->setText(textDescription.arg(locale.toString(m_currentObject.getStream()->getDictionary()->getCount()),
locale.toString(m_currentObject.getStream()->getContent()->size()),
locale.toString(width),
locale.toString(height),
locale.toString(depth)));
}
else
{
QByteArray dataToBeAdjusted = m_document->getDecodedStream(stream);
dataToBeAdjusted.replace('\r', ' ');
QByteArray percentEncodedData = dataToBeAdjusted.toPercentEncoding(m_printableCharacters);
ui->contentTextBrowser->setText(QString::fromLatin1(percentEncodedData));
ui->stackedWidget->setCurrentWidget(ui->contentTextBrowserPage);
}
}
catch (const 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(m_printableCharacters);
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;
}
QString ObjectViewerWidget::getTitleText() const
{
if (!m_currentReference.isValid())
{
return tr("[Unknown]");
}
QString referenceString = tr("%1 %2 R").arg(m_currentReference.objectNumber).arg(m_currentReference.generation);
if (m_isRootObject)
{
return referenceString;
}
else
{
return tr("%1 (part)").arg(referenceString);
}
}
const pdf::PDFDocument* ObjectViewerWidget::getDocument() const
{
return m_document;
}
void ObjectViewerWidget::setDocument(const pdf::PDFDocument* document)
{
m_document = document;
}
} // namespace pdfplugin

View File

@@ -0,0 +1,77 @@
// 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/>.
#ifndef OBJECTVIEWERWIDGET_H
#define OBJECTVIEWERWIDGET_H
#include "pdfdocument.h"
#include "pdfcms.h"
#include <QWidget>
namespace Ui
{
class ObjectViewerWidget;
}
namespace pdfplugin
{
class ObjectViewerWidget : public QWidget
{
Q_OBJECT
public:
explicit ObjectViewerWidget(QWidget* parent);
explicit ObjectViewerWidget(bool isPinned, QWidget* parent);
virtual ~ObjectViewerWidget() override;
ObjectViewerWidget* clone(bool isPinned, QWidget* parent);
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);
QString getTitleText() const;
signals:
void pinRequest();
void unpinRequest();
private:
void updateUi();
void updatePinnedUi();
Ui::ObjectViewerWidget* ui;
const pdf::PDFCMS* m_cms;
const pdf::PDFDocument* m_document;
bool m_isPinned;
pdf::PDFObjectReference m_currentReference;
pdf::PDFObject m_currentObject;
bool m_isRootObject;
QByteArray m_printableCharacters;
};
} // pdfplugin
#endif // OBJECTVIEWERWIDGET_H

View File

@@ -0,0 +1,153 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ObjectViewerWidget</class>
<widget class="QWidget" name="ObjectViewerWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>773</width>
<height>602</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,1">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="pinButton">
<property name="text">
<string>Pin</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="unpinButton">
<property name="text">
<string>Unpin</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="informationGroupBox">
<property name="title">
<string>Information</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="parentReferenceLabel">
<property name="text">
<string>Reference</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="referenceEdit">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="typeLabel">
<property name="text">
<string>Type</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="typeEdit">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="descriptionLabel">
<property name="text">
<string>Description</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="descriptionEdit">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="contentsGroupBox">
<property name="title">
<string>Contents</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QStackedWidget" name="stackedWidget">
<widget class="QWidget" name="contentTextBrowserPage">
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QTextBrowser" name="contentTextBrowser"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="imageBrowserPage">
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>98</width>
<height>31</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QLabel" name="imageBrowser">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,504 @@
// 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

View File

@@ -0,0 +1,82 @@
// 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/>.
#ifndef PDFOBJECTINSPECTORTREEITEMMODEL_H
#define PDFOBJECTINSPECTORTREEITEMMODEL_H
#include "pdfitemmodels.h"
#include "pdfobjectutils.h"
#include <set>
namespace pdfplugin
{
class PDFObjectInspectorTreeItem;
class PDFObjectInspectorTreeItemModel : public pdf::PDFTreeItemModel
{
Q_OBJECT
public:
enum Mode
{
Document,
Page,
ContentStream,
GraphicState,
ColorSpace,
Pattern,
Shading,
Image,
Form,
Font,
Action,
Annotation,
List
};
explicit PDFObjectInspectorTreeItemModel(const pdf::PDFObjectClassifier* classifier, QObject* parent);
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
virtual int columnCount(const QModelIndex& parent) const override;
virtual QVariant data(const QModelIndex& index, int role) const override;
virtual void update() override;
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,
pdf::PDFObject object,
bool followRef,
std::set<pdf::PDFObjectReference>& usedReferences) const;
PDFObjectInspectorTreeItem* getRootItem();
const pdf::PDFObjectClassifier* m_classifier;
Mode m_mode = List;
};
} // namespace pdfplugin
#endif // PDFOBJECTINSPECTORTREEITEMMODEL_H

View File

@@ -0,0 +1,308 @@
// 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 "statisticsgraphwidget.h"
#include "ui_statisticsgraphwidget.h"
#include "pdfwidgetutils.h"
#include "pdfpainterutils.h"
#include <QPainter>
namespace pdfplugin
{
StatisticsGraphWidget::StatisticsGraphWidget(QWidget* parent) :
QWidget(parent),
ui(new Ui::StatisticsGraphWidget)
{
ui->setupUi(this);
}
StatisticsGraphWidget::~StatisticsGraphWidget()
{
delete ui;
}
void StatisticsGraphWidget::setStatistics(Statistics statistics)
{
if (m_statistics != statistics)
{
m_statistics = qMove(statistics);
updateGeometry();
update();
}
}
StatisticsGraphWidget::GeometryHint StatisticsGraphWidget::getGeometryHint() const
{
GeometryHint hint;
int fivePixelsX = pdf::PDFWidgetUtils::scaleDPI_x(this, 5);
int fivePixelsY = pdf::PDFWidgetUtils::scaleDPI_y(this, 5);
int textMargin = fivePixelsX;
hint.margins = QMargins(fivePixelsX, fivePixelsY, fivePixelsX, fivePixelsY);
hint.colorRectangleWidth = pdf::PDFWidgetUtils::scaleDPI_x(this, 80);
hint.linesWidthLeft = pdf::PDFWidgetUtils::scaleDPI_x(this, 15);
hint.linesWidthRight = pdf::PDFWidgetUtils::scaleDPI_x(this, 5);
hint.markerSize = fivePixelsX;
hint.normalLineWidth = pdf::PDFWidgetUtils::scaleDPI_y(this, 2);
hint.selectedLineWidth = pdf::PDFWidgetUtils::scaleDPI_y(this, 3);
hint.colorRectangeLeftMargin = pdf::PDFWidgetUtils::scaleDPI_x(this, 40);
QFontMetrics fontMetricsTitle(getTitleFont());
hint.titleWidth = fontMetricsTitle.horizontalAdvance(m_statistics.title);
hint.titleHeight = fontMetricsTitle.lineSpacing();
QFontMetrics fontMetricsHeader(getHeaderFont());
hint.textHeight = fontMetricsHeader.lineSpacing();
QFontMetrics fontMetricsText(getTextFont());
hint.textHeight += fontMetricsText.lineSpacing() * int(m_statistics.items.size());
hint.textMargin = textMargin;
hint.textLineHeight = fontMetricsText.lineSpacing();
// Determine text header width
hint.textWidths.resize(m_statistics.headers.size(), 0);
for (int i = 0; i < m_statistics.headers.size(); ++i)
{
hint.textWidths[i] = fontMetricsHeader.horizontalAdvance(m_statistics.headers[i]);
}
for (const auto& item : m_statistics.items)
{
Q_ASSERT(static_cast<size_t>(item.texts.size()) == hint.textWidths.size());
for (int i = 0; i < item.texts.size(); ++i)
{
hint.textWidths[i] = qMax(hint.textWidths[i], fontMetricsHeader.horizontalAdvance(item.texts[i]));
}
}
int totalTextWidth = textMargin;
for (int& textWidth : hint.textWidths)
{
textWidth += 2 * textMargin;
totalTextWidth += textWidth;
}
const int widthHint = hint.margins.left() + hint.colorRectangeLeftMargin + hint.colorRectangleWidth + hint.linesWidthLeft + hint.linesWidthRight + qMax(totalTextWidth, hint.titleWidth) + hint.margins.right();
const int heightHint = hint.margins.top() + hint.titleHeight + qMax(pdf::PDFWidgetUtils::scaleDPI_y(this, 100), hint.textHeight) + hint.margins.bottom();
hint.minimalWidgetSize = QSize(widthHint, heightHint).expandedTo(pdf::PDFWidgetUtils::scaleDPI(this, QSize(400, 300)));
return hint;
}
void StatisticsGraphWidget::paintEvent(QPaintEvent* event)
{
Q_UNUSED(event);
QPainter painter(this);
QRect rect = this->rect();
painter.fillRect(rect, Qt::white);
GeometryHint geometryHint = getGeometryHint();
rect = rect.marginsRemoved(geometryHint.margins);
if (!m_statistics.title.isEmpty())
{
pdf::PDFPainterStateGuard guard(&painter);
QRect titleRect = rect;
titleRect.setHeight(geometryHint.titleHeight);
painter.setFont(getHeaderFont());
painter.drawText(titleRect, Qt::AlignCenter, m_statistics.title);
rect.setTop(titleRect.bottom());
}
rect.setLeft(rect.left() + geometryHint.colorRectangeLeftMargin);
// Color boxes
m_colorBoxes.clear();
const int height = rect.height();
QColor selectedColor = Qt::darkYellow;
int top = rect.top();
for (size_t i = 0; i < m_statistics.items.size(); ++i)
{
const StatisticsItem& item = m_statistics.items[i];
QRect currentRect(rect.left(), top, geometryHint.colorRectangleWidth, height * item.portion);
top = currentRect.bottom();
QColor color = (m_selectedColorBox == i) ? selectedColor: item.color;
color.setAlphaF(0.8f);
painter.fillRect(currentRect, color);
m_colorBoxes.push_back(currentRect);
}
QPoint minLinePoint;
QPoint maxLinePoint;
// Marked lines
{
pdf::PDFPainterStateGuard guard(&painter);
for (size_t i = 0; i < m_statistics.items.size(); ++i)
{
QPen pen = painter.pen();
pen.setColor((i == m_selectedColorBox) ? selectedColor : Qt::black);
pen.setWidth(geometryHint.markerSize);
pen.setCapStyle(Qt::RoundCap);
painter.setPen(pen);
QRect colorBox = m_colorBoxes[i];
QPoint marker = colorBox.center();
marker.setX(colorBox.right());
painter.drawPoint(marker);
pen.setWidth((i == m_selectedColorBox) ? geometryHint.selectedLineWidth : geometryHint.normalLineWidth);
painter.setPen(pen);
QPoint lineEnd = marker + QPoint(geometryHint.linesWidthLeft, 0);
painter.drawLine(marker, lineEnd);
if (i == 0)
{
minLinePoint = lineEnd;
}
maxLinePoint = lineEnd;
}
}
// Texts
{
pdf::PDFPainterStateGuard guard(&painter);
QFont headerFont = getHeaderFont();
QFont textFont = getTextFont();
QRect textRect = rect;
textRect.setLeft(maxLinePoint.x() + geometryHint.textMargin);
textRect.setHeight(geometryHint.textLineHeight);
std::vector<QLine> horizontalLines;
horizontalLines.reserve(m_statistics.items.size());
auto drawTexts = [&](const QStringList& texts, bool isHeader)
{
pdf::PDFPainterStateGuard guard(&painter);
painter.setFont(isHeader ? headerFont : textFont);
const int y = textRect.center().y();
minLinePoint.ry() = qMin(minLinePoint.ry(), y);
maxLinePoint.ry() = qMax(maxLinePoint.ry(), y);
QRect cellRect = textRect;
for (int i = 0; i < texts.size(); ++i)
{
cellRect.setWidth(geometryHint.textWidths[i]);
QRect finalCellRect = cellRect.marginsRemoved(QMargins(geometryHint.textMargin, 0, geometryHint.textMargin, 0));
QString text = texts[i];
Qt::Alignment alignment = (i == 0) ? Qt::Alignment(Qt::AlignLeft | Qt::AlignVCenter) : Qt::Alignment(Qt::AlignRight | Qt::AlignVCenter);
painter.drawText(finalCellRect, alignment, text);
cellRect.translate(cellRect.width(), 0);
}
if (!isHeader)
{
QPoint startPoint = textRect.center();
startPoint.setX(textRect.left());
QPoint endPoint(maxLinePoint.x(), startPoint.y());
horizontalLines.emplace_back(startPoint, endPoint);
}
textRect.translate(0, textRect.height());
};
if (!m_statistics.headers.isEmpty())
{
drawTexts(m_statistics.headers, true);
}
for (size_t i = 0; i < m_statistics.items.size(); ++i)
{
const StatisticsItem& item = m_statistics.items[i];
drawTexts(item.texts, i == m_selectedColorBox);
}
QPen pen = painter.pen();
pen.setColor(Qt::black);
pen.setWidth(geometryHint.normalLineWidth);
pen.setCapStyle(Qt::RoundCap);
painter.setPen(pen);
painter.drawLine(minLinePoint, maxLinePoint);
for (const QLine& line : horizontalLines)
{
painter.drawLine(line);
}
pen = painter.pen();
pen.setColor(Qt::black);
pen.setWidth(geometryHint.markerSize);
pen.setCapStyle(Qt::RoundCap);
painter.setPen(pen);
for (const QLine& line : horizontalLines)
{
painter.drawPoint(line.p1());
}
}
}
QFont StatisticsGraphWidget::getTitleFont() const
{
QFont font = this->font();
font.setPointSize(font.pointSize() * 2);
font.setBold(true);
return font;
}
QFont StatisticsGraphWidget::getHeaderFont() const
{
QFont font = this->font();
font.setBold(true);
return font;
}
QFont StatisticsGraphWidget::getTextFont() const
{
return this->font();
}
QSize StatisticsGraphWidget::sizeHint() const
{
return getGeometryHint().minimalWidgetSize + pdf::PDFWidgetUtils::scaleDPI(this, QSize(100, 100));
}
QSize StatisticsGraphWidget::minimumSizeHint() const
{
return getGeometryHint().minimalWidgetSize;
}
} // namespace pdfplugin

View File

@@ -0,0 +1,106 @@
// 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/>.
#ifndef STATISTICSGRAPHWIDGET_H
#define STATISTICSGRAPHWIDGET_H
#include <QWidget>
#include <vector>
namespace Ui
{
class StatisticsGraphWidget;
}
namespace pdfplugin
{
class StatisticsGraphWidget : public QWidget
{
Q_OBJECT
public:
struct StatisticsItem
{
bool operator==(const StatisticsItem&) const = default;
qreal portion;
QColor color;
QStringList texts;
};
struct Statistics
{
bool operator==(const Statistics&) const = default;
QString title;
QStringList headers;
std::vector<StatisticsItem> items;
};
explicit StatisticsGraphWidget(QWidget* parent);
virtual ~StatisticsGraphWidget() override;
virtual QSize sizeHint() const override;
virtual QSize minimumSizeHint() const override;
void setStatistics(Statistics statistics);
protected:
virtual void paintEvent(QPaintEvent* event) override;
private:
Ui::StatisticsGraphWidget* ui;
struct GeometryHint
{
QMargins margins;
int colorRectangleWidth = 0;
int linesWidthLeft = 0;
int linesWidthRight = 0;
int markerSize = 0;
int normalLineWidth = 0;
int selectedLineWidth = 0;
int colorRectangeLeftMargin = 0;
int titleWidth = 0;
int titleHeight = 0;
int textHeight = 0;
int textMargin = 0;
int textLineHeight = 0;
std::vector<int> textWidths;
QSize minimalWidgetSize;
};
GeometryHint getGeometryHint() const;
QFont getTitleFont() const;
QFont getHeaderFont() const;
QFont getTextFont() const;
Statistics m_statistics;
std::vector<QRect> m_colorBoxes;
size_t m_selectedColorBox = std::numeric_limits<size_t>::max();
};
} // namespace pdfplugin
#endif // STATISTICSGRAPHWIDGET_H

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>StatisticsGraphWidget</class>
<widget class="QWidget" name="StatisticsGraphWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
</widget>
<resources/>
<connections/>
</ui>