Add API for creating signature field without signing immediately

Currently Poppler allows to create a signature and sign it in one step
as well as singing an unsinged field. However there is no way to
create an unsigned field and sign it later.

This adds a SignatureAnnotation class that can be added like any
annotation and signed later
diff --git a/poppler/PDFDoc.cc b/poppler/PDFDoc.cc
index 1cbcb7f..6970355 100644
--- a/poppler/PDFDoc.cc
+++ b/poppler/PDFDoc.cc
@@ -56,6 +56,7 @@
 // Copyright (C) 2022 Erich E. Hoover <erich.e.hoover@gmail.com>
 // Copyright (C) 2023, 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela <sune@vuorela.dk>
 // Copyright (C) 2024 Vincent Lefevre <vincent@vinc17.net>
+// Copyright (C) 2024 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>. Work sponsored by Technische Universität Dresden
 //
 // To see a description of the changes please see the Changelog file that
 // came with your tarball or type make ChangeLog if you are building from git
@@ -2185,26 +2186,26 @@
     return jsInfo.containsJS();
 }
 
-bool PDFDoc::sign(const std::string &saveFilename, const std::string &certNickname, const std::string &password, GooString *partialFieldName, int page, const PDFRectangle &rect, const GooString &signatureText,
-                  const GooString &signatureTextLeft, double fontSize, double leftFontSize, std::unique_ptr<AnnotColor> &&fontColor, double borderWidth, std::unique_ptr<AnnotColor> &&borderColor,
-                  std::unique_ptr<AnnotColor> &&backgroundColor, const GooString *reason, const GooString *location, const std::string &imagePath, const std::optional<GooString> &ownerPassword, const std::optional<GooString> &userPassword)
+std::optional<PDFDoc::SignatureData> PDFDoc::createSignature(::Page *destPage, GooString *partialFieldName, const PDFRectangle &rect, const GooString &signatureText, const GooString &signatureTextLeft, double fontSize, double leftFontSize,
+                                                             std::unique_ptr<AnnotColor> &&fontColor, double borderWidth, std::unique_ptr<AnnotColor> &&borderColor, std::unique_ptr<AnnotColor> &&backgroundColor,
+                                                             const std::string &imagePath)
 {
-    ::Page *destPage = getPage(page);
     if (destPage == nullptr) {
-        return false;
+        return std::nullopt;
     }
+
     Ref imageResourceRef = Ref::INVALID();
     if (!imagePath.empty()) {
         imageResourceRef = ImageEmbeddingUtils::embed(xref, imagePath);
         if (imageResourceRef == Ref::INVALID()) {
-            return false;
+            return std::nullopt;
         }
     }
 
     Form *form = catalog->getCreateForm();
     const std::string pdfFontName = form->findPdfFontNameToUseForSigning();
     if (pdfFontName.empty()) {
-        return false;
+        return std::nullopt;
     }
 
     const DefaultAppearance da { { objName, pdfFontName.c_str() }, fontSize, std::move(fontColor) };
@@ -2226,8 +2227,6 @@
 
     const Ref ref = getXRef()->addIndirectObject(annotObj);
     catalog->addFormToAcroForm(ref);
-    // say that there a now signatures and that we should append only
-    catalog->getAcroForm()->dictSet("SigFlags", Object(3));
     catalog->setAcroFormModified();
 
     form->ensureFontsForAllCharacters(&signatureText, pdfFontName);
@@ -2241,7 +2240,7 @@
 
     Object refObj(ref);
     AnnotWidget *signatureAnnot = new AnnotWidget(this, field->getObj(), &refObj, field.get());
-    signatureAnnot->setFlags(signatureAnnot->getFlags() | Annot::flagPrint | Annot::flagLocked | Annot::flagNoRotate);
+    signatureAnnot->setFlags(signatureAnnot->getFlags() | Annot::flagPrint | /*Annot::flagLocked | TODO */ Annot::flagNoRotate);
     Dict dummy(getXRef());
     auto appearCharacs = std::make_unique<AnnotAppearanceCharacs>(&dummy);
     appearCharacs->setBorderColor(std::move(borderColor));
@@ -2254,25 +2253,49 @@
     FormWidget *formWidget = field->getWidget(field->getNumWidgets() - 1);
     formWidget->setWidgetAnnotation(signatureAnnot);
 
-    destPage->addAnnot(signatureAnnot);
-
     std::unique_ptr<AnnotBorder> border(new AnnotBorderArray());
     border->setWidth(borderWidth);
     signatureAnnot->setBorder(std::move(border));
 
-    FormWidgetSignature *fws = dynamic_cast<FormWidgetSignature *>(formWidget);
+    return SignatureData { { ref.num, ref.gen }, signatureAnnot, formWidget, std::move(field) };
+}
+
+bool PDFDoc::sign(const std::string &saveFilename, const std::string &certNickname, const std::string &password, GooString *partialFieldName, int page, const PDFRectangle &rect, const GooString &signatureText,
+                  const GooString &signatureTextLeft, double fontSize, double leftFontSize, std::unique_ptr<AnnotColor> &&fontColor, double borderWidth, std::unique_ptr<AnnotColor> &&borderColor,
+                  std::unique_ptr<AnnotColor> &&backgroundColor, const GooString *reason, const GooString *location, const std::string &imagePath, const std::optional<GooString> &ownerPassword, const std::optional<GooString> &userPassword)
+{
+    ::Page *destPage = getPage(page);
+    if (destPage == nullptr) {
+        return false;
+    }
+
+    std::optional<SignatureData> sig =
+            createSignature(destPage, partialFieldName, rect, signatureText, signatureTextLeft, fontSize, leftFontSize, std::move(fontColor), borderWidth, std::move(borderColor), std::move(backgroundColor), imagePath);
+
+    if (!sig) {
+        return false;
+    }
+
+    sig->annotWidget->setFlags(sig->annotWidget->getFlags() | Annot::flagLocked);
+
+    // say that there a now signatures and that we should append only
+    catalog->getAcroForm()->dictSet("SigFlags", Object(3));
+
+    destPage->addAnnot(sig->annotWidget);
+
+    FormWidgetSignature *fws = dynamic_cast<FormWidgetSignature *>(sig->formWidget);
     if (fws) {
         const bool res = fws->signDocument(saveFilename, certNickname, password, reason, location, ownerPassword, userPassword);
 
         // Now remove the signature stuff in case the user wants to continue editing stuff
         // So the document object is clean
-        const Object &vRefObj = field->getObj()->dictLookupNF("V");
+        const Object &vRefObj = sig->field->getObj()->dictLookupNF("V");
         if (vRefObj.isRef()) {
             getXRef()->removeIndirectObject(vRefObj.getRef());
         }
-        destPage->removeAnnot(signatureAnnot);
-        catalog->removeFormFromAcroForm(ref);
-        getXRef()->removeIndirectObject(ref);
+        destPage->removeAnnot(sig->annotWidget);
+        catalog->removeFormFromAcroForm(sig->ref);
+        getXRef()->removeIndirectObject(sig->ref);
 
         return res;
     }
diff --git a/poppler/PDFDoc.h b/poppler/PDFDoc.h
index 61aae61..a27b25f 100644
--- a/poppler/PDFDoc.h
+++ b/poppler/PDFDoc.h
@@ -40,6 +40,7 @@
 // Copyright (C) 2022 Felix Jung <fxjung@posteo.de>
 // Copyright (C) 2022 crt <chluo@cse.cuhk.edu.hk>
 // Copyright 2023 g10 Code GmbH, Author: Sune Stolborg Vuorela <sune@vuorela.dk>
+// Copyright (C) 2024 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>. Work sponsored by Technische Universität Dresden
 //
 // To see a description of the changes please see the Changelog file that
 // came with your tarball or type make ChangeLog if you are building from git
@@ -343,6 +344,17 @@
               const GooString &signatureTextLeft, double fontSize, double leftFontSize, std::unique_ptr<AnnotColor> &&fontColor, double borderWidth, std::unique_ptr<AnnotColor> &&borderColor, std::unique_ptr<AnnotColor> &&backgroundColor,
               const GooString *reason = nullptr, const GooString *location = nullptr, const std::string &imagePath = "", const std::optional<GooString> &ownerPassword = {}, const std::optional<GooString> &userPassword = {});
 
