| /* poppler-form.h: qt interface to poppler | 
 |  * Copyright (C) 2007-2008, 2011, Pino Toscano <pino@kde.org> | 
 |  * Copyright (C) 2008, 2011, 2012, 2015-2023 Albert Astals Cid <aacid@kde.org> | 
 |  * Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> | 
 |  * Copyright (C) 2012, Adam Reichold <adamreichold@myopera.com> | 
 |  * Copyright (C) 2016, Hanno Meyer-Thurow <h.mth@web.de> | 
 |  * Copyright (C) 2017, Hans-Ulrich Jüttner <huj@froreich-bioscientia.de> | 
 |  * Copyright (C) 2018, Andre Heinecke <aheinecke@intevation.de> | 
 |  * Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>. Work sponsored by the LiMux project of the city of Munich | 
 |  * Copyright (C) 2018 Chinmoy Ranjan Pradhan <chinmoyrp65@protonmail.com> | 
 |  * Copyright (C) 2018, 2020, 2021 Oliver Sander <oliver.sander@tu-dresden.de> | 
 |  * Copyright (C) 2019 João Netto <joaonetto901@gmail.com> | 
 |  * Copyright (C) 2020 David García Garzón <voki@canvoki.net> | 
 |  * Copyright (C) 2020 Thorsten Behrens <Thorsten.Behrens@CIB.de> | 
 |  * Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>. Work sponsored by Technische Universität Dresden | 
 |  * Copyright (C) 2021 Georgiy Sgibnev <georgiy@sgibnev.com>. Work sponsored by lab50.net. | 
 |  * Copyright (C) 2021 Theofilos Intzoglou <int.teo@gmail.com> | 
 |  * Copyright (C) 2022 Alexander Sulfrian <asulfrian@zedat.fu-berlin.de> | 
 |  * Copyright (C) 2023 g10 Code GmbH, Author: Sune Stolborg Vuorela <sune@vuorela.dk> | 
 |  * | 
 |  * This program is free software; you can redistribute it and/or modify | 
 |  * it under the terms of the GNU General Public License as published by | 
 |  * the Free Software Foundation; either version 2, or (at your option) | 
 |  * any later version. | 
 |  * | 
 |  * This program 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 General Public License for more details. | 
 |  * | 
 |  * You should have received a copy of the GNU General Public License | 
 |  * along with this program; if not, write to the Free Software | 
 |  * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. | 
 |  */ | 
 |  | 
 | #include "poppler-form.h" | 
 |  | 
 | #include <config.h> | 
 |  | 
 | #include <QtCore/QSizeF> | 
 | #include <QUrl> | 
 |  | 
 | #include <Form.h> | 
 | #include <Object.h> | 
 | #include <Link.h> | 
 | #include <SignatureInfo.h> | 
 | #include <CertificateInfo.h> | 
 | #include <CryptoSignBackend.h> | 
 | #ifdef ENABLE_NSS3 | 
 | #    include <NSSCryptoSignBackend.h> | 
 | #endif | 
 |  | 
 | #include "poppler-page-private.h" | 
 | #include "poppler-private.h" | 
 | #include "poppler-annotation-helper.h" | 
 |  | 
 | #include <cmath> | 
 | #include <cctype> | 
 |  | 
 | namespace { | 
 |  | 
 | Qt::Alignment formTextAlignment(::FormWidget *fm) | 
 | { | 
 |     Qt::Alignment qtalign = Qt::AlignLeft; | 
 |     switch (fm->getField()->getTextQuadding()) { | 
 |     case VariableTextQuadding::centered: | 
 |         qtalign = Qt::AlignHCenter; | 
 |         break; | 
 |     case VariableTextQuadding::rightJustified: | 
 |         qtalign = Qt::AlignRight; | 
 |         break; | 
 |     case VariableTextQuadding::leftJustified: | 
 |         qtalign = Qt::AlignLeft; | 
 |     } | 
 |     return qtalign; | 
 | } | 
 |  | 
 | } | 
 |  | 
 | namespace Poppler { | 
 |  | 
 | FormFieldIcon::FormFieldIcon(FormFieldIconData *data) : d_ptr(data) { } | 
 |  | 
 | FormFieldIcon::FormFieldIcon(const FormFieldIcon &ffIcon) | 
 | { | 
 |     d_ptr = new FormFieldIconData; | 
 |     d_ptr->icon = ffIcon.d_ptr->icon; | 
 | } | 
 |  | 
 | FormFieldIcon &FormFieldIcon::operator=(const FormFieldIcon &ffIcon) | 
 | { | 
 |     if (this != &ffIcon) { | 
 |         delete d_ptr; | 
 |         d_ptr = nullptr; | 
 |  | 
 |         d_ptr = new FormFieldIconData; | 
 |         *d_ptr = *ffIcon.d_ptr; | 
 |     } | 
 |  | 
 |     return *this; | 
 | } | 
 |  | 
 | FormFieldIcon::~FormFieldIcon() | 
 | { | 
 |     delete d_ptr; | 
 | } | 
 |  | 
 | FormField::FormField(std::unique_ptr<FormFieldData> dd) : m_formData(std::move(dd)) | 
 | { | 
 |     if (m_formData->page) { | 
 |         const int rotation = m_formData->page->getRotate(); | 
 |         // reading the coords | 
 |         double left, top, right, bottom; | 
 |         m_formData->fm->getRect(&left, &bottom, &right, &top); | 
 |         // build a normalized transform matrix for this page at 100% scale | 
 |         GfxState gfxState(72.0, 72.0, m_formData->page->getCropBox(), rotation, true); | 
 |         const double *gfxCTM = gfxState.getCTM(); | 
 |         double MTX[6]; | 
 |         double pageWidth = m_formData->page->getCropWidth(); | 
 |         double pageHeight = m_formData->page->getCropHeight(); | 
 |         // landscape and seascape page rotation: be sure to use the correct (== rotated) page size | 
 |         if (((rotation / 90) % 2) == 1) { | 
 |             qSwap(pageWidth, pageHeight); | 
 |         } | 
 |         for (int i = 0; i < 6; i += 2) { | 
 |             MTX[i] = gfxCTM[i] / pageWidth; | 
 |             MTX[i + 1] = gfxCTM[i + 1] / pageHeight; | 
 |         } | 
 |         QPointF topLeft; | 
 |         XPDFReader::transform(MTX, qMin(left, right), qMax(top, bottom), topLeft); | 
 |         QPointF bottomRight; | 
 |         XPDFReader::transform(MTX, qMax(left, right), qMin(top, bottom), bottomRight); | 
 |         m_formData->box = QRectF(topLeft, QSizeF(bottomRight.x() - topLeft.x(), bottomRight.y() - topLeft.y())); | 
 |     } | 
 | } | 
 |  | 
 | FormField::~FormField() = default; | 
 |  | 
 | QRectF FormField::rect() const | 
 | { | 
 |     return m_formData->box; | 
 | } | 
 |  | 
 | int FormField::id() const | 
 | { | 
 |     return m_formData->fm->getID(); | 
 | } | 
 |  | 
 | QString FormField::name() const | 
 | { | 
 |     QString name; | 
 |     if (const GooString *goo = m_formData->fm->getPartialName()) { | 
 |         name = UnicodeParsedString(goo); | 
 |     } | 
 |     return name; | 
 | } | 
 |  | 
 | void FormField::setName(const QString &name) const | 
 | { | 
 |     GooString *goo = QStringToGooString(name); | 
 |     m_formData->fm->setPartialName(*goo); | 
 |     delete goo; | 
 | } | 
 |  | 
 | QString FormField::fullyQualifiedName() const | 
 | { | 
 |     QString name; | 
 |     if (GooString *goo = m_formData->fm->getFullyQualifiedName()) { | 
 |         name = UnicodeParsedString(goo); | 
 |     } | 
 |     return name; | 
 | } | 
 |  | 
 | QString FormField::uiName() const | 
 | { | 
 |     QString name; | 
 |     if (const GooString *goo = m_formData->fm->getAlternateUiName()) { | 
 |         name = UnicodeParsedString(goo); | 
 |     } | 
 |     return name; | 
 | } | 
 |  | 
 | bool FormField::isReadOnly() const | 
 | { | 
 |     return m_formData->fm->isReadOnly(); | 
 | } | 
 |  | 
 | void FormField::setReadOnly(bool value) | 
 | { | 
 |     m_formData->fm->setReadOnly(value); | 
 | } | 
 |  | 
 | bool FormField::isVisible() const | 
 | { | 
 |     const unsigned int flags = m_formData->fm->getWidgetAnnotation()->getFlags(); | 
 |     if (flags & Annot::flagHidden) { | 
 |         return false; | 
 |     } | 
 |     if (flags & Annot::flagNoView) { | 
 |         return false; | 
 |     } | 
 |     return true; | 
 | } | 
 |  | 
 | void FormField::setVisible(bool value) | 
 | { | 
 |     unsigned int flags = m_formData->fm->getWidgetAnnotation()->getFlags(); | 
 |     if (value) { | 
 |         flags &= ~Annot::flagHidden; | 
 |         flags &= ~Annot::flagNoView; | 
 |     } else { | 
 |         flags |= Annot::flagHidden; | 
 |     } | 
 |     m_formData->fm->getWidgetAnnotation()->setFlags(flags); | 
 | } | 
 |  | 
 | bool FormField::isPrintable() const | 
 | { | 
 |     return (m_formData->fm->getWidgetAnnotation()->getFlags() & Annot::flagPrint); | 
 | } | 
 |  | 
 | void FormField::setPrintable(bool value) | 
 | { | 
 |     unsigned int flags = m_formData->fm->getWidgetAnnotation()->getFlags(); | 
 |     if (value) { | 
 |         flags |= Annot::flagPrint; | 
 |     } else { | 
 |         flags &= ~Annot::flagPrint; | 
 |     } | 
 |     m_formData->fm->getWidgetAnnotation()->setFlags(flags); | 
 | } | 
 |  | 
 | std::unique_ptr<Link> FormField::activationAction() const | 
 | { | 
 |     if (::LinkAction *act = m_formData->fm->getActivationAction()) { | 
 |         return PageData::convertLinkActionToLink(act, m_formData->doc, QRectF()); | 
 |     } | 
 |  | 
 |     return {}; | 
 | } | 
 |  | 
 | std::unique_ptr<Link> FormField::additionalAction(AdditionalActionType type) const | 
 | { | 
 |     Annot::FormAdditionalActionsType actionType = Annot::actionFieldModified; | 
 |     switch (type) { | 
 |     case FieldModified: | 
 |         actionType = Annot::actionFieldModified; | 
 |         break; | 
 |     case FormatField: | 
 |         actionType = Annot::actionFormatField; | 
 |         break; | 
 |     case ValidateField: | 
 |         actionType = Annot::actionValidateField; | 
 |         break; | 
 |     case CalculateField: | 
 |         actionType = Annot::actionCalculateField; | 
 |         break; | 
 |     } | 
 |  | 
 |     if (std::unique_ptr<::LinkAction> act = m_formData->fm->getAdditionalAction(actionType)) { | 
 |         return PageData::convertLinkActionToLink(act.get(), m_formData->doc, QRectF()); | 
 |     } | 
 |  | 
 |     return {}; | 
 | } | 
 |  | 
 | std::unique_ptr<Link> FormField::additionalAction(Annotation::AdditionalActionType type) const | 
 | { | 
 |     ::AnnotWidget *w = m_formData->fm->getWidgetAnnotation(); | 
 |     if (!w) { | 
 |         return {}; | 
 |     } | 
 |  | 
 |     const Annot::AdditionalActionsType actionType = toPopplerAdditionalActionType(type); | 
 |  | 
 |     if (std::unique_ptr<::LinkAction> act = w->getAdditionalAction(actionType)) { | 
 |         return PageData::convertLinkActionToLink(act.get(), m_formData->doc, QRectF()); | 
 |     } | 
 |  | 
 |     return {}; | 
 | } | 
 |  | 
 | FormFieldButton::FormFieldButton(DocumentData *doc, ::Page *p, ::FormWidgetButton *w) : FormField(std::make_unique<FormFieldData>(doc, p, w)) { } | 
 |  | 
 | FormFieldButton::~FormFieldButton() { } | 
 |  | 
 | FormFieldButton::FormType FormFieldButton::type() const | 
 | { | 
 |     return FormField::FormButton; | 
 | } | 
 |  | 
 | FormFieldButton::ButtonType FormFieldButton::buttonType() const | 
 | { | 
 |     FormWidgetButton *fwb = static_cast<FormWidgetButton *>(m_formData->fm); | 
 |     switch (fwb->getButtonType()) { | 
 |     case formButtonCheck: | 
 |         return FormFieldButton::CheckBox; | 
 |         break; | 
 |     case formButtonPush: | 
 |         return FormFieldButton::Push; | 
 |         break; | 
 |     case formButtonRadio: | 
 |         return FormFieldButton::Radio; | 
 |         break; | 
 |     } | 
 |     return FormFieldButton::CheckBox; | 
 | } | 
 |  | 
 | QString FormFieldButton::caption() const | 
 | { | 
 |     FormWidgetButton *fwb = static_cast<FormWidgetButton *>(m_formData->fm); | 
 |     QString ret; | 
 |     if (fwb->getButtonType() == formButtonPush) { | 
 |         Dict *dict = m_formData->fm->getObj()->getDict(); | 
 |         Object obj1 = dict->lookup("MK"); | 
 |         if (obj1.isDict()) { | 
 |             AnnotAppearanceCharacs appearCharacs(obj1.getDict()); | 
 |             if (appearCharacs.getNormalCaption()) { | 
 |                 ret = UnicodeParsedString(appearCharacs.getNormalCaption()); | 
 |             } | 
 |         } | 
 |     } else { | 
 |         if (const char *goo = fwb->getOnStr()) { | 
 |             ret = QString::fromUtf8(goo); | 
 |         } | 
 |     } | 
 |     return ret; | 
 | } | 
 |  | 
 | FormFieldIcon FormFieldButton::icon() const | 
 | { | 
 |     FormWidgetButton *fwb = static_cast<FormWidgetButton *>(m_formData->fm); | 
 |     if (fwb->getButtonType() == formButtonPush) { | 
 |         Dict *dict = m_formData->fm->getObj()->getDict(); | 
 |         FormFieldIconData *data = new FormFieldIconData; | 
 |         data->icon = dict; | 
 |         return FormFieldIcon(data); | 
 |     } | 
 |     return FormFieldIcon(nullptr); | 
 | } | 
 |  | 
 | void FormFieldButton::setIcon(const FormFieldIcon &icon) | 
 | { | 
 |     if (FormFieldIconData::getData(icon) == nullptr) { | 
 |         return; | 
 |     } | 
 |  | 
 |     FormWidgetButton *fwb = static_cast<FormWidgetButton *>(m_formData->fm); | 
 |     if (fwb->getButtonType() == formButtonPush) { | 
 |         ::AnnotWidget *w = m_formData->fm->getWidgetAnnotation(); | 
 |         FormFieldIconData *data = FormFieldIconData::getData(icon); | 
 |         if (data->icon != nullptr) { | 
 |             w->setNewAppearance(data->icon->lookup("AP")); | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | bool FormFieldButton::state() const | 
 | { | 
 |     FormWidgetButton *fwb = static_cast<FormWidgetButton *>(m_formData->fm); | 
 |     return fwb->getState(); | 
 | } | 
 |  | 
 | void FormFieldButton::setState(bool state) | 
 | { | 
 |     FormWidgetButton *fwb = static_cast<FormWidgetButton *>(m_formData->fm); | 
 |     fwb->setState((bool)state); | 
 | } | 
 |  | 
 | QList<int> FormFieldButton::siblings() const | 
 | { | 
 |     FormWidgetButton *fwb = static_cast<FormWidgetButton *>(m_formData->fm); | 
 |     ::FormFieldButton *ffb = static_cast<::FormFieldButton *>(fwb->getField()); | 
 |     if (fwb->getButtonType() == formButtonPush) { | 
 |         return QList<int>(); | 
 |     } | 
 |  | 
 |     QList<int> ret; | 
 |     for (int i = 0; i < ffb->getNumSiblings(); ++i) { | 
 |         ::FormFieldButton *sibling = static_cast<::FormFieldButton *>(ffb->getSibling(i)); | 
 |         for (int j = 0; j < sibling->getNumWidgets(); ++j) { | 
 |             FormWidget *w = sibling->getWidget(j); | 
 |             if (w) { | 
 |                 ret.append(w->getID()); | 
 |             } | 
 |         } | 
 |     } | 
 |  | 
 |     return ret; | 
 | } | 
 |  | 
 | FormFieldText::FormFieldText(DocumentData *doc, ::Page *p, ::FormWidgetText *w) : FormField(std::make_unique<FormFieldData>(doc, p, w)) { } | 
 |  | 
 | FormFieldText::~FormFieldText() { } | 
 |  | 
 | FormField::FormType FormFieldText::type() const | 
 | { | 
 |     return FormField::FormText; | 
 | } | 
 |  | 
 | FormFieldText::TextType FormFieldText::textType() const | 
 | { | 
 |     FormWidgetText *fwt = static_cast<FormWidgetText *>(m_formData->fm); | 
 |     if (fwt->isFileSelect()) { | 
 |         return FormFieldText::FileSelect; | 
 |     } else if (fwt->isMultiline()) { | 
 |         return FormFieldText::Multiline; | 
 |     } | 
 |     return FormFieldText::Normal; | 
 | } | 
 |  | 
 | QString FormFieldText::text() const | 
 | { | 
 |     const GooString *goo = static_cast<FormWidgetText *>(m_formData->fm)->getContent(); | 
 |     return UnicodeParsedString(goo); | 
 | } | 
 |  | 
 | void FormFieldText::setText(const QString &text) | 
 | { | 
 |     FormWidgetText *fwt = static_cast<FormWidgetText *>(m_formData->fm); | 
 |     GooString *goo = QStringToUnicodeGooString(text); | 
 |     fwt->setContent(goo); | 
 |     delete goo; | 
 | } | 
 |  | 
 | void FormFieldText::setAppearanceText(const QString &text) | 
 | { | 
 |     FormWidgetText *fwt = static_cast<FormWidgetText *>(m_formData->fm); | 
 |     GooString *goo = QStringToUnicodeGooString(text); | 
 |     fwt->setAppearanceContent(goo); | 
 |     delete goo; | 
 | } | 
 |  | 
 | bool FormFieldText::isPassword() const | 
 | { | 
 |     FormWidgetText *fwt = static_cast<FormWidgetText *>(m_formData->fm); | 
 |     return fwt->isPassword(); | 
 | } | 
 |  | 
 | bool FormFieldText::isRichText() const | 
 | { | 
 |     FormWidgetText *fwt = static_cast<FormWidgetText *>(m_formData->fm); | 
 |     return fwt->isRichText(); | 
 | } | 
 |  | 
 | int FormFieldText::maximumLength() const | 
 | { | 
 |     FormWidgetText *fwt = static_cast<FormWidgetText *>(m_formData->fm); | 
 |     const int maxlen = fwt->getMaxLen(); | 
 |     return maxlen > 0 ? maxlen : -1; | 
 | } | 
 |  | 
 | Qt::Alignment FormFieldText::textAlignment() const | 
 | { | 
 |     return formTextAlignment(m_formData->fm); | 
 | } | 
 |  | 
 | bool FormFieldText::canBeSpellChecked() const | 
 | { | 
 |     FormWidgetText *fwt = static_cast<FormWidgetText *>(m_formData->fm); | 
 |     return !fwt->noSpellCheck(); | 
 | } | 
 |  | 
 | double FormFieldText::getFontSize() const | 
 | { | 
 |     FormWidgetText *fwt = static_cast<FormWidgetText *>(m_formData->fm); | 
 |     return fwt->getTextFontSize(); | 
 | } | 
 |  | 
 | void FormFieldText::setFontSize(int fontSize) | 
 | { | 
 |     FormWidgetText *fwt = static_cast<FormWidgetText *>(m_formData->fm); | 
 |     fwt->setTextFontSize(fontSize); | 
 | } | 
 |  | 
 | FormFieldChoice::FormFieldChoice(DocumentData *doc, ::Page *p, ::FormWidgetChoice *w) : FormField(std::make_unique<FormFieldData>(doc, p, w)) { } | 
 |  | 
 | FormFieldChoice::~FormFieldChoice() { } | 
 |  | 
 | FormFieldChoice::FormType FormFieldChoice::type() const | 
 | { | 
 |     return FormField::FormChoice; | 
 | } | 
 |  | 
 | FormFieldChoice::ChoiceType FormFieldChoice::choiceType() const | 
 | { | 
 |     FormWidgetChoice *fwc = static_cast<FormWidgetChoice *>(m_formData->fm); | 
 |     if (fwc->isCombo()) { | 
 |         return FormFieldChoice::ComboBox; | 
 |     } | 
 |     return FormFieldChoice::ListBox; | 
 | } | 
 |  | 
 | QStringList FormFieldChoice::choices() const | 
 | { | 
 |     FormWidgetChoice *fwc = static_cast<FormWidgetChoice *>(m_formData->fm); | 
 |     QStringList ret; | 
 |     int num = fwc->getNumChoices(); | 
 |     ret.reserve(num); | 
 |     for (int i = 0; i < num; ++i) { | 
 |         ret.append(UnicodeParsedString(fwc->getChoice(i))); | 
 |     } | 
 |     return ret; | 
 | } | 
 |  | 
 | QVector<QPair<QString, QString>> FormFieldChoice::choicesWithExportValues() const | 
 | { | 
 |     FormWidgetChoice *fwc = static_cast<FormWidgetChoice *>(m_formData->fm); | 
 |     QVector<QPair<QString, QString>> ret; | 
 |     const int num = fwc->getNumChoices(); | 
 |     ret.reserve(num); | 
 |     for (int i = 0; i < num; ++i) { | 
 |         const QString display = UnicodeParsedString(fwc->getChoice(i)); | 
 |         const GooString *exportValueG = fwc->getExportVal(i); | 
 |         const QString exportValue = exportValueG ? UnicodeParsedString(exportValueG) : display; | 
 |         ret.append({ display, exportValue }); | 
 |     } | 
 |     return ret; | 
 | } | 
 |  | 
 | bool FormFieldChoice::isEditable() const | 
 | { | 
 |     FormWidgetChoice *fwc = static_cast<FormWidgetChoice *>(m_formData->fm); | 
 |     return fwc->isCombo() ? fwc->hasEdit() : false; | 
 | } | 
 |  | 
 | bool FormFieldChoice::multiSelect() const | 
 | { | 
 |     FormWidgetChoice *fwc = static_cast<FormWidgetChoice *>(m_formData->fm); | 
 |     return !fwc->isCombo() ? fwc->isMultiSelect() : false; | 
 | } | 
 |  | 
 | QList<int> FormFieldChoice::currentChoices() const | 
 | { | 
 |     FormWidgetChoice *fwc = static_cast<FormWidgetChoice *>(m_formData->fm); | 
 |     int num = fwc->getNumChoices(); | 
 |     QList<int> choices; | 
 |     for (int i = 0; i < num; ++i) { | 
 |         if (fwc->isSelected(i)) { | 
 |             choices.append(i); | 
 |         } | 
 |     } | 
 |     return choices; | 
 | } | 
 |  | 
 | void FormFieldChoice::setCurrentChoices(const QList<int> &choice) | 
 | { | 
 |     FormWidgetChoice *fwc = static_cast<FormWidgetChoice *>(m_formData->fm); | 
 |     fwc->deselectAll(); | 
 |     for (int i = 0; i < choice.count(); ++i) { | 
 |         fwc->select(choice.at(i)); | 
 |     } | 
 | } | 
 |  | 
 | QString FormFieldChoice::editChoice() const | 
 | { | 
 |     FormWidgetChoice *fwc = static_cast<FormWidgetChoice *>(m_formData->fm); | 
 |  | 
 |     if (fwc->isCombo() && fwc->hasEdit()) { | 
 |         return UnicodeParsedString(fwc->getEditChoice()); | 
 |     } else { | 
 |         return QString(); | 
 |     } | 
 | } | 
 |  | 
 | void FormFieldChoice::setEditChoice(const QString &text) | 
 | { | 
 |     FormWidgetChoice *fwc = static_cast<FormWidgetChoice *>(m_formData->fm); | 
 |  | 
 |     if (fwc->isCombo() && fwc->hasEdit()) { | 
 |         GooString *goo = QStringToUnicodeGooString(text); | 
 |         fwc->setEditChoice(goo); | 
 |         delete goo; | 
 |     } | 
 | } | 
 |  | 
 | Qt::Alignment FormFieldChoice::textAlignment() const | 
 | { | 
 |     return formTextAlignment(m_formData->fm); | 
 | } | 
 |  | 
 | bool FormFieldChoice::canBeSpellChecked() const | 
 | { | 
 |     FormWidgetChoice *fwc = static_cast<FormWidgetChoice *>(m_formData->fm); | 
 |     return !fwc->noSpellCheck(); | 
 | } | 
 |  | 
 | class CertificateInfoPrivate | 
 | { | 
 | public: | 
 |     struct EntityInfo | 
 |     { | 
 |         QString common_name; | 
 |         QString email_address; | 
 |         QString org_name; | 
 |         QString distinguished_name; | 
 |     }; | 
 |  | 
 |     EntityInfo issuer_info; | 
 |     EntityInfo subject_info; | 
 |     QString nick_name; | 
 |     QByteArray certificate_der; | 
 |     QByteArray serial_number; | 
 |     QByteArray public_key; | 
 |     QDateTime validity_start; | 
 |     QDateTime validity_end; | 
 |     int public_key_type; | 
 |     int public_key_strength; | 
 |     int ku_extensions; | 
 |     int version; | 
 |     bool is_self_signed; | 
 |     bool is_null; | 
 |     CertificateInfo::KeyLocation keyLocation; | 
 | }; | 
 |  | 
 | CertificateInfo::CertificateInfo() : d_ptr(new CertificateInfoPrivate()) | 
 | { | 
 |     d_ptr->is_null = true; | 
 | } | 
 |  | 
 | CertificateInfo::CertificateInfo(CertificateInfoPrivate *priv) : d_ptr(priv) { } | 
 |  | 
 | CertificateInfo::CertificateInfo(const CertificateInfo &other) : d_ptr(other.d_ptr) { } | 
 |  | 
 | CertificateInfo::~CertificateInfo() = default; | 
 |  | 
 | CertificateInfo &CertificateInfo::operator=(const CertificateInfo &other) | 
 | { | 
 |     if (this != &other) { | 
 |         d_ptr = other.d_ptr; | 
 |     } | 
 |  | 
 |     return *this; | 
 | } | 
 |  | 
 | bool CertificateInfo::isNull() const | 
 | { | 
 |     Q_D(const CertificateInfo); | 
 |     return d->is_null; | 
 | } | 
 |  | 
 | int CertificateInfo::version() const | 
 | { | 
 |     Q_D(const CertificateInfo); | 
 |     return d->version; | 
 | } | 
 |  | 
 | QByteArray CertificateInfo::serialNumber() const | 
 | { | 
 |     Q_D(const CertificateInfo); | 
 |     return d->serial_number; | 
 | } | 
 |  | 
 | QString CertificateInfo::issuerInfo(EntityInfoKey key) const | 
 | { | 
 |     Q_D(const CertificateInfo); | 
 |     switch (key) { | 
 |     case CommonName: | 
 |         return d->issuer_info.common_name; | 
 |     case DistinguishedName: | 
 |         return d->issuer_info.distinguished_name; | 
 |     case EmailAddress: | 
 |         return d->issuer_info.email_address; | 
 |     case Organization: | 
 |         return d->issuer_info.org_name; | 
 |     default: | 
 |         return QString(); | 
 |     } | 
 | } | 
 |  | 
 | QString CertificateInfo::subjectInfo(EntityInfoKey key) const | 
 | { | 
 |     Q_D(const CertificateInfo); | 
 |     switch (key) { | 
 |     case CommonName: | 
 |         return d->subject_info.common_name; | 
 |     case DistinguishedName: | 
 |         return d->subject_info.distinguished_name; | 
 |     case EmailAddress: | 
 |         return d->subject_info.email_address; | 
 |     case Organization: | 
 |         return d->subject_info.org_name; | 
 |     default: | 
 |         return QString(); | 
 |     } | 
 | } | 
 |  | 
 | QString CertificateInfo::nickName() const | 
 | { | 
 |     Q_D(const CertificateInfo); | 
 |     return d->nick_name; | 
 | } | 
 |  | 
 | QDateTime CertificateInfo::validityStart() const | 
 | { | 
 |     Q_D(const CertificateInfo); | 
 |     return d->validity_start; | 
 | } | 
 |  | 
 | QDateTime CertificateInfo::validityEnd() const | 
 | { | 
 |     Q_D(const CertificateInfo); | 
 |     return d->validity_end; | 
 | } | 
 |  | 
 | CertificateInfo::KeyUsageExtensions CertificateInfo::keyUsageExtensions() const | 
 | { | 
 |     Q_D(const CertificateInfo); | 
 |  | 
 |     KeyUsageExtensions kuExtensions = KuNone; | 
 |     if (d->ku_extensions & KU_DIGITAL_SIGNATURE) { | 
 |         kuExtensions |= KuDigitalSignature; | 
 |     } | 
 |     if (d->ku_extensions & KU_NON_REPUDIATION) { | 
 |         kuExtensions |= KuNonRepudiation; | 
 |     } | 
 |     if (d->ku_extensions & KU_KEY_ENCIPHERMENT) { | 
 |         kuExtensions |= KuKeyEncipherment; | 
 |     } | 
 |     if (d->ku_extensions & KU_DATA_ENCIPHERMENT) { | 
 |         kuExtensions |= KuDataEncipherment; | 
 |     } | 
 |     if (d->ku_extensions & KU_KEY_AGREEMENT) { | 
 |         kuExtensions |= KuKeyAgreement; | 
 |     } | 
 |     if (d->ku_extensions & KU_KEY_CERT_SIGN) { | 
 |         kuExtensions |= KuKeyCertSign; | 
 |     } | 
 |     if (d->ku_extensions & KU_CRL_SIGN) { | 
 |         kuExtensions |= KuClrSign; | 
 |     } | 
 |     if (d->ku_extensions & KU_ENCIPHER_ONLY) { | 
 |         kuExtensions |= KuEncipherOnly; | 
 |     } | 
 |  | 
 |     return kuExtensions; | 
 | } | 
 |  | 
 | CertificateInfo::KeyLocation CertificateInfo::keyLocation() const | 
 | { | 
 |     Q_D(const CertificateInfo); | 
 |     return d->keyLocation; | 
 | } | 
 |  | 
 | QByteArray CertificateInfo::publicKey() const | 
 | { | 
 |     Q_D(const CertificateInfo); | 
 |     return d->public_key; | 
 | } | 
 |  | 
 | CertificateInfo::PublicKeyType CertificateInfo::publicKeyType() const | 
 | { | 
 |     Q_D(const CertificateInfo); | 
 |     switch (d->public_key_type) { | 
 |     case RSAKEY: | 
 |         return RsaKey; | 
 |     case DSAKEY: | 
 |         return DsaKey; | 
 |     case ECKEY: | 
 |         return EcKey; | 
 |     default: | 
 |         return OtherKey; | 
 |     } | 
 | } | 
 |  | 
 | int CertificateInfo::publicKeyStrength() const | 
 | { | 
 |     Q_D(const CertificateInfo); | 
 |     return d->public_key_strength; | 
 | } | 
 |  | 
 | bool CertificateInfo::isSelfSigned() const | 
 | { | 
 |     Q_D(const CertificateInfo); | 
 |     return d->is_self_signed; | 
 | } | 
 |  | 
 | QByteArray CertificateInfo::certificateData() const | 
 | { | 
 |     Q_D(const CertificateInfo); | 
 |     return d->certificate_der; | 
 | } | 
 |  | 
 | bool CertificateInfo::checkPassword(const QString &password) const | 
 | { | 
 | #ifdef ENABLE_SIGNATURES | 
 |     auto backend = CryptoSign::Factory::createActive(); | 
 |     if (!backend) { | 
 |         return false; | 
 |     } | 
 |     Q_D(const CertificateInfo); | 
 |     auto sigHandler = backend->createSigningHandler(d->nick_name.toStdString(), HashAlgorithm::Sha256); | 
 |     unsigned char buffer[5]; | 
 |     memcpy(buffer, "test", 5); | 
 |     sigHandler->addData(buffer, 5); | 
 |     std::optional<GooString> tmpSignature = sigHandler->signDetached(password.toStdString()); | 
 |     return tmpSignature.has_value(); | 
 | #else | 
 |     return false; | 
 | #endif | 
 | } | 
 |  | 
 | class SignatureValidationInfoPrivate | 
 | { | 
 | public: | 
 |     explicit SignatureValidationInfoPrivate(CertificateInfo &&ci) : cert_info(ci) { } | 
 |  | 
 |     SignatureValidationInfo::SignatureStatus signature_status; | 
 |     SignatureValidationInfo::CertificateStatus certificate_status; | 
 |     CertificateInfo cert_info; | 
 |  | 
 |     QByteArray signature; | 
 |     QString signer_name; | 
 |     QString signer_subject_dn; | 
 |     QString location; | 
 |     QString reason; | 
 |     HashAlgorithm hash_algorithm; | 
 |     time_t signing_time; | 
 |     QList<qint64> range_bounds; | 
 |     qint64 docLength; | 
 | }; | 
 |  | 
 | SignatureValidationInfo::SignatureValidationInfo(SignatureValidationInfoPrivate *priv) : d_ptr(priv) { } | 
 |  | 
 | SignatureValidationInfo::SignatureValidationInfo(const SignatureValidationInfo &other) : d_ptr(other.d_ptr) { } | 
 |  | 
 | SignatureValidationInfo::~SignatureValidationInfo() { } | 
 |  | 
 | SignatureValidationInfo::SignatureStatus SignatureValidationInfo::signatureStatus() const | 
 | { | 
 |     Q_D(const SignatureValidationInfo); | 
 |     return d->signature_status; | 
 | } | 
 |  | 
 | SignatureValidationInfo::CertificateStatus SignatureValidationInfo::certificateStatus() const | 
 | { | 
 |     Q_D(const SignatureValidationInfo); | 
 |     return d->certificate_status; | 
 | } | 
 |  | 
 | QString SignatureValidationInfo::signerName() const | 
 | { | 
 |     Q_D(const SignatureValidationInfo); | 
 |     return d->signer_name; | 
 | } | 
 |  | 
 | QString SignatureValidationInfo::signerSubjectDN() const | 
 | { | 
 |     Q_D(const SignatureValidationInfo); | 
 |     return d->signer_subject_dn; | 
 | } | 
 |  | 
 | QString SignatureValidationInfo::location() const | 
 | { | 
 |     Q_D(const SignatureValidationInfo); | 
 |     return d->location; | 
 | } | 
 |  | 
 | QString SignatureValidationInfo::reason() const | 
 | { | 
 |     Q_D(const SignatureValidationInfo); | 
 |     return d->reason; | 
 | } | 
 |  | 
 | SignatureValidationInfo::HashAlgorithm SignatureValidationInfo::hashAlgorithm() const | 
 | { | 
 | #ifdef ENABLE_SIGNATURES | 
 |     Q_D(const SignatureValidationInfo); | 
 |  | 
 |     switch (d->hash_algorithm) { | 
 |     case ::HashAlgorithm::Md2: | 
 |         return HashAlgorithmMd2; | 
 |     case ::HashAlgorithm::Md5: | 
 |         return HashAlgorithmMd5; | 
 |     case ::HashAlgorithm::Sha1: | 
 |         return HashAlgorithmSha1; | 
 |     case ::HashAlgorithm::Sha256: | 
 |         return HashAlgorithmSha256; | 
 |     case ::HashAlgorithm::Sha384: | 
 |         return HashAlgorithmSha384; | 
 |     case ::HashAlgorithm::Sha512: | 
 |         return HashAlgorithmSha512; | 
 |     case ::HashAlgorithm::Sha224: | 
 |         return HashAlgorithmSha224; | 
 |     case ::HashAlgorithm::Unknown: | 
 |         return HashAlgorithmUnknown; | 
 |     } | 
 | #endif | 
 |     return HashAlgorithmUnknown; | 
 | } | 
 |  | 
 | time_t SignatureValidationInfo::signingTime() const | 
 | { | 
 |     Q_D(const SignatureValidationInfo); | 
 |     return d->signing_time; | 
 | } | 
 |  | 
 | QByteArray SignatureValidationInfo::signature() const | 
 | { | 
 |     Q_D(const SignatureValidationInfo); | 
 |     return d->signature; | 
 | } | 
 |  | 
 | QList<qint64> SignatureValidationInfo::signedRangeBounds() const | 
 | { | 
 |     Q_D(const SignatureValidationInfo); | 
 |     return d->range_bounds; | 
 | } | 
 |  | 
 | bool SignatureValidationInfo::signsTotalDocument() const | 
 | { | 
 |     Q_D(const SignatureValidationInfo); | 
 |     if (d->range_bounds.size() == 4 && d->range_bounds.value(0) == 0 && d->range_bounds.value(1) >= 0 && d->range_bounds.value(2) > d->range_bounds.value(1) && d->range_bounds.value(3) >= d->range_bounds.value(2)) { | 
 |         // The range from d->range_bounds.value(1) to d->range_bounds.value(2) is | 
 |         // not authenticated by the signature and should only contain the signature | 
 |         // itself padded with 0 bytes. This has been checked in readSignature(). | 
 |         // If it failed, d->signature is empty. | 
 |         // A potential range after d->range_bounds.value(3) would be also not | 
 |         // authenticated. Therefore d->range_bounds.value(3) should coincide with | 
 |         // the end of the document. | 
 |         if (d->docLength == d->range_bounds.value(3) && !d->signature.isEmpty()) { | 
 |             return true; | 
 |         } | 
 |     } | 
 |     return false; | 
 | } | 
 |  | 
 | CertificateInfo SignatureValidationInfo::certificateInfo() const | 
 | { | 
 |     Q_D(const SignatureValidationInfo); | 
 |     return d->cert_info; | 
 | } | 
 |  | 
 | SignatureValidationInfo &SignatureValidationInfo::operator=(const SignatureValidationInfo &other) | 
 | { | 
 |     if (this != &other) { | 
 |         d_ptr = other.d_ptr; | 
 |     } | 
 |  | 
 |     return *this; | 
 | } | 
 |  | 
 | FormFieldSignature::FormFieldSignature(DocumentData *doc, ::Page *p, ::FormWidgetSignature *w) : FormField(std::make_unique<FormFieldData>(doc, p, w)) { } | 
 |  | 
 | FormFieldSignature::~FormFieldSignature() { } | 
 |  | 
 | FormField::FormType FormFieldSignature::type() const | 
 | { | 
 |     return FormField::FormSignature; | 
 | } | 
 |  | 
 | FormFieldSignature::SignatureType FormFieldSignature::signatureType() const | 
 | { | 
 |     SignatureType sigType = AdbePkcs7detached; | 
 |     FormWidgetSignature *fws = static_cast<FormWidgetSignature *>(m_formData->fm); | 
 |     switch (fws->signatureType()) { | 
 |     case adbe_pkcs7_sha1: | 
 |         sigType = AdbePkcs7sha1; | 
 |         break; | 
 |     case adbe_pkcs7_detached: | 
 |         sigType = AdbePkcs7detached; | 
 |         break; | 
 |     case ETSI_CAdES_detached: | 
 |         sigType = EtsiCAdESdetached; | 
 |         break; | 
 |     case unknown_signature_type: | 
 |         sigType = UnknownSignatureType; | 
 |         break; | 
 |     case unsigned_signature_field: | 
 |         sigType = UnsignedSignature; | 
 |         break; | 
 |     } | 
 |     return sigType; | 
 | } | 
 |  | 
 | SignatureValidationInfo FormFieldSignature::validate(ValidateOptions opt) const | 
 | { | 
 |     return validate(opt, QDateTime()); | 
 | } | 
 |  | 
 | static CertificateInfo::KeyLocation fromPopplerCore(KeyLocation location) | 
 | { | 
 |     switch (location) { | 
 |     case KeyLocation::Computer: | 
 |         return CertificateInfo::KeyLocation::Computer; | 
 |     case KeyLocation::Other: | 
 |         return CertificateInfo::KeyLocation::Other; | 
 |     case KeyLocation::Unknown: | 
 |         return CertificateInfo::KeyLocation::Unknown; | 
 |     case KeyLocation::HardwareToken: | 
 |         return CertificateInfo::KeyLocation::HardwareToken; | 
 |     } | 
 |     return CertificateInfo::KeyLocation::Unknown; | 
 | } | 
 |  | 
 | static CertificateInfoPrivate *createCertificateInfoPrivate(const X509CertificateInfo *ci) | 
 | { | 
 |     CertificateInfoPrivate *certPriv = new CertificateInfoPrivate; | 
 |     certPriv->is_null = true; | 
 |     if (ci) { | 
 |         certPriv->version = ci->getVersion(); | 
 |         certPriv->ku_extensions = ci->getKeyUsageExtensions(); | 
 |         certPriv->keyLocation = fromPopplerCore(ci->getKeyLocation()); | 
 |  | 
 |         const GooString &certSerial = ci->getSerialNumber(); | 
 |         certPriv->serial_number = QByteArray(certSerial.c_str(), certSerial.getLength()); | 
 |  | 
 |         const X509CertificateInfo::EntityInfo &issuerInfo = ci->getIssuerInfo(); | 
 |         certPriv->issuer_info.common_name = issuerInfo.commonName.c_str(); | 
 |         certPriv->issuer_info.distinguished_name = issuerInfo.distinguishedName.c_str(); | 
 |         certPriv->issuer_info.email_address = issuerInfo.email.c_str(); | 
 |         certPriv->issuer_info.org_name = issuerInfo.organization.c_str(); | 
 |  | 
 |         const X509CertificateInfo::EntityInfo &subjectInfo = ci->getSubjectInfo(); | 
 |         certPriv->subject_info.common_name = subjectInfo.commonName.c_str(); | 
 |         certPriv->subject_info.distinguished_name = subjectInfo.distinguishedName.c_str(); | 
 |         certPriv->subject_info.email_address = subjectInfo.email.c_str(); | 
 |         certPriv->subject_info.org_name = subjectInfo.organization.c_str(); | 
 |  | 
 |         certPriv->nick_name = ci->getNickName().c_str(); | 
 |  | 
 |         X509CertificateInfo::Validity certValidity = ci->getValidity(); | 
 |         certPriv->validity_start = QDateTime::fromSecsSinceEpoch(certValidity.notBefore, Qt::UTC); | 
 |         certPriv->validity_end = QDateTime::fromSecsSinceEpoch(certValidity.notAfter, Qt::UTC); | 
 |  | 
 |         const X509CertificateInfo::PublicKeyInfo &pkInfo = ci->getPublicKeyInfo(); | 
 |         certPriv->public_key = QByteArray(pkInfo.publicKey.c_str(), pkInfo.publicKey.getLength()); | 
 |         certPriv->public_key_type = static_cast<int>(pkInfo.publicKeyType); | 
 |         certPriv->public_key_strength = pkInfo.publicKeyStrength; | 
 |  | 
 |         const GooString &certDer = ci->getCertificateDER(); | 
 |         certPriv->certificate_der = QByteArray(certDer.c_str(), certDer.getLength()); | 
 |  | 
 |         certPriv->is_null = false; | 
 |     } | 
 |  | 
 |     return certPriv; | 
 | } | 
 |  | 
 | SignatureValidationInfo FormFieldSignature::validate(int opt, const QDateTime &validationTime) const | 
 | { | 
 |     FormWidgetSignature *fws = static_cast<FormWidgetSignature *>(m_formData->fm); | 
 |     const time_t validationTimeT = validationTime.isValid() ? validationTime.toSecsSinceEpoch() : -1; | 
 |     SignatureInfo *si = fws->validateSignature(opt & ValidateVerifyCertificate, opt & ValidateForceRevalidation, validationTimeT, !(opt & ValidateWithoutOCSPRevocationCheck), opt & ValidateUseAIACertFetch); | 
 |  | 
 |     // get certificate info | 
 |     const X509CertificateInfo *ci = si->getCertificateInfo(); | 
 |     CertificateInfoPrivate *certPriv = createCertificateInfoPrivate(ci); | 
 |  | 
 |     SignatureValidationInfoPrivate *priv = new SignatureValidationInfoPrivate(CertificateInfo(certPriv)); | 
 |     switch (si->getSignatureValStatus()) { | 
 |     case SIGNATURE_VALID: | 
 |         priv->signature_status = SignatureValidationInfo::SignatureValid; | 
 |         break; | 
 |     case SIGNATURE_INVALID: | 
 |         priv->signature_status = SignatureValidationInfo::SignatureInvalid; | 
 |         break; | 
 |     case SIGNATURE_DIGEST_MISMATCH: | 
 |         priv->signature_status = SignatureValidationInfo::SignatureDigestMismatch; | 
 |         break; | 
 |     case SIGNATURE_DECODING_ERROR: | 
 |         priv->signature_status = SignatureValidationInfo::SignatureDecodingError; | 
 |         break; | 
 |     default: | 
 |     case SIGNATURE_GENERIC_ERROR: | 
 |         priv->signature_status = SignatureValidationInfo::SignatureGenericError; | 
 |         break; | 
 |     case SIGNATURE_NOT_FOUND: | 
 |         priv->signature_status = SignatureValidationInfo::SignatureNotFound; | 
 |         break; | 
 |     case SIGNATURE_NOT_VERIFIED: | 
 |         priv->signature_status = SignatureValidationInfo::SignatureNotVerified; | 
 |         break; | 
 |     } | 
 |     switch (si->getCertificateValStatus()) { | 
 |     case CERTIFICATE_TRUSTED: | 
 |         priv->certificate_status = SignatureValidationInfo::CertificateTrusted; | 
 |         break; | 
 |     case CERTIFICATE_UNTRUSTED_ISSUER: | 
 |         priv->certificate_status = SignatureValidationInfo::CertificateUntrustedIssuer; | 
 |         break; | 
 |     case CERTIFICATE_UNKNOWN_ISSUER: | 
 |         priv->certificate_status = SignatureValidationInfo::CertificateUnknownIssuer; | 
 |         break; | 
 |     case CERTIFICATE_REVOKED: | 
 |         priv->certificate_status = SignatureValidationInfo::CertificateRevoked; | 
 |         break; | 
 |     case CERTIFICATE_EXPIRED: | 
 |         priv->certificate_status = SignatureValidationInfo::CertificateExpired; | 
 |         break; | 
 |     default: | 
 |     case CERTIFICATE_GENERIC_ERROR: | 
 |         priv->certificate_status = SignatureValidationInfo::CertificateGenericError; | 
 |         break; | 
 |     case CERTIFICATE_NOT_VERIFIED: | 
 |         priv->certificate_status = SignatureValidationInfo::CertificateNotVerified; | 
 |         break; | 
 |     } | 
 |     priv->signer_name = QString::fromStdString(si->getSignerName()); | 
 |     priv->signer_subject_dn = QString::fromStdString(si->getSubjectDN()); | 
 |     priv->hash_algorithm = si->getHashAlgorithm(); | 
 |     priv->location = UnicodeParsedString(si->getLocation().toStr()); | 
 |     priv->reason = UnicodeParsedString(si->getReason().toStr()); | 
 |  | 
 |     priv->signing_time = si->getSigningTime(); | 
 |     const std::vector<Goffset> ranges = fws->getSignedRangeBounds(); | 
 |     if (!ranges.empty()) { | 
 |         for (Goffset bound : ranges) { | 
 |             priv->range_bounds.append(bound); | 
 |         } | 
 |     } | 
 |     const std::optional<GooString> checkedSignature = fws->getCheckedSignature(&priv->docLength); | 
 |     if (priv->range_bounds.size() == 4 && checkedSignature) { | 
 |         priv->signature = QByteArray::fromHex(checkedSignature->c_str()); | 
 |     } | 
 |  | 
 |     return SignatureValidationInfo(priv); | 
 | } | 
 |  | 
 | FormFieldSignature::SigningResult FormFieldSignature::sign(const QString &outputFileName, const PDFConverter::NewSignatureData &data) const | 
 | { | 
 |     FormWidgetSignature *fws = static_cast<FormWidgetSignature *>(m_formData->fm); | 
 |     if (fws->signatureType() != unsigned_signature_field) { | 
 |         return FieldAlreadySigned; | 
 |     } | 
 |  | 
 |     Goffset file_size = 0; | 
 |     const std::optional<GooString> sig = fws->getCheckedSignature(&file_size); | 
 |     if (sig) { | 
 |         // the above unsigned_signature_field check | 
 |         // should already catch this, but double check | 
 |         return FieldAlreadySigned; | 
 |     } | 
 |     const auto reason = std::unique_ptr<GooString>(data.reason().isEmpty() ? nullptr : QStringToUnicodeGooString(data.reason())); | 
 |     const auto location = std::unique_ptr<GooString>(data.location().isEmpty() ? nullptr : QStringToUnicodeGooString(data.location())); | 
 |     const auto ownerPwd = std::optional<GooString>(data.documentOwnerPassword().constData()); | 
 |     const auto userPwd = std::optional<GooString>(data.documentUserPassword().constData()); | 
 |     const auto gSignatureText = std::unique_ptr<GooString>(QStringToUnicodeGooString(data.signatureText())); | 
 |     const auto gSignatureLeftText = std::unique_ptr<GooString>(QStringToUnicodeGooString(data.signatureLeftText())); | 
 |  | 
 |     const bool success = fws->signDocumentWithAppearance(outputFileName.toStdString(), data.certNickname().toStdString(), data.password().toStdString(), reason.get(), location.get(), ownerPwd, userPwd, *gSignatureText, *gSignatureLeftText, | 
 |                                                          data.fontSize(), data.leftFontSize(), convertQColor(data.fontColor()), data.borderWidth(), convertQColor(data.borderColor()), convertQColor(data.backgroundColor())); | 
 |  | 
 |     return success ? SigningSuccess : GenericSigningError; | 
 | } | 
 |  | 
 | bool hasNSSSupport() | 
 | { | 
 | #ifdef ENABLE_NSS3 | 
 |     return true; | 
 | #else | 
 |     return false; | 
 | #endif | 
 | } | 
 |  | 
 | QVector<CertificateInfo> getAvailableSigningCertificates() | 
 | { | 
 |     auto backend = CryptoSign::Factory::createActive(); | 
 |     if (!backend) { | 
 |         return {}; | 
 |     } | 
 |     QVector<CertificateInfo> vReturnCerts; | 
 |     std::vector<std::unique_ptr<X509CertificateInfo>> vCerts = backend->getAvailableSigningCertificates(); | 
 |  | 
 |     for (auto &cert : vCerts) { | 
 |         CertificateInfoPrivate *certPriv = createCertificateInfoPrivate(cert.get()); | 
 |         vReturnCerts.append(CertificateInfo(certPriv)); | 
 |     } | 
 |  | 
 |     return vReturnCerts; | 
 | } | 
 |  | 
 | static std::optional<CryptoSignBackend> convertToFrontend(std::optional<CryptoSign::Backend::Type> type) | 
 | { | 
 |     if (!type) { | 
 |         return std::nullopt; | 
 |     } | 
 |     switch (type.value()) { | 
 |     case CryptoSign::Backend::Type::NSS3: | 
 |         return CryptoSignBackend::NSS; | 
 |     case CryptoSign::Backend::Type::GPGME: | 
 |         return CryptoSignBackend::GPG; | 
 |     } | 
 |     return std::nullopt; | 
 | } | 
 |  | 
 | static std::optional<CryptoSign::Backend::Type> convertToBackend(std::optional<CryptoSignBackend> backend) | 
 | { | 
 |     if (!backend) { | 
 |         return std::nullopt; | 
 |     } | 
 |  | 
 |     switch (backend.value()) { | 
 |     case CryptoSignBackend::NSS: | 
 |         return CryptoSign::Backend::Type::NSS3; | 
 |     case CryptoSignBackend::GPG: | 
 |         return CryptoSign::Backend::Type::GPGME; | 
 |     } | 
 |     return std::nullopt; | 
 | } | 
 |  | 
 | QVector<CryptoSignBackend> availableCryptoSignBackends() | 
 | { | 
 |     QVector<CryptoSignBackend> backends; | 
 |     for (auto &backend : CryptoSign::Factory::getAvailable()) { | 
 |         auto converted = convertToFrontend(backend); | 
 |         if (converted) { | 
 |             backends.push_back(converted.value()); | 
 |         } | 
 |     } | 
 |     return backends; | 
 | } | 
 |  | 
 | std::optional<CryptoSignBackend> activeCryptoSignBackend() | 
 | { | 
 |     return convertToFrontend(CryptoSign::Factory::getActive()); | 
 | } | 
 |  | 
 | bool setActiveCryptoSignBackend(CryptoSignBackend backend) | 
 | { | 
 |     auto available = availableCryptoSignBackends(); | 
 |     if (!available.contains(backend)) { | 
 |         return false; | 
 |     } | 
 |     auto converted = convertToBackend(backend); | 
 |     if (!converted) { | 
 |         return false; | 
 |     } | 
 |     CryptoSign::Factory::setPreferredBackend(converted.value()); | 
 |     return activeCryptoSignBackend() == backend; | 
 | } | 
 |  | 
 | static bool hasNSSBackendFeature(CryptoSignBackendFeature feature) | 
 | { | 
 |     switch (feature) { | 
 |     case CryptoSignBackendFeature::BackendAsksPassphrase: | 
 |         return false; | 
 |     } | 
 |     return false; | 
 | } | 
 |  | 
 | static bool hasGPGBackendFeature(CryptoSignBackendFeature feature) | 
 | { | 
 |     switch (feature) { | 
 |     case CryptoSignBackendFeature::BackendAsksPassphrase: | 
 |         return true; | 
 |     } | 
 |     return false; | 
 | } | 
 |  | 
 | bool hasCryptoSignBackendFeature(CryptoSignBackend backend, CryptoSignBackendFeature feature) | 
 | { | 
 |     switch (backend) { | 
 |     case CryptoSignBackend::NSS: | 
 |         return hasNSSBackendFeature(feature); | 
 |     case CryptoSignBackend::GPG: | 
 |         return hasGPGBackendFeature(feature); | 
 |     } | 
 |     return false; | 
 | } | 
 |  | 
 | QString POPPLER_QT6_EXPORT getNSSDir() | 
 | { | 
 | #ifdef ENABLE_NSS3 | 
 |     return QString::fromLocal8Bit(NSSSignatureConfiguration::getNSSDir().c_str()); | 
 | #else | 
 |     return QString(); | 
 | #endif | 
 | } | 
 |  | 
 | void setNSSDir(const QString &path) | 
 | { | 
 | #ifdef ENABLE_NSS3 | 
 |     if (path.isEmpty()) { | 
 |         return; | 
 |     } | 
 |  | 
 |     GooString *goo = QStringToGooString(path); | 
 |     NSSSignatureConfiguration::setNSSDir(*goo); | 
 |     delete goo; | 
 | #else | 
 |     (void)path; | 
 | #endif | 
 | } | 
 |  | 
 | namespace { | 
 | std::function<QString(const QString &)> nssPasswordCall; | 
 | } | 
 |  | 
 | void setNSSPasswordCallback(const std::function<char *(const char *)> &f) | 
 | { | 
 | #ifdef ENABLE_NSS3 | 
 |     NSSSignatureConfiguration::setNSSPasswordCallback(f); | 
 | #else | 
 |     qWarning() << "setNSSPasswordCallback called but this poppler is built without NSS support"; | 
 |     (void)f; | 
 | #endif | 
 | } | 
 | } |