mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-06-05 21:59:17 +02:00
Statistics widget
This commit is contained in:
@ -228,6 +228,48 @@ PDFObject PDFObjectUtils::replaceReferences(const PDFObject& object, const std::
|
|||||||
return replaceReferencesVisitor.getObject();
|
return replaceReferencesVisitor.getObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString PDFObjectUtils::getObjectTypeName(PDFObject::Type type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case pdf::PDFObject::Type::Null:
|
||||||
|
return PDFTranslationContext::tr("Null");
|
||||||
|
|
||||||
|
case pdf::PDFObject::Type::Bool:
|
||||||
|
return PDFTranslationContext::tr("Boolean");
|
||||||
|
|
||||||
|
case pdf::PDFObject::Type::Int:
|
||||||
|
return PDFTranslationContext::tr("Integer");
|
||||||
|
|
||||||
|
case pdf::PDFObject::Type::Real:
|
||||||
|
return PDFTranslationContext::tr("Real");
|
||||||
|
|
||||||
|
case pdf::PDFObject::Type::String:
|
||||||
|
return PDFTranslationContext::tr("String");
|
||||||
|
|
||||||
|
case pdf::PDFObject::Type::Name:
|
||||||
|
return PDFTranslationContext::tr("Name");
|
||||||
|
|
||||||
|
case pdf::PDFObject::Type::Array:
|
||||||
|
return PDFTranslationContext::tr("Array");
|
||||||
|
|
||||||
|
case pdf::PDFObject::Type::Dictionary:
|
||||||
|
return PDFTranslationContext::tr("Dictionary");
|
||||||
|
|
||||||
|
case pdf::PDFObject::Type::Stream:
|
||||||
|
return PDFTranslationContext::tr("Stream");
|
||||||
|
|
||||||
|
case pdf::PDFObject::Type::Reference:
|
||||||
|
return PDFTranslationContext::tr("Reference");
|
||||||
|
|
||||||
|
default:
|
||||||
|
Q_ASSERT(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
void PDFObjectClassifier::classify(const PDFDocument* document)
|
void PDFObjectClassifier::classify(const PDFDocument* document)
|
||||||
{
|
{
|
||||||
// Clear old classification, if it exist
|
// Clear old classification, if it exist
|
||||||
|
@ -32,7 +32,7 @@ class PDFObjectStorage;
|
|||||||
class PDFDocument;
|
class PDFDocument;
|
||||||
|
|
||||||
/// Utilities for manipulation with objects
|
/// Utilities for manipulation with objects
|
||||||
class PDFObjectUtils
|
class Pdf4QtLIBSHARED_EXPORT PDFObjectUtils
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// Returns a list of references referenced by \p objects. So, all references, which are present
|
/// Returns a list of references referenced by \p objects. So, all references, which are present
|
||||||
@ -47,6 +47,10 @@ public:
|
|||||||
|
|
||||||
static PDFObject replaceReferences(const PDFObject& object, const std::map<PDFObjectReference, PDFObjectReference>& referenceMapping);
|
static PDFObject replaceReferences(const PDFObject& object, const std::map<PDFObjectReference, PDFObjectReference>& referenceMapping);
|
||||||
|
|
||||||
|
/// Returns name for object type
|
||||||
|
/// \param type Type
|
||||||
|
static QString getObjectTypeName(PDFObject::Type type);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PDFObjectUtils() = delete;
|
PDFObjectUtils() = delete;
|
||||||
};
|
};
|
||||||
|
@ -28,9 +28,16 @@ ObjectStatisticsDialog::ObjectStatisticsDialog(const pdf::PDFDocument* document,
|
|||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
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;
|
pdf::PDFObjectClassifier classifier;
|
||||||
classifier.classify(document);
|
classifier.classify(document);
|
||||||
m_statistics = classifier.calculateStatistics(document);
|
m_statistics = classifier.calculateStatistics(document);
|
||||||
|
|
||||||
|
updateStatisticsWidget();
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectStatisticsDialog::~ObjectStatisticsDialog()
|
ObjectStatisticsDialog::~ObjectStatisticsDialog()
|
||||||
@ -38,4 +45,122 @@ ObjectStatisticsDialog::~ObjectStatisticsDialog()
|
|||||||
delete ui;
|
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) << 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) << locale.toString(currentObjectCount);
|
||||||
|
statistics.items.emplace_back(qMove(item));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
Q_ASSERT(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->graphWidget->setStatistics(qMove(statistics));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace pdfplugin
|
} // namespace pdfplugin
|
||||||
|
@ -44,9 +44,12 @@ private:
|
|||||||
|
|
||||||
enum StatisticsType
|
enum StatisticsType
|
||||||
{
|
{
|
||||||
|
ByObjectClass,
|
||||||
|
ByObjectType
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void updateStatisticsWidget();
|
||||||
|
|
||||||
const pdf::PDFDocument* m_document;
|
const pdf::PDFDocument* m_document;
|
||||||
pdf::PDFObjectClassifier::Statistics m_statistics;
|
pdf::PDFObjectClassifier::Statistics m_statistics;
|
||||||
};
|
};
|
||||||
|
@ -18,10 +18,12 @@
|
|||||||
#include "statisticsgraphwidget.h"
|
#include "statisticsgraphwidget.h"
|
||||||
#include "ui_statisticsgraphwidget.h"
|
#include "ui_statisticsgraphwidget.h"
|
||||||
|
|
||||||
|
#include "pdfwidgetutils.h"
|
||||||
|
|
||||||
namespace pdfplugin
|
namespace pdfplugin
|
||||||
{
|
{
|
||||||
|
|
||||||
StatisticsGraphWidget::StatisticsGraphWidget(QWidget *parent) :
|
StatisticsGraphWidget::StatisticsGraphWidget(QWidget* parent) :
|
||||||
QWidget(parent),
|
QWidget(parent),
|
||||||
ui(new Ui::StatisticsGraphWidget)
|
ui(new Ui::StatisticsGraphWidget)
|
||||||
{
|
{
|
||||||
@ -33,4 +35,62 @@ StatisticsGraphWidget::~StatisticsGraphWidget()
|
|||||||
delete ui;
|
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);
|
||||||
|
|
||||||
|
hint.margins = QMargins(fivePixelsX, fivePixelsY, fivePixelsX, fivePixelsY);
|
||||||
|
hint.colorRectangleWidth = pdf::PDFWidgetUtils::scaleDPI_x(this, 40);
|
||||||
|
hint.linesWidthLeft = pdf::PDFWidgetUtils::scaleDPI_x(this, 15);
|
||||||
|
hint.linesWidthRight = pdf::PDFWidgetUtils::scaleDPI_x(this, 5);
|
||||||
|
|
||||||
|
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());
|
||||||
|
|
||||||
|
// Determine text header width
|
||||||
|
s
|
||||||
|
|
||||||
|
return hint;
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace pdfplugin
|
} // namespace pdfplugin
|
||||||
|
@ -33,11 +33,56 @@ class StatisticsGraphWidget : public QWidget
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
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);
|
explicit StatisticsGraphWidget(QWidget* parent);
|
||||||
virtual ~StatisticsGraphWidget() override;
|
virtual ~StatisticsGraphWidget() override;
|
||||||
|
|
||||||
|
void setStatistics(Statistics statistics);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::StatisticsGraphWidget* ui;
|
Ui::StatisticsGraphWidget* ui;
|
||||||
|
|
||||||
|
struct GeometryHint
|
||||||
|
{
|
||||||
|
QMargins margins;
|
||||||
|
int colorRectangleWidth = 0;
|
||||||
|
int linesWidthLeft = 0;
|
||||||
|
int linesWidthRight = 0;
|
||||||
|
|
||||||
|
int titleWidth = 0;
|
||||||
|
int titleHeight = 0;
|
||||||
|
int textHeight = 0;
|
||||||
|
|
||||||
|
std::vector<int> textWidths;
|
||||||
|
|
||||||
|
QSize minimalWidgetSize = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
GeometryHint getGeometryHint() const;
|
||||||
|
|
||||||
|
QFont getTitleFont() const;
|
||||||
|
QFont getHeaderFont() const;
|
||||||
|
QFont getTextFont() const;
|
||||||
|
|
||||||
|
Statistics m_statistics;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace pdfplugin
|
} // namespace pdfplugin
|
||||||
|
Reference in New Issue
Block a user