+    struct SignatureData
+    {
+        Ref ref;
+        AnnotWidget *annotWidget = nullptr;
+        FormWidget *formWidget = nullptr;
+        std::unique_ptr<::FormFieldSignature> field = nullptr;
+    };
+
+    std::optional<SignatureData> createSignature(::Page *destPage, GooString *partialFieldName, const PDFRectangle &rect, const GooString &signatureText, const GooString &signatureTextLeft, double fontSize, double leftFontSize,
+                                                 std::unique_ptr<AnnotColor> &&fontColor, double borderWidth, std::unique_ptr<AnnotColor> &&borderColor, std::unique_ptr<AnnotColor> &&backgroundColor, const std::string &imagePath);
+
 private:
     // insert referenced objects in XRef
     bool markDictionary(Dict *dict, XRef *xRef, XRef *countRef, unsigned int numOffset, int oldRefNum, int newRefNum, std::set<Dict *> *alreadyMarkedDicts);
diff --git a/qt6/src/CMakeLists.txt b/qt6/src/CMakeLists.txt
index 40c43e5..8b3484b 100644
--- a/qt6/src/CMakeLists.txt
+++ b/qt6/src/CMakeLists.txt
@@ -61,6 +61,7 @@
   poppler-optcontent.h
   poppler-page-transition.h
   poppler-media.h
+  poppler-converter.h
   ${CMAKE_CURRENT_BINARY_DIR}/poppler-export.h
   ${CMAKE_CURRENT_BINARY_DIR}/poppler-version.h
   DESTINATION include/poppler/qt6)
diff --git a/qt6/src/poppler-annotation.cc b/qt6/src/poppler-annotation.cc
index 1a3c813..39a5a4f 100644
--- a/qt6/src/poppler-annotation.cc
+++ b/qt6/src/poppler-annotation.cc
@@ -13,7 +13,7 @@
  * Copyright (C) 2020-2022 Oliver Sander <oliver.sander@tu-dresden.de>
  * Copyright (C) 2020 Katarina Behrens <Katarina.Behrens@cib.de>
  * 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) 2020, 2024 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>. Work sponsored by Technische Universität Dresden
  * Copyright (C) 2021 Mahmoud Ahmed Khalil <mahmoudkhalil11@gmail.com>
  * Adapting code from
  *   Copyright (C) 2004 by Enrico Ros <eros.kde@email.it>
@@ -47,6 +47,7 @@
 #include "poppler-annotation-private.h"
 #include "poppler-page-private.h"
 #include "poppler-private.h"
+#include "poppler-form.h"
 
 // poppler includes
 #include <Page.h>
@@ -2906,6 +2907,206 @@
     stampann->setCustomImage(annotCustomImage);
 }
 
