Validation settings, certificate store

This commit is contained in:
Jakub Melka
2020-06-27 17:28:03 +02:00
parent aa36af6587
commit 49a2af275f
16 changed files with 732 additions and 86 deletions

View File

@@ -50,5 +50,6 @@
<file>resources/result-information.svg</file>
<file>resources/result-ok.svg</file>
<file>resources/result-warning.svg</file>
<file>resources/signature.svg</file>
</qresource>
</RCC>

View File

@@ -18,6 +18,8 @@
#include "pdfsidebarwidget.h"
#include "ui_pdfsidebarwidget.h"
#include "pdfviewersettings.h"
#include "pdfwidgetutils.h"
#include "pdftexttospeech.h"
@@ -45,11 +47,17 @@ constexpr const char* STYLESHEET =
"QWidget#speechPage { background-color: #F0F0F0 }"
"QWidget#PDFSidebarWidget { background-color: #404040; background: green;}";
PDFSidebarWidget::PDFSidebarWidget(pdf::PDFDrawWidgetProxy* proxy, PDFTextToSpeech* textToSpeech, QWidget* parent) :
PDFSidebarWidget::PDFSidebarWidget(pdf::PDFDrawWidgetProxy* proxy,
PDFTextToSpeech* textToSpeech,
pdf::PDFCertificateStore* certificateStore,
PDFViewerSettings* settings,
QWidget* parent) :
QWidget(parent),
ui(new Ui::PDFSidebarWidget),
m_proxy(proxy),
m_textToSpeech(textToSpeech),
m_certificateStore(certificateStore),
m_settings(settings),
m_outlineTreeModel(nullptr),
m_thumbnailsModel(nullptr),
m_optionalContentTreeModel(nullptr),
@@ -114,6 +122,9 @@ PDFSidebarWidget::PDFSidebarWidget(pdf::PDFDrawWidgetProxy* proxy, PDFTextToSpee
ui->speechRateValueLabel, ui->speechPitchValueLabel, ui->speechVolumeValueLabel,
ui->speechActualTextEdit);
ui->signatureTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->signatureTreeWidget, &QTreeWidget::customContextMenuRequested, this, &PDFSidebarWidget::onSignatureCustomContextMenuRequested);
selectPage(Invalid);
updateButtons();
}
@@ -342,11 +353,18 @@ void PDFSidebarWidget::updateSignatures(const std::vector<pdf::PDFSignatureVerif
ui->signatureTreeWidget->setUpdatesEnabled(false);
ui->signatureTreeWidget->clear();
m_certificateInfos.clear();
QIcon okIcon(":/resources/result-ok.svg");
QIcon errorIcon(":/resources/result-error.svg");
QIcon warningIcon(":/resources/result-warning.svg");
QIcon infoIcon(":/resources/result-information.svg");
if (m_settings->getSettings().m_signatureTreatWarningsAsErrors)
{
warningIcon = errorIcon;
}
for (const pdf::PDFSignatureVerificationResult& signature : signatures)
{
const pdf::PDFCertificateInfos& certificateInfos = signature.getCertificateInfos();
@@ -401,6 +419,13 @@ void PDFSidebarWidget::updateSignatures(const std::vector<pdf::PDFSignatureVerif
QTreeWidgetItem* certRoot = new QTreeWidgetItem(certChainRoot, QStringList(currentCertificateInfo.getName(pdf::PDFCertificateInfo::CommonName)));
certRoot->setIcon(0, infoIcon);
pdf::PDFCertificateInfo::KeyUsageFlags keyUsageFlags = currentCertificateInfo.getKeyUsage();
if (keyUsageFlags.testFlag(pdf::PDFCertificateInfo::KeyUsageCertSign))
{
m_certificateInfos.push_back(currentCertificateInfo);
certRoot->setData(0, Qt::UserRole, m_certificateInfos.size() - 1);
}
auto addName = [certRoot, &currentCertificateInfo, &infoIcon](pdf::PDFCertificateInfo::NameEntry nameEntry, QString caption)
{
QString text = currentCertificateInfo.getName(nameEntry);
@@ -473,7 +498,6 @@ void PDFSidebarWidget::updateSignatures(const std::vector<pdf::PDFSignatureVerif
}
QStringList keyUsages;
pdf::PDFCertificateInfo::KeyUsageFlags keyUsageFlags = currentCertificateInfo.getKeyUsage();
if (keyUsageFlags.testFlag(pdf::PDFCertificateInfo::KeyUsageDigitalSignature))
{
keyUsages << tr("Digital signatures");
@@ -619,6 +643,37 @@ void PDFSidebarWidget::onThumbnailClicked(const QModelIndex& index)
}
}
void PDFSidebarWidget::onSignatureCustomContextMenuRequested(const QPoint& pos)
{
if (QTreeWidgetItem* item = ui->signatureTreeWidget->itemAt(pos))
{
QVariant data = item->data(0, Qt::UserRole);
if (data.isValid())
{
const pdf::PDFCertificateInfo& info = m_certificateInfos.at(data.toInt());
if (!m_certificateStore->contains(info))
{
QMenu menu;
QAction* action = menu.addAction(tr("Add to trusted certificates"));
auto addCertificate = [this, info]()
{
if (QMessageBox::question(this, tr("Add to Trusted Certificate Store"), tr("Are you sure want to add '%1' to the trusted certificate store?").arg(info.getName(pdf::PDFCertificateInfo::CommonName))) == QMessageBox::Yes)
{
if (!m_certificateStore->add(pdf::PDFCertificateStore::EntryType::User, info))
{
QMessageBox::critical(this, tr("Trusted Certificate Store Error"), tr("Failed to add certificate to the trusted certificate store."));
}
}
};
connect(action, &QAction::triggered, this, addCertificate);
menu.exec(ui->signatureTreeWidget->viewport()->mapToGlobal(pos));
}
}
}
}
void PDFSidebarWidget::paintEvent(QPaintEvent* event)
{
Q_UNUSED(event);

View File

@@ -35,7 +35,9 @@ namespace pdf
{
class PDFAction;
class PDFDocument;
class PDFCertificateInfo;
class PDFDrawWidgetProxy;
class PDFCertificateStore;
class PDFModifiedDocument;
class PDFThumbnailsItemModel;
class PDFOutlineTreeItemModel;
@@ -48,13 +50,18 @@ class PDFOptionalContentTreeItemModel;
namespace pdfviewer
{
class PDFTextToSpeech;
class PDFViewerSettings;
class PDFSidebarWidget : public QWidget
{
Q_OBJECT
public:
explicit PDFSidebarWidget(pdf::PDFDrawWidgetProxy* proxy, PDFTextToSpeech* textToSpeech, QWidget* parent);
explicit PDFSidebarWidget(pdf::PDFDrawWidgetProxy* proxy,
PDFTextToSpeech* textToSpeech,
pdf::PDFCertificateStore* certificateStore,
PDFViewerSettings* settings,
QWidget* parent);
virtual ~PDFSidebarWidget() override;
virtual void paintEvent(QPaintEvent* event) override;
@@ -102,6 +109,7 @@ private:
void onThumbnailsSizeChanged(int size);
void onAttachmentCustomContextMenuRequested(const QPoint& pos);
void onThumbnailClicked(const QModelIndex& index);
void onSignatureCustomContextMenuRequested(const QPoint &pos);
struct PageInfo
{
@@ -112,6 +120,8 @@ private:
Ui::PDFSidebarWidget* ui;
pdf::PDFDrawWidgetProxy* m_proxy;
PDFTextToSpeech* m_textToSpeech;
pdf::PDFCertificateStore* m_certificateStore;
PDFViewerSettings* m_settings;
pdf::PDFOutlineTreeItemModel* m_outlineTreeModel;
pdf::PDFThumbnailsItemModel* m_thumbnailsModel;
pdf::PDFOptionalContentTreeItemModel* m_optionalContentTreeModel;
@@ -120,6 +130,7 @@ private:
pdf::PDFAttachmentsTreeItemModel* m_attachmentsTreeModel;
std::map<Page, PageInfo> m_pageInfo;
std::vector<pdf::PDFSignatureVerificationResult> m_signatures;
std::vector<pdf::PDFCertificateInfo> m_certificateInfos;
};
} // namespace pdfviewer

View File

@@ -217,7 +217,7 @@ PDFViewerMainWindow::PDFViewerMainWindow(QWidget* parent) :
m_pdfWidget->getDrawWidgetProxy()->setProgress(m_progress);
m_textToSpeech->setProxy(m_pdfWidget->getDrawWidgetProxy());
m_sidebarWidget = new PDFSidebarWidget(m_pdfWidget->getDrawWidgetProxy(), m_textToSpeech, this);
m_sidebarWidget = new PDFSidebarWidget(m_pdfWidget->getDrawWidgetProxy(), m_textToSpeech, &m_certificateStore, m_settings, this);
m_sidebarDockWidget = new QDockWidget(tr("Sidebar"), this);
m_sidebarDockWidget->setObjectName("SidebarDockWidget");
m_sidebarDockWidget->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
@@ -966,8 +966,14 @@ void PDFViewerMainWindow::openDocument(const QString& fileName)
if (result.result == pdf::PDFDocumentReader::Result::OK)
{
// Verify signatures
pdf::PDFSignatureHandler::Parameters parameters;
parameters.store = &m_certificateStore;
parameters.enableVerification = m_settings->getSettings().m_signatureVerificationEnabled;
parameters.ignoreExpirationDate = m_settings->getSettings().m_signatureIgnoreCertificateValidityTime;
parameters.useSystemCertificateStore = m_settings->getSettings().m_signatureUseSystemStore;
pdf::PDFForm form = pdf::PDFForm::parse(&document, document.getCatalog()->getFormObject());
result.signatures = pdf::PDFSignatureHandler::verifySignatures(form, reader.getSource());
result.signatures = pdf::PDFSignatureHandler::verifySignatures(form, reader.getSource(), parameters);
result.document.reset(new pdf::PDFDocument(qMove(document)));
}

View File

@@ -98,11 +98,8 @@ private slots:
void on_actionRotateLeft_triggered();
void on_actionPrint_triggered();
void on_actionRender_to_Images_triggered();
void on_actionOptimize_triggered();
void on_actionSave_As_triggered();
void on_actionSave_triggered();
private:
@@ -180,6 +177,7 @@ private:
QWinTaskbarButton* m_taskbarButton;
QWinTaskbarProgress* m_progressTaskbarIndicator;
PDFFileInfo m_fileInfo;
pdf::PDFCertificateStore m_certificateStore;
std::vector<pdf::PDFSignatureVerificationResult> m_signatures;
QFuture<AsyncReadingResult> m_future;

View File

@@ -86,6 +86,13 @@ void PDFViewerSettings::readSettings(QSettings& settings, const pdf::PDFCMSSetti
m_settings.m_formAppearanceFlags = static_cast<pdf::PDFFormManager::FormAppearanceFlags>(settings.value("formAppearance", int(pdf::PDFFormManager::getDefaultApperanceFlags())).toInt());
settings.endGroup();
settings.beginGroup("Signature");
m_settings.m_signatureVerificationEnabled = settings.value("signatureVerificationEnabled", defaultSettings.m_signatureVerificationEnabled).toBool();
m_settings.m_signatureTreatWarningsAsErrors = settings.value("signatureTreatWarningsAsErrors", defaultSettings.m_signatureTreatWarningsAsErrors).toBool();
m_settings.m_signatureIgnoreCertificateValidityTime = settings.value("signatureIgnoreCertificateValidityTime", defaultSettings.m_signatureIgnoreCertificateValidityTime).toBool();
m_settings.m_signatureUseSystemStore = settings.value("signatureUseSystemStore", defaultSettings.m_signatureUseSystemStore).toBool();
settings.endGroup();
emit settingsChanged();
}
@@ -139,6 +146,13 @@ void PDFViewerSettings::writeSettings(QSettings& settings)
settings.beginGroup("Forms");
settings.setValue("formAppearance", int(m_settings.m_formAppearanceFlags));
settings.endGroup();
settings.beginGroup("Signature");
settings.setValue("signatureVerificationEnabled", m_settings.m_signatureVerificationEnabled);
settings.setValue("signatureTreatWarningsAsErrors", m_settings.m_signatureTreatWarningsAsErrors);
settings.setValue("signatureIgnoreCertificateValidityTime", m_settings.m_signatureIgnoreCertificateValidityTime);
settings.setValue("signatureUseSystemStore", m_settings.m_signatureUseSystemStore);
settings.endGroup();
}
QString PDFViewerSettings::getDirectory() const
@@ -247,7 +261,11 @@ PDFViewerSettings::Settings::Settings() :
m_magnifierZoom(2.0),
m_maximumUndoSteps(5),
m_maximumRedoSteps(5),
m_formAppearanceFlags(pdf::PDFFormManager::getDefaultApperanceFlags())
m_formAppearanceFlags(pdf::PDFFormManager::getDefaultApperanceFlags()),
m_signatureVerificationEnabled(true),
m_signatureTreatWarningsAsErrors(false),
m_signatureIgnoreCertificateValidityTime(false),
m_signatureUseSystemStore(true)
{
}

View File

@@ -83,6 +83,12 @@ public:
// Form settings
pdf::PDFFormManager::FormAppearanceFlags m_formAppearanceFlags;
// Signature settings
bool m_signatureVerificationEnabled;
bool m_signatureTreatWarningsAsErrors;
bool m_signatureIgnoreCertificateValidityTime;
bool m_signatureUseSystemStore;
};
const Settings& getSettings() const { return m_settings; }

View File

@@ -59,6 +59,7 @@ PDFViewerSettingsDialog::PDFViewerSettingsDialog(const PDFViewerSettings::Settin
new QListWidgetItem(QIcon(":/resources/ui.svg"), tr("UI"), ui->optionsPagesWidget, UISettings);
new QListWidgetItem(QIcon(":/resources/speech.svg"), tr("Speech"), ui->optionsPagesWidget, SpeechSettings);
new QListWidgetItem(QIcon(":/resources/form-settings.svg"), tr("Forms"), ui->optionsPagesWidget, FormSettings);
new QListWidgetItem(QIcon(":/resources/signature.svg"), tr("Signature"), ui->optionsPagesWidget, SignatureSettings);
ui->renderingEngineComboBox->addItem(tr("Software"), static_cast<int>(pdf::RendererEngine::Software));
ui->renderingEngineComboBox->addItem(tr("Hardware accelerated (OpenGL)"), static_cast<int>(pdf::RendererEngine::OpenGL));
@@ -198,6 +199,10 @@ void PDFViewerSettingsDialog::on_optionsPagesWidget_currentItemChanged(QListWidg
ui->stackedWidget->setCurrentWidget(ui->formPage);
break;
case SignatureSettings:
ui->stackedWidget->setCurrentWidget(ui->signaturePage);
break;
default:
Q_ASSERT(false);
break;
@@ -327,6 +332,12 @@ void PDFViewerSettingsDialog::loadData()
// Form Settings
ui->formHighlightFieldsCheckBox->setChecked(m_settings.m_formAppearanceFlags.testFlag(pdf::PDFFormManager::HighlightFields));
ui->formHighlightRequiredFieldsCheckBox->setChecked(m_settings.m_formAppearanceFlags.testFlag(pdf::PDFFormManager::HighlightRequiredFields));
// Signature Settings
ui->signatureVerificationEnableCheckBox->setChecked(m_settings.m_signatureVerificationEnabled);
ui->signatureStrictModeEnabledCheckBox->setChecked(m_settings.m_signatureTreatWarningsAsErrors);
ui->signatureIgnoreExpiredCheckBox->setChecked(m_settings.m_signatureIgnoreCertificateValidityTime);
ui->signatureUseSystemCertificateStoreCheckBox->setChecked(m_settings.m_signatureUseSystemStore);
}
void PDFViewerSettingsDialog::saveData()
@@ -514,6 +525,22 @@ void PDFViewerSettingsDialog::saveData()
{
m_settings.m_maximumRedoSteps = ui->maximumRedoStepsEdit->value();
}
else if (sender == ui->signatureVerificationEnableCheckBox)
{
m_settings.m_signatureVerificationEnabled = ui->signatureVerificationEnableCheckBox->isChecked();
}
else if (sender == ui->signatureStrictModeEnabledCheckBox)
{
m_settings.m_signatureTreatWarningsAsErrors = ui->signatureStrictModeEnabledCheckBox->isChecked();
}
else if (sender == ui->signatureIgnoreExpiredCheckBox)
{
m_settings.m_signatureIgnoreCertificateValidityTime = ui->signatureIgnoreExpiredCheckBox->isChecked();
}
else if (sender == ui->signatureUseSystemCertificateStoreCheckBox)
{
m_settings.m_signatureUseSystemStore = ui->signatureUseSystemCertificateStoreCheckBox->isChecked();
}
const bool loadData = !qobject_cast<const QDoubleSpinBox*>(sender) && !qobject_cast<const QSpinBox*>(sender);
if (loadData)

View File

@@ -71,7 +71,8 @@ public:
SecuritySettings,
UISettings,
SpeechSettings,
FormSettings
FormSettings,
SignatureSettings
};
const PDFViewerSettings::Settings& getSettings() const { return m_settings; }

View File

@@ -26,7 +26,7 @@
<item>
<widget class="QStackedWidget" name="stackedWidget">
<property name="currentIndex">
<number>1</number>
<number>10</number>
</property>
<widget class="QWidget" name="enginePage">
<layout class="QVBoxLayout" name="enginePageLayout">
@@ -1176,6 +1176,156 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="signaturePage">
<layout class="QVBoxLayout" name="signaturePageLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="signatureGroupBox">
<property name="title">
<string>Digital Signature Verification</string>
</property>
<layout class="QVBoxLayout" name="signatureGroupBoxLayout">
<item>
<layout class="QGridLayout" name="signatureWidgetsLayout">
<item row="2" column="0">
<widget class="QLabel" name="signatureIgnoreExpiredLabel">
<property name="text">
<string>Ignore expired certificates</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="signatureVerificationLabel">
<property name="text">
<string>Signature verification</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="signatureStrictModeLabel">
<property name="text">
<string>Strict mode</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="signatureVerificationEnableCheckBox">
<property name="text">
<string>Enable</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="signatureStrictModeEnabledCheckBox">
<property name="text">
<string>Enable</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="signatureUseSystemCertificateStoreLabel">
<property name="text">
<string>Use system certificate store</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="signatureIgnoreExpiredCheckBox">
<property name="text">
<string>Enable</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="signatureUseSystemCertificateStoreCheckBox">
<property name="text">
<string>Enable</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="signatureCertificatesTableLabel">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Trusted certificate store&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<widget class="QTableWidget" name="trustedCertificateStoreTableWidget"/>
</item>
<item>
<layout class="QHBoxLayout" name="trustedCertificateStoreButtonsLayout">
<item>
<spacer name="trustedCertificateStoreButtonsSpacer">
<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="removeCertificateButton">
<property name="text">
<string>Remove</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="trustedCertificateStoreDownloadEUTLButton">
<property name="text">
<string>Download EUTL</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="signatureInfoLabel">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Settings for verification of digital signatures. Digital signatures are verified as strict, as possible, to avoid any malicious content or signature manipulation. Verification can also be turned off, if it is not wanted. When &lt;span style=&quot; font-weight:600;&quot;&gt;Strict mode&lt;/span&gt; is turned on, every warning is treated as error. You can also ignore certificate expiration date, but do this only, if you know what you are doing, because it is dangerous. For verification, list of trusted certificates is used. System certificates can be inserted into the list, and also you can manage your own list of trusted certificates. Also, certificates from EUTL (EU trusted list) can be downloaded into the trusted certificate storage.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>

View File

@@ -0,0 +1,94 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="30mm"
height="30mm"
viewBox="0 0 30 30"
version="1.1"
id="svg8"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="signature.svg">
<defs
id="defs2">
<inkscape:path-effect
effect="spiro"
id="path-effect831"
is_visible="true" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.6"
inkscape:cx="166.75422"
inkscape:cy="113.44001"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="3840"
inkscape:window-height="2035"
inkscape:window-x="-13"
inkscape:window-y="-13"
inkscape:window-maximized="1" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<cc:license
rdf:resource="http://creativecommons.org/licenses/by-sa/4.0/" />
<dc:creator>
<cc:Agent>
<dc:title>Jakub Melka</dc:title>
</cc:Agent>
</dc:creator>
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/by-sa/4.0/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Notice" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Attribution" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#ShareAlike" />
</cc:License>
</rdf:RDF>
</metadata>
<g
inkscape:label="Vrstva 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-267)">
<g
aria-label="✍"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:25.39999962px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
id="text837">
<path
d="m 24.320405,282.11063 h 4.7625 v 12.47675 H 2.7899365 q -0.471289,0 -0.8805664,-0.17363 -0.396875,-0.17363 -0.7069336,-0.48369 -0.29765621,-0.29766 -0.47128902,-0.70693 -0.17363281,-0.39688 -0.17363281,-0.86817 0,-0.59531 0.24804687,-1.00459 0.24804686,-0.40928 0.64492186,-0.80615 v -1.15342 l 1.5875,-1.5875 q -0.3348633,-0.47129 -0.5208984,-1.00459 -0.1736329,-0.5457 -0.1736329,-1.12861 0,-0.70693 0.2604493,-1.35186 0.2728515,-0.65732 0.7813476,-1.16582 l 5.3454101,-5.34541 q 0.5084961,-0.50849 1.1534179,-0.76894 0.657324,-0.27285 1.364258,-0.27285 h 2.827734 l 6.98252,-6.98252 4.303613,4.30361 -4.526856,4.52686 z m -0.744141,1.78593 -4.638476,-4.65088 q -0.111621,0.11163 -0.496094,0.4961 -0.37207,0.38447 -0.880566,0.89297 -0.496094,0.50849 -1.066602,1.079 -0.558105,0.57051 -1.041797,1.0542 -0.483691,0.48369 -0.818554,0.81856 -0.334864,0.32246 -0.396875,0.35966 0.384472,0.13643 0.483691,0.47129 0.111621,0.32246 0.111621,0.68213 0,0.14883 -0.0124,0.29766 0,0.13642 0,0.27285 0,0.74414 -0.285254,1.40146 -0.272852,0.64493 -0.756543,1.12862 -0.483691,0.48369 -1.141016,0.76894 -0.644922,0.27285 -1.389062,0.27285 H 7.6888622 q -0.3720703,0 -0.6945312,0.13643 -0.322461,0.14883 -0.5705078,0.38447 -0.2356446,0.24805 -0.3844727,0.57051 -0.1364258,0.32246 -0.1364258,0.69453 0,0.33486 0.1364258,0.68213 0.062012,0.0992 0.1116211,0.18604 0.049609,0.0992 0.1116211,0.19843 0.2480469,0.32246 0.6325195,0.5085 0.396875,0.19844 0.79375,0.19844 H 27.296967 v -8.90489 z m -18.566308,8.01192 0.1240234,-0.11162 q -0.1240234,-0.35967 -0.1240234,-0.76895 0,-0.5581 0.2108398,-1.0418 0.2108399,-0.48369 0.5705079,-0.84335 0.3720703,-0.37208 0.8557617,-0.58292 0.4836914,-0.21083 1.0417968,-0.21083 H 8.581831 q 3.150195,-3.1254 6.238379,-6.22598 3.100586,-3.11299 6.238379,-6.25078 l -1.785938,-1.78594 q -4.005957,4.00596 -8.024316,8.01191 -4.0183595,4.00596 -8.0119141,8.03672 v 1.77354 z m 6.238379,-13.36973 q -0.731738,0 -1.2526369,0.5209 l -5.3454101,5.35781 q -0.2480468,0.24805 -0.396875,0.58291 -0.1364257,0.32246 -0.1364257,0.68213 0,0.48369 0.2232422,0.84336 0.2356445,0.35967 0.5953124,0.64492 l 8.6196291,-8.63203 z m 9.810254,-6.23838 -1.153418,1.15342 1.785937,1.78594 1.153418,-1.15342 z"
style="stroke-width:0.26458332"
id="path839"
inkscape:connector-curvature="0" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.2 KiB