+/** SignatureAnnotation [Annotation] */
+class SignatureAnnotationPrivate : public AnnotationPrivate
+{
+public:
+    SignatureAnnotationPrivate();
+    Annotation *makeAlias() override;
+    Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override;
+
+    // data fields
+    QString text;
+    QString leftText;
+    double fontSize = 10.0;
+    double leftFontSize = 20.0;
+    QColor fontColor = Qt::red;
+    QColor borderColor = Qt::red;
+    double borderWidth = 1.5;
+    QColor backgroundColor = QColor(240, 240, 240);
+    QString imagePath;
+    QString fieldPartialName;
+    std::unique_ptr<::FormFieldSignature> field = nullptr;
+};
+
+SignatureAnnotationPrivate::SignatureAnnotationPrivate() : AnnotationPrivate() { }
+
+Annotation *SignatureAnnotationPrivate::makeAlias()
+{
+    return new SignatureAnnotation(*this);
+}
+
+Annot *SignatureAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *doc)
+{
+    SignatureAnnotation *q = static_cast<SignatureAnnotation *>(makeAlias());
+
+    // Set page and document
+    pdfPage = destPage;
+    parentDoc = doc;
+
+    // Set pdfAnnot
+    PDFRectangle rect = boundaryToPdfRectangle(boundary, flags);
+
+    GooString signatureText(text.toStdString());
+    GooString signatureTextLeft(leftText.toStdString());
+
+    std::optional<PDFDoc::SignatureData> sig = destPage->getDoc()->createSignature(destPage, QStringToGooString(fieldPartialName), rect, signatureText, signatureTextLeft, fontSize, leftFontSize, convertQColor(fontColor), borderWidth,
+                                                                                   convertQColor(borderColor), convertQColor(backgroundColor), imagePath.toStdString());
+
+    if (!sig) {
+        return nullptr;
+    }
+
+    sig->formWidget->updateWidgetAppearance();
+    field = std::move(sig->field);
+
+    // Set properties
+    flushBaseAnnotationProperties();
+
+    pdfAnnot = sig->annotWidget;
+
+    delete q;
+
+    return pdfAnnot;
+}
+
+SignatureAnnotation::SignatureAnnotation() : Annotation(*new SignatureAnnotationPrivate()) { }
+
+SignatureAnnotation::SignatureAnnotation(SignatureAnnotationPrivate &dd) : Annotation(dd) { }
+
+SignatureAnnotation::~SignatureAnnotation() { }
+
+Annotation::SubType SignatureAnnotation::subType() const
+{
+    return AWidget;
+}
+
+void SignatureAnnotation::setText(const QString &text)
+{
+    Q_D(SignatureAnnotation);
+    d->text = text;
+}
+
+void SignatureAnnotation::setLeftText(const QString &text)
+{
+    Q_D(SignatureAnnotation);
+    d->leftText = text;
+}
+
+double SignatureAnnotation::fontSize() const
+{
+    Q_D(const SignatureAnnotation);
+    return d->fontSize;
+}
+
+void SignatureAnnotation::setFontSize(double fontSize)
+{
+    Q_D(SignatureAnnotation);
+    d->fontSize = fontSize;
+}
+
+double SignatureAnnotation::leftFontSize() const
+{
+    Q_D(const SignatureAnnotation);
+    return d->leftFontSize;
+}
+
+void SignatureAnnotation::setLeftFontSize(double fontSize)
+{
+    Q_D(SignatureAnnotation);
+    d->leftFontSize = fontSize;
+}
+
+QColor SignatureAnnotation::fontColor() const
+{
+    Q_D(const SignatureAnnotation);
+    return d->fontColor;
+}
+
+void SignatureAnnotation::setFontColor(const QColor &color)
+{
+    Q_D(SignatureAnnotation);
+    d->fontColor = color;
+}
+
+QColor SignatureAnnotation::borderColor() const
+{
+    Q_D(const SignatureAnnotation);
+    return d->borderColor;
+}
+
+void SignatureAnnotation::setBorderColor(const QColor &color)
+{
+    Q_D(SignatureAnnotation);
+    d->borderColor = color;
+}
+
+QColor SignatureAnnotation::backgroundColor() const
+{
+    Q_D(const SignatureAnnotation);
+    return d->backgroundColor;
+}
+
+double SignatureAnnotation::borderWidth() const
+{
+    Q_D(const SignatureAnnotation);
+    return d->borderWidth;
+}
+
+void SignatureAnnotation::setBorderWidth(double width)
+{
+    Q_D(SignatureAnnotation);
+    d->borderWidth = width;
+}
+
+void SignatureAnnotation::setBackgroundColor(const QColor &color)
+{
+    Q_D(SignatureAnnotation);
+    d->backgroundColor = color;
+}
+
+QString SignatureAnnotation::imagePath() const
+{
+    Q_D(const SignatureAnnotation);
+    return d->imagePath;
+}
+
+void SignatureAnnotation::setImagePath(const QString &imagePath)
+{
+    Q_D(SignatureAnnotation);
+    d->imagePath = imagePath;
+}
+
+QString SignatureAnnotation::fieldPartialName() const
+{
+    Q_D(const SignatureAnnotation);
+    return d->fieldPartialName;
+}
+
+void SignatureAnnotation::setFieldPartialName(const QString &fieldPartialName)
+{
+    Q_D(SignatureAnnotation);
+    d->fieldPartialName = fieldPartialName;
+}
+
+SignatureAnnotation::SigningResult SignatureAnnotation::sign(const QString &outputFileName, const PDFConverter::NewSignatureData &data)
+{
+    Q_D(SignatureAnnotation);
+    auto formField = std::make_unique<FormFieldSignature>(d->parentDoc, d->pdfPage, static_cast<::FormWidgetSignature *>(d->field->getCreateWidget()));
+
+    const auto result = formField->sign(outputFileName, data);
+
+    switch (result) {
+    case FormFieldSignature::SigningSuccess:
+        return SigningSuccess;
+    case FormFieldSignature::FieldAlreadySigned:
+        return FieldAlreadySigned;
+    case FormFieldSignature::GenericSigningError:
+        return GenericSigningError;
+    }
+    return GenericSigningError;
+}
+
 /** InkAnnotation [Annotation] */
 class InkAnnotationPrivate : public AnnotationPrivate
 {
diff --git a/qt6/src/poppler-annotation.h b/qt6/src/poppler-annotation.h
index 16f2eda..6f93f44 100644
--- a/qt6/src/poppler-annotation.h
+++ b/qt6/src/poppler-annotation.h
@@ -9,7 +9,7 @@
  * Copyright (C) 2013, Anthony Granger <grangeranthony@gmail.com>
  * Copyright (C) 2018, Dileep Sankhla <sankhla.dileep96@gmail.com>
  * Copyright (C) 2020, Katarina Behrens <Katarina.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) 2020, 2024, Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>. Work sponsored by Technische Universität Dresden
  * Copyright (C) 2021, Oliver Sander <oliver.sander@tu-dresden.de>
  * Copyright (C) 2021, Mahmoud Ahmed Khalil <mahmoudkhalil11@gmail.com>
  * Adapting code from
@@ -43,6 +43,7 @@
 #include <QtGui/QColor>
 #include <QtGui/QFont>
 #include "poppler-export.h"
+#include "poppler-converter.h"
 
 #include <memory>
 
@@ -56,6 +57,7 @@
 class GeomAnnotationPrivate;
 class HighlightAnnotationPrivate;
 class StampAnnotationPrivate;
+class SignatureAnnotationPrivate;
 class InkAnnotationPrivate;
 class LinkAnnotationPrivate;
 class CaretAnnotationPrivate;
@@ -772,6 +774,90 @@
 };
 
 /**
+ * \short Signature annotation.
+ *
+ * A signature annotation. By creating one and adding it to the page you can create
+ * an unsigned signature field.
+ *
+ * To read existing signature fields use FormFieldSignature.
+ *
+ * \since 24.10
+ */
+class POPPLER_QT6_EXPORT SignatureAnnotation : public Annotation
+{
+    friend class AnnotationPrivate;
+
+public:
+    /**
+     * \since 24.10
+     */
+    enum SigningResult
+    {
+        SigningSuccess,
+        FieldAlreadySigned, ///< Trying to sign a field that is already signed
+        GenericSigningError,
+    };
+
+    SignatureAnnotation();
+    ~SignatureAnnotation() override;
+    SubType subType() const override;
+
+    void setText(const QString &text);
+    void setLeftText(const QString &text);
+
+    /**
+     * Default: 10
+     */
+    double fontSize() const;
+    void setFontSize(double fontSize);
+
+    /**
+     * Default: 20
+     */
+    double leftFontSize() const;
+    void setLeftFontSize(double fontSize);
+
+    /**
+     * Default: red
+     */
+    QColor fontColor() const;
+    void setFontColor(const QColor &color);
+
+    /**
+     * Default: red
+     */
+    QColor borderColor() const;
+    void setBorderColor(const QColor &color);
+
+    /**
+     * border width in points
+     *
+     * Default: 1.5
+     */
+    double borderWidth() const;
+    void setBorderWidth(double width);
+
+    /**
+     * Default: QColor(240, 240, 240)
+     */
+    QColor backgroundColor() const;
+    void setBackgroundColor(const QColor &color);
+
+    QString imagePath() const;
+    void setImagePath(const QString &imagePath);
+
+    QString fieldPartialName() const;
+    void setFieldPartialName(const QString &fieldPartialName);
+
+    [[nodiscard]] SigningResult sign(const QString &outputFileName, const PDFConverter::NewSignatureData &data);
+
+private:
+    explicit SignatureAnnotation(SignatureAnnotationPrivate &dd);
+    Q_DECLARE_PRIVATE(SignatureAnnotation)
+    Q_DISABLE_COPY(SignatureAnnotation)
+};
+
+/**
  * \short Ink Annotation.
  *
  * Annotation representing an ink path on a page.
diff --git a/qt6/src/poppler-converter.h b/qt6/src/poppler-converter.h
new file mode 100644
index 0000000..e7394cd
--- /dev/null
+++ b/qt6/src/poppler-converter.h
@@ -0,0 +1,465 @@
+/* poppler-converter.h: qt interface to poppler
+ * Copyright (C) 2005, Net Integration Technologies, Inc.
+ * Copyright (C) 2005, 2007, Brad Hards <bradh@frogmouth.net>
+ * Copyright (C) 2005-2015, 2017-2022, Albert Astals Cid <aacid@kde.org>
+ * Copyright (C) 2005, Stefan Kebekus <stefan.kebekus@math.uni-koeln.de>
+ * Copyright (C) 2006-2011, Pino Toscano <pino@kde.org>
+ * Copyright (C) 2009 Shawn Rutledge <shawn.t.rutledge@gmail.com>
+ * Copyright (C) 2010 Suzuki Toshiya <mpsuzuki@hiroshima-u.ac.jp>
+ * Copyright (C) 2010 Matthias Fauconneau <matthias.fauconneau@gmail.com>
+ * Copyright (C) 2011 Andreas Hartmetz <ahartmetz@gmail.com>
+ * Copyright (C) 2011 Glad Deschrijver <glad.deschrijver@gmail.com>
+ * Copyright (C) 2012, Guillermo A. Amaral B. <gamaral@kde.org>
+ * Copyright (C) 2012, Fabio D'Urso <fabiodurso@hotmail.it>
+ * Copyright (C) 2012, Tobias Koenig <tobias.koenig@kdab.com>
+ * Copyright (C) 2012, 2014, 2015, 2018, 2019 Adam Reichold <adamreichold@myopera.com>
+ * Copyright (C) 2012, 2013 Thomas Freitag <Thomas.Freitag@alfa.de>
+ * Copyright (C) 2013 Anthony Granger <grangeranthony@gmail.com>
+ * Copyright (C) 2016 Jakub Alba <jakubalba@gmail.com>
+ * Copyright (C) 2017, 2020, 2021 Oliver Sander <oliver.sander@tu-dresden.de>
+ * Copyright (C) 2017, 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, 2021 Nelson Benítez León <nbenitezl@gmail.com>
+ * Copyright (C) 2019 Jan Grulich <jgrulich@redhat.com>
+ * Copyright (C) 2019 Alexander Volkov <a.volkov@rusbitech.ru>
+ * Copyright (C) 2020 Philipp Knechtges <philipp-dev@knechtges.com>
+ * Copyright (C) 2020 Katarina Behrens <Katarina.Behrens@cib.de>
+ * Copyright (C) 2020 Thorsten Behrens <Thorsten.Behrens@CIB.de>
+ * Copyright (C) 2020, 2024 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>. Work sponsored by Technische Universität Dresden
+ * Copyright (C) 2021 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>.
+ * Copyright (C) 2021 Mahmoud Khalil <mahmoudkhalil11@gmail.com>
+ * Copyright (C) 2021 Georgiy Sgibnev <georgiy@sgibnev.com>. Work sponsored by lab50.net.
+ * Copyright (C) 2022 Martin <martinbts@gmx.net>
+ * Copyright (C) 2023 Kevin Ottens <kevin.ottens@enioka.com>. Work sponsored by De Bortoli Wines
+ *
+ * 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.
+ */
+
+#ifndef __POPPLER_CONVERTER_H__
+#define __POPPLER_CONVERTER_H__
+
+#include <QtCore/QByteArray>
+#include <QtCore/QIODevice>
+
+#include "poppler-export.h"
+
+namespace Poppler {
+
+class DocumentData;
+
+class BaseConverterPrivate;
+class PSConverterPrivate;
+class PDFConverterPrivate;
+
+/**
+   \brief Base converter.
+
+   This is the base class for the converters.
+*/
+class POPPLER_QT6_EXPORT BaseConverter
+{
+    friend class Document;
+
+public:
+    /**
+      Destructor.
+    */
+    virtual ~BaseConverter();
+
+    /** Sets the output file name. You must set this or the output device. */
+    void setOutputFileName(const QString &outputFileName);
+
+    /**
+     * Sets the output device. You must set this or the output file name.
+     */
+    void setOutputDevice(QIODevice *device);
+
+    /**
+      Does the conversion.
+
+      \return whether the conversion succeeded
+    */
+    virtual bool convert() = 0;
+
+    enum Error
+    {
+        NoError,
+        FileLockedError,
+        OpenOutputError,
+        NotSupportedInputFileError
+    };
+
+    /**
+      Returns the last error
+    */
+    Error lastError() const;
+
+protected:
+    /// \cond PRIVATE
+    explicit BaseConverter(BaseConverterPrivate &dd);
+    Q_DECLARE_PRIVATE(BaseConverter)
+    BaseConverterPrivate *d_ptr;
+    /// \endcond
+
+private:
+    Q_DISABLE_COPY(BaseConverter)
+};
+
+/**
+   Converts a PDF to PS
+
+   Sizes have to be in Points (1/72 inch)
+
+   If you are using QPrinter you can get paper size by doing:
+   \code
+QPrinter dummy(QPrinter::PrinterResolution);
+dummy.setFullPage(true);
+dummy.setPageSize(myPageSize);
+width = dummy.width();
+height = dummy.height();
+   \endcode
+*/
+class POPPLER_QT6_EXPORT PSConverter : public BaseConverter
+{
+    friend class Document;
+
+public:
+    /**
+      Options for the PS export.
+     */
+    enum PSOption
+    {
+        Printing = 0x00000001, ///< The PS is generated for printing purposes
+        StrictMargins = 0x00000002,
+        ForceRasterization = 0x00000004,
+        PrintToEPS = 0x00000008, ///< Output EPS instead of PS
+        HideAnnotations = 0x00000010, ///< Don't print annotations
+        ForceOverprintPreview = 0x00000020 ///< Force rasterized overprint preview during conversion \since 23.09
+    };
+    Q_DECLARE_FLAGS(PSOptions, PSOption)
+
+    /**
+      Destructor.
+    */
+    ~PSConverter() override;
+
+    /** Sets the list of pages to print. Mandatory. */
+    void setPageList(const QList<int> &pageList);
+
+    /**
+      Sets the title of the PS Document. Optional
+    */
+    void setTitle(const QString &title);
+
+    /**
+      Sets the horizontal DPI. Defaults to 72.0
+    */
+    void setHDPI(double hDPI);
+
+    /**
+      Sets the vertical DPI. Defaults to 72.0
+    */
+    void setVDPI(double vDPI);
+
+    /**
+      Sets the rotate. Defaults to not rotated
+    */
+    void setRotate(int rotate);
+
+    /**
+      Sets the output paper width. Has to be set.
+    */
+    void setPaperWidth(int paperWidth);
+
+    /**
+      Sets the output paper height. Has to be set.
+    */
+    void setPaperHeight(int paperHeight);
+
+    /**
+      Sets the output right margin. Defaults to 0
+    */
+    void setRightMargin(int marginRight);
+
+    /**
+      Sets the output bottom margin. Defaults to 0
+    */
+    void setBottomMargin(int marginBottom);
+
+    /**
+      Sets the output left margin. Defaults to 0
+    */
+    void setLeftMargin(int marginLeft);
+
+    /**
+      Sets the output top margin. Defaults to 0
+    */
+    void setTopMargin(int marginTop);
+
+    /**
+      Defines if margins have to be strictly followed (even if that
+      means changing aspect ratio), or if the margins can be adapted
+      to keep aspect ratio.
+
+      Defaults to false.
+    */
+    void setStrictMargins(bool strictMargins);
+
+    /**
+      Defines if the page will be rasterized to an image with overprint
+      preview enabled before printing.
+
+      Defaults to false
+
+      \since 23.09
+    */
+    void setForceOverprintPreview(bool forceOverprintPreview);
+
+    /** Defines if the page will be rasterized to an image before printing. Defaults to false */
+    void setForceRasterize(bool forceRasterize);
+
+    /**
+      Sets the options for the PS export.
+     */
+    void setPSOptions(PSOptions options);
+
+    /**
+      The currently set options for the PS export.
+
+      The default flags are: Printing.
+     */
+    PSOptions psOptions() const;
+
+    /**
+      Sets a function that will be called each time a page is converted.
+
+      The payload belongs to the caller.
+     */
+    void setPageConvertedCallback(void (*callback)(int page, void *payload), void *payload);
+
+    bool convert() override;
+
+private:
+    Q_DECLARE_PRIVATE(PSConverter)
+    Q_DISABLE_COPY(PSConverter)
+
+    explicit PSConverter(DocumentData *document);
+};
+
+/**
+   Converts a PDF to PDF (thus saves a copy of the document).
+*/
+class POPPLER_QT6_EXPORT PDFConverter : public BaseConverter
+{
+    friend class Document;
+
+public:
+    /**
+      Options for the PDF export.
+     */
+    enum PDFOption
+    {
+        WithChanges = 0x00000001 ///< The changes done to the document are saved as well
+    };
+    Q_DECLARE_FLAGS(PDFOptions, PDFOption)
+
+    /**
+      Destructor.
+    */
+    ~PDFConverter() override;
+
+    /**
+      Sets the options for the PDF export.
+     */
+    void setPDFOptions(PDFOptions options);
+    /**
+      The currently set options for the PDF export.
+     */
+    PDFOptions pdfOptions() const;
+
+    /**
+     * Holds data for a new signature
+     *  - Common Name of cert to sign (aka nickname)
+     *  - password for the cert
+     *  - page where to add the signature
+     *  - rect for the signature annotation
+     *  - text that will be shown inside the rect
+     *  - font size and color
+     *  - border width and color
+     *  - background color
+     * \since 21.01
+     */
+    class POPPLER_QT6_EXPORT NewSignatureData
+    {
+    public:
+        NewSignatureData();
+        ~NewSignatureData();
+        NewSignatureData(const NewSignatureData &) = delete;
+        NewSignatureData &operator=(const NewSignatureData &) = delete;
+
+        QString certNickname() const;
+        void setCertNickname(const QString &certNickname);
+
+        QString password() const;
+        void setPassword(const QString &password);
+
+        int page() const;
+        void setPage(int page);
+
+        QRectF boundingRectangle() const;
+        void setBoundingRectangle(const QRectF &rect);
+
+        QString signatureText() const;
+        void setSignatureText(const QString &text);
+
+        /**
+         * If this text is not empty, the signature representation
+         * will split in two, with this text on the left and signatureText
+         * on the right
+         *
+         * \since 21.06
+         */
+        QString signatureLeftText() const;
+        void setSignatureLeftText(const QString &text);
+
+        /**
+         * Signature's property Reason.
+         *
+         * Default: an empty string.
+         *
+         * \since 21.10
+         */
+        QString reason() const;
+        void setReason(const QString &reason);
+
+        /**
+         * Signature's property Location.
+         *
+         * Default: an empty string.
+         *
+         * \since 21.10
+         */
+        QString location() const;
+        void setLocation(const QString &location);
+
+        /**
+         * Default: 10
+         */
+        double fontSize() const;
+        void setFontSize(double fontSize);
+
+        /**
+         * Default: 20
+         *
+         * \since 21.06
+         */
+        double leftFontSize() const;
+        void setLeftFontSize(double fontSize);
+
+        /**
+         * Default: red
+         */
+        QColor fontColor() const;
+        void setFontColor(const QColor &color);
+
+        /**
+         * Default: red
+         */
+        QColor borderColor() const;
+        void setBorderColor(const QColor &color);
+
+        /**
+         * border width in points
+         *
+         * Default: 1.5
+         *
+         * \since 21.05
+         */
+        double borderWidth() const;
+        void setBorderWidth(double width);
+
+        /**
+         * Default: QColor(240, 240, 240)
+         */
+        QColor backgroundColor() const;
+        void setBackgroundColor(const QColor &color);
+
+        /**
+         * Default: QUuid::createUuid().toString()
+         */
+        QString fieldPartialName() const;
+        void setFieldPartialName(const QString &name);
+
+        /**
+         * Document owner password (needed if the document that is being signed is password protected)
+         *
+         * Default: no password
+         *
+         * \since 22.02
+         */
+        QByteArray documentOwnerPassword() const;
+        void setDocumentOwnerPassword(const QByteArray &password);
+
+        /**
+         * Document user password (needed if the document that is being signed is password protected)
+         *
+         * Default: no password
+         *
+         * \since 22.02
+         */
+        QByteArray documentUserPassword() const;
+        void setDocumentUserPassword(const QByteArray &password);
+
+        /**
+         * Filesystem path to an image file to be used as background
+         * image for the signature annotation widget.
+         *
+         * Default: empty
+         *
+         * \since 22.02
+         */
+        QString imagePath() const;
+        void setImagePath(const QString &path);
+
+    private:
+        struct NewSignatureDataPrivate;
+        NewSignatureDataPrivate *const d;
+    };
+
+    /**
+        Sign PDF at given Annotation / signature form
+
+        \param data new signature data
+
+        \return whether the signing succeeded
+
+        \since 21.01
+    */
+    bool sign(const NewSignatureData &data);
+
+    bool convert() override;
+
+private:
+    Q_DECLARE_PRIVATE(PDFConverter)
+    Q_DISABLE_COPY(PDFConverter)
+
+    explicit PDFConverter(DocumentData *document);
+};
+
+}
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(Poppler::PDFConverter::PDFOptions)
+Q_DECLARE_OPERATORS_FOR_FLAGS(Poppler::PSConverter::PSOptions)
+
+#endif
diff --git a/qt6/src/poppler-qt6.h b/qt6/src/poppler-qt6.h
index eda10da..2350e3f 100644
--- a/qt6/src/poppler-qt6.h
+++ b/qt6/src/poppler-qt6.h
@@ -24,7 +24,7 @@
  * Copyright (C) 2020 Philipp Knechtges <philipp-dev@knechtges.com>
  * Copyright (C) 2020 Katarina Behrens <Katarina.Behrens@cib.de>
  * 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) 2020, 2024 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>. Work sponsored by Technische Universität Dresden
  * Copyright (C) 2021 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>.
  * Copyright (C) 2021 Mahmoud Khalil <mahmoudkhalil11@gmail.com>
  * Copyright (C) 2021 Georgiy Sgibnev <georgiy@sgibnev.com>. Work sponsored by lab50.net.
@@ -58,6 +58,7 @@
 #include "poppler-link.h"
 #include "poppler-optcontent.h"
 #include "poppler-page-transition.h"
+#include "poppler-converter.h"
 
 #include <QtCore/QByteArray>
 #include <QtCore/QDateTime>
@@ -1771,404 +1772,6 @@
     explicit Document(DocumentData *dataA);
 };
 
-class BaseConverterPrivate;
-class PSConverterPrivate;
-class PDFConverterPrivate;
-/**
-   \brief Base converter.
-
-   This is the base class for the converters.
-*/
-class POPPLER_QT6_EXPORT BaseConverter
-{
-    friend class Document;
-
-public:
-    /**
-      Destructor.
-    */
-    virtual ~BaseConverter();
-
-    /** Sets the output file name. You must set this or the output device. */
-    void setOutputFileName(const QString &outputFileName);
-
-    /**
-     * Sets the output device. You must set this or the output file name.
-     */
-    void setOutputDevice(QIODevice *device);
-
-    /**
-      Does the conversion.
-
-      \return whether the conversion succeeded
-    */
-    virtual bool convert() = 0;
-
-    enum Error
-    {
-        NoError,
-        FileLockedError,
-        OpenOutputError,
-        NotSupportedInputFileError
-    };
-
-    /**
-      Returns the last error
-    */
-    Error lastError() const;
-
-protected:
-    /// \cond PRIVATE
-    explicit BaseConverter(BaseConverterPrivate &dd);
-    Q_DECLARE_PRIVATE(BaseConverter)
-    BaseConverterPrivate *d_ptr;
-    /// \endcond
-
-private:
-    Q_DISABLE_COPY(BaseConverter)
-};
-
-/**
-   Converts a PDF to PS
-
-   Sizes have to be in Points (1/72 inch)
-
-   If you are using QPrinter you can get paper size by doing:
-   \code
-QPrinter dummy(QPrinter::PrinterResolution);
-dummy.setFullPage(true);
-dummy.setPageSize(myPageSize);
-width = dummy.width();
-height = dummy.height();
-   \endcode
-*/
-class POPPLER_QT6_EXPORT PSConverter : public BaseConverter
-{
-    friend class Document;
-
-public:
-    /**
-      Options for the PS export.
-     */
-    enum PSOption
-    {
-        Printing = 0x00000001, ///< The PS is generated for printing purposes
-        StrictMargins = 0x00000002,
-        ForceRasterization = 0x00000004,
-        PrintToEPS = 0x00000008, ///< Output EPS instead of PS
-        HideAnnotations = 0x00000010, ///< Don't print annotations
-        ForceOverprintPreview = 0x00000020 ///< Force rasterized overprint preview during conversion \since 23.09
-    };
-    Q_DECLARE_FLAGS(PSOptions, PSOption)
-
-    /**
-      Destructor.
-    */
-    ~PSConverter() override;
-
-    /** Sets the list of pages to print. Mandatory. */
-    void setPageList(const QList<int> &pageList);
-
-    /**
-      Sets the title of the PS Document. Optional
-    */
-    void setTitle(const QString &title);
-
-    /**
-      Sets the horizontal DPI. Defaults to 72.0
-    */
-    void setHDPI(double hDPI);
-
-    /**
-      Sets the vertical DPI. Defaults to 72.0
-    */
-    void setVDPI(double vDPI);
-
-    /**
-      Sets the rotate. Defaults to not rotated
-    */
-    void setRotate(int rotate);
-
-    /**
-      Sets the output paper width. Has to be set.
-    */
-    void setPaperWidth(int paperWidth);
-
-    /**
-      Sets the output paper height. Has to be set.
-    */
-    void setPaperHeight(int paperHeight);
-
-    /**
-      Sets the output right margin. Defaults to 0
-    */
-    void setRightMargin(int marginRight);
-
-    /**
-      Sets the output bottom margin. Defaults to 0
-    */
-    void setBottomMargin(int marginBottom);
-
-    /**
-      Sets the output left margin. Defaults to 0
-    */
-    void setLeftMargin(int marginLeft);
-
-    /**
-      Sets the output top margin. Defaults to 0
-    */
-    void setTopMargin(int marginTop);
-
-    /**
-      Defines if margins have to be strictly followed (even if that
-      means changing aspect ratio), or if the margins can be adapted
-      to keep aspect ratio.
-
-      Defaults to false.
-    */
-    void setStrictMargins(bool strictMargins);
-
-    /**
-      Defines if the page will be rasterized to an image with overprint
-      preview enabled before printing.
-
-      Defaults to false
-
-      \since 23.09
-    */
-    void setForceOverprintPreview(bool forceOverprintPreview);
-
-    /** Defines if the page will be rasterized to an image before printing. Defaults to false */
-    void setForceRasterize(bool forceRasterize);
-
-    /**
-      Sets the options for the PS export.
-     */
-    void setPSOptions(PSOptions options);
-
-    /**
-      The currently set options for the PS export.
-
-      The default flags are: Printing.
-     */
-    PSOptions psOptions() const;
-
-    /**
-      Sets a function that will be called each time a page is converted.
-
-      The payload belongs to the caller.
-     */
-    void setPageConvertedCallback(void (*callback)(int page, void *payload), void *payload);
-
-    bool convert() override;
-
-private:
-    Q_DECLARE_PRIVATE(PSConverter)
-    Q_DISABLE_COPY(PSConverter)
-
-    explicit PSConverter(DocumentData *document);
-};
-
-/**
-   Converts a PDF to PDF (thus saves a copy of the document).
-*/
-class POPPLER_QT6_EXPORT PDFConverter : public BaseConverter
-{
-    friend class Document;
-
-public:
-    /**
-      Options for the PDF export.
-     */
-    enum PDFOption
-    {
-        WithChanges = 0x00000001 ///< The changes done to the document are saved as well
-    };
-    Q_DECLARE_FLAGS(PDFOptions, PDFOption)
-
-    /**
-      Destructor.
-    */
-    ~PDFConverter() override;
-
-    /**
-      Sets the options for the PDF export.
-     */
-    void setPDFOptions(PDFOptions options);
-    /**
-      The currently set options for the PDF export.
-     */
-    PDFOptions pdfOptions() const;
-
-    /**
-     * Holds data for a new signature
-     *  - Common Name of cert to sign (aka nickname)
-     *  - password for the cert
-     *  - page where to add the signature
-     *  - rect for the signature annotation
-     *  - text that will be shown inside the rect
-     *  - font size and color
-     *  - border width and color
-     *  - background color
-     * \since 21.01
-     */
-    class POPPLER_QT6_EXPORT NewSignatureData
-    {
-    public:
-        NewSignatureData();
-        ~NewSignatureData();
-        NewSignatureData(const NewSignatureData &) = delete;
-        NewSignatureData &operator=(const NewSignatureData &) = delete;
-
-        QString certNickname() const;
-        void setCertNickname(const QString &certNickname);
-
-        QString password() const;
-        void setPassword(const QString &password);
-
-        int page() const;
-        void setPage(int page);
-
-        QRectF boundingRectangle() const;
-        void setBoundingRectangle(const QRectF &rect);
-
-        QString signatureText() const;
-        void setSignatureText(const QString &text);
-
-        /**
-         * If this text is not empty, the signature representation
-         * will split in two, with this text on the left and signatureText
-         * on the right
-         *
-         * \since 21.06
-         */
-        QString signatureLeftText() const;
-        void setSignatureLeftText(const QString &text);
-
-        /**
-         * Signature's property Reason.
-         *
-         * Default: an empty string.
-         *
-         * \since 21.10
-         */
-        QString reason() const;
-        void setReason(const QString &reason);
-
-        /**
-         * Signature's property Location.
-         *
-         * Default: an empty string.
-         *
-         * \since 21.10
-         */
-        QString location() const;
-        void setLocation(const QString &location);
-
-        /**
-         * Default: 10
-         */
-        double fontSize() const;
-        void setFontSize(double fontSize);
-
-        /**
-         * Default: 20
-         *
-         * \since 21.06
-         */
-        double leftFontSize() const;
-        void setLeftFontSize(double fontSize);
-
-        /**
-         * Default: red
-         */
-        QColor fontColor() const;
-        void setFontColor(const QColor &color);
-
-        /**
-         * Default: red
-         */
-        QColor borderColor() const;
-        void setBorderColor(const QColor &color);
-
-        /**
-         * border width in points
-         *
-         * Default: 1.5
-         *
-         * \since 21.05
-         */
-        double borderWidth() const;
-        void setBorderWidth(double width);
-
-        /**
-         * Default: QColor(240, 240, 240)
-         */
-        QColor backgroundColor() const;
-        void setBackgroundColor(const QColor &color);
-
-        /**
-         * Default: QUuid::createUuid().toString()
-         */
-        QString fieldPartialName() const;
-        void setFieldPartialName(const QString &name);
-
-        /**
-         * Document owner password (needed if the document that is being signed is password protected)
-         *
-         * Default: no password
-         *
-         * \since 22.02
-         */
-        QByteArray documentOwnerPassword() const;
-        void setDocumentOwnerPassword(const QByteArray &password);
-
-        /**
-         * Document user password (needed if the document that is being signed is password protected)
-         *
-         * Default: no password
-         *
-         * \since 22.02
-         */
-        QByteArray documentUserPassword() const;
-        void setDocumentUserPassword(const QByteArray &password);
-
-        /**
-         * Filesystem path to an image file to be used as background
-         * image for the signature annotation widget.
-         *
-         * Default: empty
-         *
-         * \since 22.02
-         */
-        QString imagePath() const;
-        void setImagePath(const QString &path);
-
-    private:
-        struct NewSignatureDataPrivate;
-        NewSignatureDataPrivate *const d;
-    };
-
-    /**
-        Sign PDF at given Annotation / signature form
-
-        \param data new signature data
-
-        \return whether the signing succeeded
-
-        \since 21.01
-    */
-    bool sign(const NewSignatureData &data);
-
-    bool convert() override;
-
-private:
-    Q_DECLARE_PRIVATE(PDFConverter)
-    Q_DISABLE_COPY(PDFConverter)
-
-    explicit PDFConverter(DocumentData *document);
-};
-
 /**
    Conversion from PDF date string format to QDateTime
 */
@@ -2338,7 +1941,5 @@
 Q_DECLARE_OPERATORS_FOR_FLAGS(Poppler::Page::PainterFlags)
 Q_DECLARE_OPERATORS_FOR_FLAGS(Poppler::Page::SearchFlags)
 Q_DECLARE_OPERATORS_FOR_FLAGS(Poppler::Document::RenderHints)
-Q_DECLARE_OPERATORS_FOR_FLAGS(Poppler::PDFConverter::PDFOptions)
-Q_DECLARE_OPERATORS_FOR_FLAGS(Poppler::PSConverter::PSOptions)
 
 #endif