[poppler] poppler/Annot.cc poppler/Annot.h poppler/Catalog.cc poppler/Catalog.h poppler/Dict.cc poppler/Dict.h poppler/Form.cc poppler/Form.h poppler/GlobalParams.cc poppler/GlobalParams.h poppler/GlobalParamsWin.cc poppler/PDFDoc.cc qt5/src qt6/src

GitLab Mirror gitlab-mirror at kemper.freedesktop.org
Wed Apr 6 12:12:39 UTC 2022


 poppler/Annot.cc              |  194 +++++++++++++++++++++++----
 poppler/Annot.h               |    7 
 poppler/Catalog.cc            |   66 +++++----
 poppler/Catalog.h             |    2 
 poppler/Dict.cc               |   10 +
 poppler/Dict.h                |    7 
 poppler/Form.cc               |  301 +++++++++++++++++++++++++++++++++++++++++-
 poppler/Form.h                |   26 +++
 poppler/GlobalParams.cc       |  117 ++++++++++++++++
 poppler/GlobalParams.h        |   28 +++
 poppler/GlobalParamsWin.cc    |   47 ++++++
 poppler/PDFDoc.cc             |    2 
 qt5/src/poppler-annotation.cc |   68 ++++++---
 qt6/src/poppler-annotation.cc |   69 ++++++---
 14 files changed, 842 insertions(+), 102 deletions(-)

New commits:
commit 5f915d46c99ecbc0c026b86de50f9e0243391a01
Author: Albert Astals Cid <aacid at kde.org>
Date:   Tue Feb 22 16:01:19 2022 +0100

    Annotations: Make sure we embed fonts for the FreeText annots

diff --git a/poppler/Annot.cc b/poppler/Annot.cc
index 366e7de1..8312cf97 100644
--- a/poppler/Annot.cc
+++ b/poppler/Annot.cc
@@ -1513,6 +1513,8 @@ void Annot::update(const char *key, Object &&value)
     annotObj.dictSet(const_cast<char *>(key), std::move(value));
 
     doc->getXRef()->setModifiedObject(&annotObj, ref);
+
+    hasBeenUpdated = true;
 }
 
 void Annot::setContents(std::unique_ptr<GooString> &&new_content)
@@ -2094,6 +2096,7 @@ void Annot::setNewAppearance(Object &&newAppearance)
         Object obj1 = Object(new Dict(doc->getXRef()));
         obj1.dictAdd("N", Object(updatedAppearanceStream));
         update("AP", std::move(obj1));
+        update("AS", Object(objName, "N"));
 
         Object updatedAP = annotObj.dictLookup("AP");
         appearStreams = std::make_unique<AnnotAppearance>(doc, &updatedAP);
@@ -2831,13 +2834,12 @@ void AnnotLink::draw(Gfx *gfx, bool printing)
 //------------------------------------------------------------------------
 const double AnnotFreeText::undefinedFontPtSize = 10.;
 
-AnnotFreeText::AnnotFreeText(PDFDoc *docA, PDFRectangle *rectA, const DefaultAppearance &da) : AnnotMarkup(docA, rectA)
+AnnotFreeText::AnnotFreeText(PDFDoc *docA, PDFRectangle *rectA) : AnnotMarkup(docA, rectA)
 {
     type = typeFreeText;
 
-    const std::string daStr = da.toAppearanceString();
     annotObj.dictSet("Subtype", Object(objName, "FreeText"));
-    annotObj.dictSet("DA", Object(new GooString(daStr)));
+    annotObj.dictSet("DA", Object(new GooString()));
 
     initialize(docA, annotObj.getDict());
 }
@@ -3041,6 +3043,64 @@ static std::unique_ptr<GfxFont> createAnnotDrawFont(XRef *xref, Dict *fontParent
     return GfxFont::makeFont(xref, resourceName, dummyRef, fontDict);
 }
 
+class HorizontalTextLayouter
+{
+public:
+    void add(const std::string &text, const std::string &fontName, const double width) { data.emplace_back(text, fontName, width); }
+
+    std::string layout(const VariableTextQuadding quadding, const double availableWidth, const double fontSize, double &xposPrev) const
+    {
+        double totalWidth = 0;
+        for (const Data &d : data) {
+            totalWidth += d.width;
+        }
+
+        double newXpos;
+        switch (quadding) {
+        case VariableTextQuadding::centered:
+            newXpos = (availableWidth - totalWidth) / 2;
+            break;
+        case VariableTextQuadding::rightJustified:
+            newXpos = availableWidth - totalWidth;
+            break;
+        default: // VariableTextQuadding::lLeftJustified:
+            newXpos = 0;
+            break;
+        }
+
+        AnnotAppearanceBuilder builder;
+        bool first = true;
+        double prevBlockWidth = 0;
+        for (const Data &d : data) {
+            builder.appendf("/{0:s} {1:.2f} Tf\n", d.fontName.c_str(), fontSize);
+
+            const double yDiff = first ? -fontSize : 0;
+            const double xDiff = first ? newXpos - xposPrev : prevBlockWidth;
+
+            builder.appendf("{0:.2f} {1:.2f} Td\n", xDiff, yDiff);
+            builder.writeString(d.text);
+            builder.append("Tj\n");
+            first = false;
+            prevBlockWidth = d.width;
+        }
+        xposPrev = newXpos + totalWidth - prevBlockWidth;
+
+        return builder.buffer()->toStr();
+    }
+
+private:
+    struct Data
+    {
+        Data(const std::string &t, const std::string &fName, double w) : text(t), fontName(fName), width(w) { }
+
+        const std::string text;
+        const std::string fontName;
+        const double width;
+    };
+
+    std::vector<Data> data;
+};
+
 void AnnotFreeText::generateFreeTextAppearance()
 {
     double borderWidth, ca = opacity;
@@ -3132,30 +3192,78 @@ void AnnotFreeText::generateFreeTextAppearance()
     // Set font state
     appearBuilder.setDrawColor(da.getFontColor(), true);
     appearBuilder.appendf("BT 1 0 0 1 {0:.2f} {1:.2f} Tm\n", textmargin, height - textmargin - da.getFontPtSize() * font->getDescent());
-    appearBuilder.setTextFont(da.getFontName(), da.getFontPtSize());
 
     int i = 0;
     double xposPrev = 0;
+    const bool isUnicode = contents->hasUnicodeMarker();
     while (i < contents->getLength()) {
+        HorizontalTextLayouter textLayouter;
+
         GooString out;
-        double linewidth, xpos;
-        layoutText(contents.get(), &out, &i, *font, &linewidth, textwidth / da.getFontPtSize(), nullptr, false);
-        linewidth *= da.getFontPtSize();
-        switch (quadding) {
-        case VariableTextQuadding::centered:
-            xpos = (textwidth - linewidth) / 2;
-            break;
-        case VariableTextQuadding::rightJustified:
-            xpos = textwidth - linewidth;
-            break;
-        default: // VariableTextQuadding::leftJustified:
-            xpos = 0;
-            break;
+        double availableTextWidthInFontPtSize = textwidth / da.getFontPtSize();
+        double blockWidth;
+        bool newFontNeeded;
+        layoutText(contents.get(), &out, &i, *font, &blockWidth, availableTextWidthInFontPtSize, nullptr, false, &newFontNeeded);
+        availableTextWidthInFontPtSize -= blockWidth;
+        textLayouter.add(out.toStr(), da.getFontName().getName(), blockWidth * da.getFontPtSize());
+
+        while (availableTextWidthInFontPtSize >= 0 && newFontNeeded) {
+            if (!form) {
+                // There's no fonts to look for, so just skip the characters
+                i += isUnicode ? 2 : 1;
+                error(errSyntaxError, -1, "AnnotFreeText::generateFreeTextAppearance, found character that the font can't represent");
+                newFontNeeded = false;
+            } else {
+                Unicode uChar;
+                if (isUnicode) {
+                    uChar = (unsigned char)(contents->getChar(i)) << 8;
+                    uChar += (unsigned char)(contents->getChar(i + 1));
+                } else {
+                    uChar = pdfDocEncoding[contents->getChar(i) & 0xff];
+                }
+                const std::string auxFontName = form->getFallbackFontForChar(uChar, *font);
+                if (!auxFontName.empty()) {
+                    std::shared_ptr<GfxFont> auxFont = form->getDefaultResources()->lookupFont(auxFontName.c_str());
+
+                    // Here we just layout one char, we don't know if the one afterwards can be layouted with the original font
+                    GooString auxContents = GooString(contents->toStr().substr(i, isUnicode ? 2 : 1));
+                    if (isUnicode) {
+                        auxContents.prependUnicodeMarker();
+                    }
+                    int auxI = 0;
+                    layoutText(&auxContents, &out, &auxI, *auxFont, &blockWidth, availableTextWidthInFontPtSize, nullptr, false, &newFontNeeded);
+                    assert(!newFontNeeded);
+                    // layoutText will always at least layout one character even if it doesn't fit in
+                    // the given space which makes sense (except in the case of switching fonts, so we control if we ran out of space here manually)
+                    availableTextWidthInFontPtSize -= blockWidth;
+                    if (availableTextWidthInFontPtSize >= 0) {
+                        i += auxI - (isUnicode ? 2 : 0); // the second term is the unicode marker
+                        textLayouter.add(out.toStr(), auxFontName, blockWidth * da.getFontPtSize());
+                    }
+
+                } else {
+                    error(errSyntaxError, -1, "AnnotFreeText::generateFreeTextAppearance, couldn't find a font for character U+{0:04uX}", uChar);
+                    newFontNeeded = false;
+                    i += contents->hasUnicodeMarker() ? 2 : 1;
+                }
+            }
+
+            // Now layout the rest of the text with the original font if we still have space
+            if (availableTextWidthInFontPtSize >= 0) {
+                layoutText(contents.get(), &out, &i, *font, &blockWidth, availableTextWidthInFontPtSize, nullptr, false, &newFontNeeded);
+                availableTextWidthInFontPtSize -= blockWidth;
+                // layoutText will always at least layout one character even if it doesn't fit in
+                // the given space which makes sense (except in the case of switching fonts, so we control if we ran out of space here manually)
+                if (availableTextWidthInFontPtSize >= 0) {
+                    textLayouter.add(out.toStr(), da.getFontName().getName(), blockWidth * da.getFontPtSize());
+                } else {
+                    i -= isUnicode ? 2 : 1;
+                }
+            }
         }
-        appearBuilder.appendf("{0:.2f} {1:.2f} Td\n", xpos - xposPrev, -da.getFontPtSize());
-        appearBuilder.writeString(out.toStr());
-        appearBuilder.append("Tj\n");
-        xposPrev = xpos;
+
+        const std::string layoutedLine = textLayouter.layout(quadding, textwidth, da.getFontPtSize(), xposPrev);
+        appearBuilder.append(layoutedLine.c_str());
     }
 
     appearBuilder.append("ET Q\n");
@@ -3165,14 +3273,23 @@ void AnnotFreeText::generateFreeTextAppearance()
     bbox[2] = rect->x2 - rect->x1;
     bbox[3] = rect->y2 - rect->y1;
 
+    Object newAppearance;
     if (ca == 1) {
-        appearance = createForm(appearBuilder.buffer(), bbox, false, std::move(resourceObj));
+        newAppearance = createForm(appearBuilder.buffer(), bbox, false, std::move(resourceObj));
     } else {
         Object aStream = createForm(appearBuilder.buffer(), bbox, true, std::move(resourceObj));
 
         GooString appearBuf("/GS0 gs\n/Fm0 Do");
         Dict *resDict = createResourcesDict("Fm0", std::move(aStream), "GS0", ca, nullptr);
-        appearance = createForm(&appearBuf, bbox, false, resDict);
+        newAppearance = createForm(&appearBuf, bbox, false, resDict);
+    }
+    if (hasBeenUpdated) {
+        // We should technically do this for all annots but AnnotFreeText
+        // is particularly special since we're potentially embeddeing a font so we really need
+        // to set the AP and not let other renderers guess it from the contents
+        setNewAppearance(std::move(newAppearance));
+    } else {
+        appearance = std::move(newAppearance);
     }
 }
 
@@ -4078,7 +4195,7 @@ bool AnnotWidget::setFormAdditionalAction(FormAdditionalActionsType formAddition
 // TODO: Handle surrogate pairs in UTF-16.
 //       Should be able to generate output for any CID-keyed font.
 //       Doesn't handle vertical fonts--should it?
-void Annot::layoutText(const GooString *text, GooString *outBuf, int *i, const GfxFont &font, double *width, double widthLimit, int *charCount, bool noReencode)
+void Annot::layoutText(const GooString *text, GooString *outBuf, int *i, const GfxFont &font, double *width, double widthLimit, int *charCount, bool noReencode, bool *newFontNeeded)
 {
     CharCode c;
     Unicode uChar;
@@ -4087,6 +4204,9 @@ void Annot::layoutText(const GooString *text, GooString *outBuf, int *i, const G
     int uLen, n;
     double dx, dy, ox, oy;
 
+    if (newFontNeeded) {
+        *newFontNeeded = false;
+    }
     if (width != nullptr) {
         *width = 0.0;
     }
@@ -4171,10 +4291,28 @@ void Annot::layoutText(const GooString *text, GooString *outBuf, int *i, const G
                 outBuf->append(uChar & 0xff);
             } else if (ccToUnicode->mapToCharCode(&uChar, &c, 1)) {
                 if (font.isCIDFont()) {
-                    // TODO: This assumes an identity CMap.  It should be extended to
-                    // handle the general case.
-                    outBuf->append((c >> 8) & 0xff);
-                    outBuf->append(c & 0xff);
+                    auto cidFont = static_cast<const GfxCIDFont *>(&font);
+                    if (c < cidFont->getCIDToGIDLen()) {
+                        const int glyph = cidFont->getCIDToGID()[c];
+                        if (glyph > 0 || c == 0) {
+                            outBuf->append((c >> 8) & 0xff);
+                            outBuf->append(c & 0xff);
+                        } else {
+                            if (newFontNeeded) {
+                                *newFontNeeded = true;
+                                *i -= unicode ? 2 : 1;
+                                break;
+                            }
+                            outBuf->append((c >> 8) & 0xff);
+                            outBuf->append(c & 0xff);
+                            error(errSyntaxError, -1, "AnnotWidget::layoutText, font doesn't have glyph for charcode U+{0:04uX}", c);
+                        }
+                    } else {
+                        // TODO: This assumes an identity CMap.  It should be extended to
+                        // handle the general case.
+                        outBuf->append((c >> 8) & 0xff);
+                        outBuf->append(c & 0xff);
+                    }
                 } else {
                     // 8-bit font
                     outBuf->append(c);
diff --git a/poppler/Annot.h b/poppler/Annot.h
index 19688712..29e8f6ea 100644
--- a/poppler/Annot.h
+++ b/poppler/Annot.h
@@ -772,7 +772,8 @@ public:
     // Check if point is inside the annot rectangle.
     bool inRect(double x, double y) const;
 
-    static void layoutText(const GooString *text, GooString *outBuf, int *i, const GfxFont &font, double *width, double widthLimit, int *charCount, bool noReencode);
+    // If newFontNeeded is not null, it will contain whether the given font has glyphs to represent the needed text
+    static void layoutText(const GooString *text, GooString *outBuf, int *i, const GfxFont &font, double *width, double widthLimit, int *charCount, bool noReencode, bool *newFontNeeded = nullptr);
 
 private:
     void readArrayNum(Object *pdfArray, int key, double *value);
@@ -827,6 +828,8 @@ protected:
 
     bool hasRef;
     mutable std::recursive_mutex mutex;
+
+    bool hasBeenUpdated = false;
 };
 
 //------------------------------------------------------------------------
@@ -1055,7 +1058,7 @@ public:
 
     static const double undefinedFontPtSize;
 
-    AnnotFreeText(PDFDoc *docA, PDFRectangle *rect, const DefaultAppearance &da);
+    AnnotFreeText(PDFDoc *docA, PDFRectangle *rect);
     AnnotFreeText(PDFDoc *docA, Object &&dictObject, const Object *obj);
     ~AnnotFreeText() override;
 
diff --git a/poppler/Catalog.cc b/poppler/Catalog.cc
index 986577a7..add93530 100644
--- a/poppler/Catalog.cc
+++ b/poppler/Catalog.cc
@@ -103,7 +103,13 @@ Catalog::Catalog(PDFDoc *docA)
         return;
     }
     // get the AcroForm dictionary
-    acroForm = catDict.dictLookup("AcroForm");
+    acroForm = catDict.getDict()->lookup("AcroForm");
+    if (acroForm.isDict()) {
+        // We later assume the Fields Array exists, so check it does
+        if (!acroForm.dictLookup("Fields").isArray()) {
+            acroForm.setToNull();
+        }
+    }
 
     // read base URI
     Object obj = catDict.getDict()->lookupEnsureEncryptedIfNeeded("URI");
@@ -1055,6 +1061,26 @@ Catalog::FormType Catalog::getFormType()
     return res;
 }
 
+Form *Catalog::getCreateForm()
+{
+    catalogLocker();
+    if (!form) {
+        if (!acroForm.isDict()) {
+            acroForm = Object(new Dict(xref));
+            acroForm.dictSet("Fields", Object(new Array(xref)));
+
+            const Ref newFormRef = xref->addIndirectObject(acroForm);
+
+            Object catDict = xref->getCatalog();
+            catDict.dictSet("AcroForm", Object(newFormRef));
+
+            xref->setModifiedObject(&catDict, { xref->getRootNum(), xref->getRootGen() });
+        }
+    }
+
+    return getForm();
+}
+
 Form *Catalog::getForm()
 {
     catalogLocker();
@@ -1073,28 +1099,23 @@ void Catalog::addFormToAcroForm(const Ref formRef)
 {
     catalogLocker();
 
-    Object catDict = xref->getCatalog();
-    Ref acroFormRef;
-    acroForm = catDict.getDict()->lookup("AcroForm", &acroFormRef);
-
     if (!acroForm.isDict()) {
-        // none there yet, need to create a new fields dict
-        Object newForm = Object(new Dict(xref));
-        newForm.dictSet("SigFlags", Object(3));
+        getCreateForm();
+    }
 
-        Array *fieldArray = new Array(xref);
-        fieldArray->add(Object(formRef));
-        newForm.dictSet("Fields", Object(fieldArray));
+    // append to field array
+    Ref fieldRef;
+    Object fieldArray = acroForm.getDict()->lookup("Fields", &fieldRef);
+    fieldArray.getArray()->add(Object(formRef));
 
-        Ref newRef = xref->addIndirectObject(newForm);
-        catDict.dictSet("AcroForm", Object(newRef));
-        acroForm = catDict.getDict()->lookup("AcroForm");
-    } else {
-        // append to field array
-        Ref fieldRef;
-        Object fieldArray = acroForm.getDict()->lookup("Fields", &fieldRef);
-        fieldArray.getArray()->add(Object(formRef));
-    }
+    setAcroFormModified();
+}
+
+void Catalog::setAcroFormModified()
+{
+    Object catDict = xref->getCatalog();
+    Ref acroFormRef;
+    catDict.getDict()->lookup("AcroForm", &acroFormRef);
 
     if (acroFormRef != Ref::INVALID()) {
         xref->setModifiedObject(&acroForm, acroFormRef);
@@ -1108,9 +1129,6 @@ void Catalog::removeFormFromAcroForm(const Ref formRef)
     catalogLocker();
 
     Object catDict = xref->getCatalog();
-    Ref acroFormRef;
-    acroForm = catDict.getDict()->lookup("AcroForm", &acroFormRef);
-
     if (acroForm.isDict()) {
         // remove from field array
         Ref fieldRef;
@@ -1124,7 +1142,7 @@ void Catalog::removeFormFromAcroForm(const Ref formRef)
             }
         }
 
-        xref->setModifiedObject(&acroForm, acroFormRef);
+        setAcroFormModified();
     }
 }
 
diff --git a/poppler/Catalog.h b/poppler/Catalog.h
index 70ca99ca..03c7314c 100644
--- a/poppler/Catalog.h
+++ b/poppler/Catalog.h
@@ -212,6 +212,7 @@ public:
     Object *getAcroForm() { return &acroForm; }
     void addFormToAcroForm(const Ref formRef);
     void removeFormFromAcroForm(const Ref formRef);
+    void setAcroFormModified();
 
     OCGs *getOptContentConfig() { return optContent; }
 
@@ -226,6 +227,7 @@ public:
     };
 
     FormType getFormType();
+    Form *getCreateForm();
     Form *getForm();
 
     ViewerPreferences *getViewerPreferences();
diff --git a/poppler/Dict.cc b/poppler/Dict.cc
index 59936c9f..789f90ed 100644
--- a/poppler/Dict.cc
+++ b/poppler/Dict.cc
@@ -247,3 +247,13 @@ bool Dict::hasKey(const char *key) const
 {
     return find(key) != nullptr;
 }
+
+std::string Dict::findAvailableKey(const std::string &suggestedKey)
+{
+    int i = 0;
+    std::string res = suggestedKey;
+    while (find(res.c_str())) {
+        res = suggestedKey + std::to_string(i++);
+    }
+    return res;
+}
diff --git a/poppler/Dict.h b/poppler/Dict.h
index 1639ab44..621b76a2 100644
--- a/poppler/Dict.h
+++ b/poppler/Dict.h
@@ -16,7 +16,7 @@
 // Copyright (C) 2005 Kristian Høgsberg <krh at redhat.com>
 // Copyright (C) 2006 Krzysztof Kowalczyk <kkowalczyk at gmail.com>
 // Copyright (C) 2007-2008 Julien Rebetez <julienr at svn.gnome.org>
-// Copyright (C) 2010, 2017-2021 Albert Astals Cid <aacid at kde.org>
+// Copyright (C) 2010, 2017-2022 Albert Astals Cid <aacid at kde.org>
 // Copyright (C) 2010 Paweł Wiejacha <pawel.wiejacha at gmail.com>
 // Copyright (C) 2013 Thomas Freitag <Thomas.Freitag at alfa.de>
 // Copyright (C) 2017 Adrian Johnson <ajohnson at redneon.com>
@@ -103,6 +103,11 @@ public:
 
     bool hasKey(const char *key) const;
 
+    // Returns a key name that is not in the dictionary
+    // It will be suggestedKey itself if available
+    // otherwise it will start adding 0, 1, 2, 3, etc. to suggestedKey until there's one available
+    std::string findAvailableKey(const std::string &suggestedKey);
+
 private:
     friend class Object; // for incRef/decRef
 
diff --git a/poppler/Form.cc b/poppler/Form.cc
index 43ae67ba..165e7288 100644
--- a/poppler/Form.cc
+++ b/poppler/Form.cc
@@ -49,10 +49,13 @@
 #include "goo/GooString.h"
 #include "Error.h"
 #include "ErrorCodes.h"
+#include "CharCodeToUnicode.h"
 #include "Object.h"
 #include "Array.h"
 #include "Dict.h"
 #include "Gfx.h"
+#include "GfxFont.h"
+#include "GlobalParams.h"
 #include "Form.h"
 #include "PDFDoc.h"
 #include "DateInfo.h"
@@ -68,6 +71,12 @@
 #include "Lexer.h"
 #include "Parser.h"
 
+#include "fofi/FoFiTrueType.h"
+#include "fofi/FoFiIdentifier.h"
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
 // return a newly allocated char* containing an UTF16BE string of size length
 char *pdfDocEncodingToUTF16(const std::string &orig, int *length)
 {
@@ -2489,7 +2498,7 @@ void FormFieldSignature::print(int indent)
 // Form
 //------------------------------------------------------------------------
 
-Form::Form(PDFDoc *doc)
+Form::Form(PDFDoc *docA) : doc(docA)
 {
     Object obj1;
 
@@ -2645,6 +2654,296 @@ FormField *Form::createFieldFromDict(Object &&obj, PDFDoc *docA, const Ref aref,
     return field;
 }
 
+static const std::string kOurDictFontNamePrefix = "popplerfont";
+
+std::string Form::findFontInDefaultResources(const std::string &fontFamily, const std::string &fontStyle)
+{
+    if (!resDict.isDict()) {
+        return {};
+    }
+
+    const std::string fontFamilyAndStyle = fontFamily + " " + fontStyle;
+
+    Object fontDictObj = resDict.dictLookup("Font");
+    assert(fontDictObj.isDict());
+
+    const Dict *fontDict = fontDictObj.getDict();
+    for (int i = 0; i < fontDict->getLength(); ++i) {
+        const char *key = fontDict->getKey(i);
+        if (GooString::startsWith(key, kOurDictFontNamePrefix)) {
+            const Object fontObj = fontDict->getVal(i);
+            if (fontObj.isDict() && fontObj.dictIs("Font")) {
+                const Object fontBaseFontObj = fontObj.dictLookup("BaseFont");
+                if (fontBaseFontObj.isName(fontFamilyAndStyle.c_str())) {
+                    return key;
+                }
+            }
+        }
+    }
+
+    return {};
+}
+
+std::string Form::addFontToDefaultResources(const std::string &fontFamily, const std::string &fontStyle)
+{
+    const FamilyStyleFontSearchResult res = globalParams->findSystemFontFileForFamilyAndStyle(fontFamily, fontStyle);
+
+    return addFontToDefaultResources(res.filepath, res.faceIndex, fontFamily, fontStyle);
+}
+
+std::string Form::addFontToDefaultResources(const std::string &filepath, int faceIndex, const std::string &fontFamily, const std::string &fontStyle)
+{
+    if (!GooString::endsWith(filepath, ".ttf") && !GooString::endsWith(filepath, ".ttc") && !GooString::endsWith(filepath, ".otf")) {
+        error(errIO, -1, "We only support embedding ttf/ttc/otf fonts for now. The font file for %s %s was %s", fontFamily.c_str(), fontStyle.c_str(), filepath.c_str());
+        return {};
+    }
+
+    const FoFiIdentifierType fontFoFiType = FoFiIdentifier::identifyFile(filepath.c_str());
+    if (fontFoFiType != fofiIdTrueType && fontFoFiType != fofiIdTrueTypeCollection && fontFoFiType != fofiIdOpenTypeCFF8Bit && fontFoFiType != fofiIdOpenTypeCFFCID) {
+        error(errIO, -1, "We only support embedding ttf/ttc/otf fonts for now. The font file for %s %s was %s of type %d", fontFamily.c_str(), fontStyle.c_str(), filepath.c_str(), fontFoFiType);
+        return {};
+    }
+
+    const std::string fontFamilyAndStyle = fontFamily + " " + fontStyle;
+
+    XRef *xref = doc->getXRef();
+    Object fontDict(new Dict(xref));
+    fontDict.dictSet("Type", Object(objName, "Font"));
+    fontDict.dictSet("Subtype", Object(objName, "Type0"));
+    fontDict.dictSet("BaseFont", Object(objName, fontFamilyAndStyle.c_str()));
+
+    fontDict.dictSet("Encoding", Object(objName, "Identity-H"));
+
+    {
+        std::unique_ptr<Array> descendantFonts = std::make_unique<Array>(xref);
+
+        const bool isTrueType = (fontFoFiType == fofiIdTrueType || fontFoFiType == fofiIdTrueTypeCollection);
+        std::unique_ptr<Dict> descendantFont = std::make_unique<Dict>(xref);
+        descendantFont->set("Type", Object(objName, "Font"));
+        descendantFont->set("Subtype", Object(objName, isTrueType ? "CIDFontType2" : "CIDFontType0"));
+        descendantFont->set("BaseFont", Object(objName, fontFamilyAndStyle.c_str()));
+
+        {
+            // We only support fonts with identity cmaps for now
+            Dict *cidSystemInfo = new Dict(xref);
+            cidSystemInfo->set("Registry", Object(new GooString("Adobe")));
+            cidSystemInfo->set("Ordering", Object(new GooString("Identity")));
+            cidSystemInfo->set("Supplement", Object(0));
+            descendantFont->set("CIDSystemInfo", Object(cidSystemInfo));
+        }
+
+        FT_Library freetypeLib;
+        if (FT_Init_FreeType(&freetypeLib)) {
+            error(errIO, -1, "FT_Init_FreeType failed");
+            return {};
+        }
+        const std::unique_ptr<FT_Library, void (*)(FT_Library *)> freetypeLibDeleter(&freetypeLib, [](FT_Library *l) { FT_Done_FreeType(*l); });
+
+        FT_Face face;
+        if (FT_New_Face(freetypeLib, filepath.c_str(), faceIndex, &face)) {
+            error(errIO, -1, "FT_New_Face failed for %s", filepath.c_str());
+            return {};
+        }
+        const std::unique_ptr<FT_Face, void (*)(FT_Face *)> faceDeleter(&face, [](FT_Face *f) { FT_Done_Face(*f); });
+
+        if (FT_Set_Char_Size(face, 1000, 1000, 0, 0)) {
+            error(errIO, -1, "FT_Set_Char_Size failed for %s", filepath.c_str());
+            return {};
+        }
+
+        {
+            std::unique_ptr<Dict> fontDescriptor = std::make_unique<Dict>(xref);
+            fontDescriptor->set("Type", Object(objName, "FontDescriptor"));
+            fontDescriptor->set("FontName", Object(objName, "Noto Sans"));
+
+            // a bit arbirary but the Flags field is mandatory...
+            const std::string lowerCaseFontFamily = GooString::toLowerCase(fontFamily);
+            if (lowerCaseFontFamily.find("serif") != std::string::npos && lowerCaseFontFamily.find("sans") == std::string::npos) {
+                fontDescriptor->set("Flags", Object(2)); // Serif
+            } else {
+                fontDescriptor->set("Flags", Object(0)); // Sans Serif
+            }
+
+            Array *fontBBox = new Array(xref);
+            fontBBox->add(Object(static_cast<int>(face->bbox.xMin)));
+            fontBBox->add(Object(static_cast<int>(face->bbox.yMin)));
+            fontBBox->add(Object(static_cast<int>(face->bbox.xMax)));
+            fontBBox->add(Object(static_cast<int>(face->bbox.yMax)));
+            fontDescriptor->set("FontBBox", Object(fontBBox));
+
+            fontDescriptor->set("Ascent", Object(static_cast<int>(face->ascender)));
+
+            fontDescriptor->set("Descent", Object(static_cast<int>(face->descender)));
+
+            {
+                const std::unique_ptr<GooFile> file(GooFile::open(filepath));
+                if (!file) {
+                    error(errIO, -1, "Failed to open %s", filepath.c_str());
+                    return {};
+                }
+                const Goffset fileSize = file->size();
+                if (fileSize < 0) {
+                    error(errIO, -1, "Failed to get file size for %s", filepath.c_str());
+                    return {};
+                }
+                char *dataPtr = static_cast<char *>(gmalloc(fileSize));
+                const Goffset bytesRead = file->read(dataPtr, fileSize, 0);
+                if (bytesRead != fileSize) {
+                    error(errIO, -1, "Failed to read contents of %s", filepath.c_str());
+                    gfree(dataPtr);
+                    return {};
+                }
+
+                if (isTrueType) {
+                    const Ref fontFile2Ref = xref->addStreamObject(new Dict(xref), dataPtr, fileSize);
+                    fontDescriptor->set("FontFile2", Object(fontFile2Ref));
+                } else {
+                    Dict *fontFileStreamDict = new Dict(xref);
+                    fontFileStreamDict->set("Subtype", Object(objName, "OpenType"));
+                    const Ref fontFile3Ref = xref->addStreamObject(fontFileStreamDict, dataPtr, fileSize);
+                    fontDescriptor->set("FontFile3", Object(fontFile3Ref));
+                }
+            }
+
+            const Ref fontDescriptorRef = xref->addIndirectObject(Object(fontDescriptor.release()));
+            descendantFont->set("FontDescriptor", Object(fontDescriptorRef));
+        }
+
+        static const int basicMultilingualMaxCode = 65535;
+
+        const std::unique_ptr<FoFiTrueType> fft = FoFiTrueType::load(filepath.c_str());
+        if (fft) {
+
+            // Look for the Unicode BMP cmaps, which are 0/3 or 3/1
+            int unicodeBMPCMap = fft->findCmap(0, 3);
+            if (unicodeBMPCMap < 0) {
+                unicodeBMPCMap = fft->findCmap(3, 1);
+            }
+            if (unicodeBMPCMap < 0) {
+                error(errIO, -1, "Font does not have an unicode BMP cmap %s", filepath.c_str());
+                return {};
+            }
+
+            Array *widthsInner = new Array(xref);
+            for (int code = 0; code <= basicMultilingualMaxCode; ++code) {
+                const int glyph = fft->mapCodeToGID(unicodeBMPCMap, code);
+                if (FT_Load_Glyph(face, glyph, FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING)) {
+                    widthsInner->add(Object(0));
+                } else {
+                    widthsInner->add(Object(static_cast<int>(face->glyph->metrics.horiAdvance)));
+                }
+            }
+            Array *widths = new Array(xref);
+            widths->add(Object(0));
+            widths->add(Object(widthsInner));
+            descendantFont->set("W", Object(widths));
+
+            char *dataPtr = static_cast<char *>(gmalloc(2 * (basicMultilingualMaxCode + 1)));
+            int i = 0;
+
+            for (int code = 0; code <= basicMultilingualMaxCode; ++code) {
+                const int glyph = fft->mapCodeToGID(unicodeBMPCMap, code);
+                dataPtr[i++] = (unsigned char)(glyph >> 8);
+                dataPtr[i++] = (unsigned char)(glyph & 0xff);
+            }
+            const Ref cidToGidMapStream = xref->addStreamObject(new Dict(xref), dataPtr, basicMultilingualMaxCode * 2);
+            descendantFont->set("CIDToGIDMap", Object(cidToGidMapStream));
+        }
+
+        descendantFonts->add(Object(descendantFont.release()));
+
+        fontDict.dictSet("DescendantFonts", Object(descendantFonts.release()));
+    }
+
+    const Ref fontDictRef = xref->addIndirectObject(fontDict);
+
+    std::string dictFontName = kOurDictFontNamePrefix;
+    Object *acroForm = doc->getCatalog()->getAcroForm();
+    if (resDict.isDict()) {
+        Ref fontDictObjRef;
+        Object fontDictObj = resDict.getDict()->lookup("Font", &fontDictObjRef);
+        assert(fontDictObj.isDict());
+        dictFontName = fontDictObj.getDict()->findAvailableKey(dictFontName);
+        fontDictObj.dictSet(dictFontName.c_str(), Object(fontDictRef));
+
+        if (fontDictObjRef != Ref::INVALID()) {
+            xref->setModifiedObject(&fontDictObj, fontDictObjRef);
+        } else {
+            Ref resDictRef;
+            acroForm->getDict()->lookup("DR", &resDictRef);
+            if (resDictRef != Ref::INVALID()) {
+                xref->setModifiedObject(&resDict, resDictRef);
+            } else {
+                doc->getCatalog()->setAcroFormModified();
+            }
+        }
+
+        // maybe we can do something to reuse the existing data instead of recreating from scratch?
+        delete defaultResources;
+        defaultResources = new GfxResources(xref, resDict.getDict(), nullptr);
+    } else {
+        Dict *fontsDict = new Dict(xref);
+        fontsDict->set(dictFontName.c_str(), Object(fontDictRef));
+
+        Dict *defaultResourcesDict = new Dict(xref);
+        defaultResourcesDict->set("Font", Object(fontsDict));
+
+        assert(!defaultResources);
+        defaultResources = new GfxResources(xref, defaultResourcesDict, nullptr);
+        resDict = Object(defaultResourcesDict);
+
+        acroForm->dictSet("DR", resDict.copy());
+        doc->getCatalog()->setAcroFormModified();
+    }
+
+    return dictFontName;
+}
+
+std::string Form::getFallbackFontForChar(Unicode uChar, const GfxFont &fontToEmulate)
+{
+    return doGetAddFontToDefaultResources(uChar, fontToEmulate, false /*addIfNotFound*/);
+}
+
+void Form::ensureFontsForAllCharacters(const GooString *unicodeText, const std::string &pdfFontNameToEmulate)
+{
+    std::shared_ptr<GfxFont> f = defaultResources->lookupFont(pdfFontNameToEmulate.c_str());
+    const CharCodeToUnicode *ccToUnicode = f->getToUnicode();
+    if (!ccToUnicode) {
+        return; // will never happen with current code
+    }
+    if (!f->isCIDFont()) {
+        return; // will never happen with current code
+    }
+
+    auto cidFont = static_cast<const GfxCIDFont *>(f.get());
+    // If the text has some characters that are not available in the font, try adding a font for those
+    for (int i = 2; i < unicodeText->getLength(); i += 2) {
+        Unicode uChar = (unsigned char)(unicodeText->getChar(i)) << 8;
+        uChar += (unsigned char)(unicodeText->getChar(i + 1));
+
+        CharCode c;
+        ccToUnicode->mapToCharCode(&uChar, &c, 1);
+
+        if (c < cidFont->getCIDToGIDLen() && c != 0 && c != '\r' && c != '\n') {
+            const int glyph = cidFont->getCIDToGID()[c];
+            if (glyph == 0) {
+                doGetAddFontToDefaultResources(uChar, *f, true /*addIfNotFound*/);
+            }
+        }
+    }
+}
+
+std::string Form::doGetAddFontToDefaultResources(Unicode uChar, const GfxFont &fontToEmulate, bool addIfNotFound)
+{
+    const UCharFontSearchResult res = globalParams->findSystemFontFileForUChar(uChar, fontToEmulate);
+
+    std::string pdfFontName = findFontInDefaultResources(res.family, res.style);
+    if (addIfNotFound && pdfFontName.empty()) {
+        pdfFontName = addFontToDefaultResources(res.filepath, res.faceIndex, res.family, res.style);
+    }
+    return pdfFontName;
+}
+
 void Form::postWidgetsLoad()
 {
     // We create the widget annotations associated to
diff --git a/poppler/Form.h b/poppler/Form.h
index 43a59f95..98421c9a 100644
--- a/poppler/Form.h
+++ b/poppler/Form.h
@@ -32,8 +32,9 @@
 #ifndef FORM_H
 #define FORM_H
 
-#include "Object.h"
 #include "Annot.h"
+#include "CharTypes.h"
+#include "Object.h"
 #include "poppler_private_export.h"
 
 #include <ctime>
@@ -676,6 +677,22 @@ public:
        Page::loadStandaloneFields */
     static FormField *createFieldFromDict(Object &&obj, PDFDoc *docA, const Ref aref, FormField *parent, std::set<int> *usedParents);
 
+    // Finds in the default resources dictionary a font named popplerfontXXX that
+    // has the given fontFamily and fontStyle. This makes us relatively sure that we added that font ourselves
+    std::string findFontInDefaultResources(const std::string &fontFamily, const std::string &fontStyle);
+
+    // Finds in the system a font name matching the given fontFamily and fontStyle
+    // And adds it to the default resources dictionary, font name there will be popplerfontXXX
+    std::string addFontToDefaultResources(const std::string &fontFamily, const std::string &fontStyle);
+
+    // Finds in the default resources dictionary a font named popplerfontXXX that
+    // emulates fontToEmulate and can draw the given char
+    std::string getFallbackFontForChar(Unicode uChar, const GfxFont &fontToEmulate);
+
+    // Makes sure the default resources has fonts to draw all the given chars and as close as possible to the given pdfFontNameToEmulate
+    // If needed adds fonts to the default resources dictionary, font names will be popplerfontXXX
+    void ensureFontsForAllCharacters(const GooString *unicodeText, const std::string &pdfFontNameToEmulate);
+
     bool getNeedAppearances() const { return needAppearances; }
     int getNumFields() const { return numFields; }
     FormField *getRootField(int i) const { return rootFields[i]; }
@@ -695,9 +712,16 @@ public:
     void reset(const std::vector<std::string> &fields, bool excludeFields);
 
 private:
+    // Finds in the system a font name matching the given fontFamily and fontStyle
+    // And adds it to the default resources dictionary, font name there will be popplerfontXXX
+    std::string addFontToDefaultResources(const std::string &filepath, int faceIndex, const std::string &fontFamily, const std::string &fontStyle);
+
+    std::string doGetAddFontToDefaultResources(Unicode uChar, const GfxFont &fontToEmulate, bool addIfNotFound);
+
     FormField **rootFields;
     int numFields;
     int size;
+    PDFDoc *const doc;
     bool needAppearances;
     GfxResources *defaultResources;
     Object resDict;
diff --git a/poppler/GlobalParams.cc b/poppler/GlobalParams.cc
index a95d940e..570b592b 100644
--- a/poppler/GlobalParams.cc
+++ b/poppler/GlobalParams.cc
@@ -90,6 +90,9 @@
 #include "UnicodeMapTables.h"
 #include "UnicodeMapFuncs.h"
 
+#include "fofi/FoFiTrueType.h"
+#include "fofi/FoFiIdentifier.h"
+
 //------------------------------------------------------------------------
 
 #define cidToUnicodeCacheSize 4
@@ -251,6 +254,8 @@ public:
     SysFontList &operator=(const SysFontList &) = delete;
     const SysFontInfo *find(const std::string &name, bool isFixedWidth, bool exact);
 
+    const std::vector<SysFontInfo *> &getFonts() const { return fonts; }
+
 #ifdef _WIN32
     void scanWindowsFonts(GooString *winFontDir);
 #endif
@@ -888,6 +893,39 @@ GooString *GlobalParams::findFontFile(const std::string &fontName)
     return path;
 }
 
+static bool supportedFontForEmbedding(Unicode uChar, const char *filepath, int faceIndex)
+{
+    if (!GooString::endsWith(filepath, ".ttf") && !GooString::endsWith(filepath, ".ttc") && !GooString::endsWith(filepath, ".otf")) {
+        // for now we only support ttf, ttc, otf fonts
+        return false;
+    }
+
+    const FoFiIdentifierType fontFoFiType = FoFiIdentifier::identifyFile(filepath);
+    if (fontFoFiType != fofiIdTrueType && fontFoFiType != fofiIdTrueTypeCollection && fontFoFiType != fofiIdOpenTypeCFF8Bit && fontFoFiType != fofiIdOpenTypeCFFCID) {
+        // for now we only support ttf, ttc, otf fonts
+        return false;
+    }
+
+    const std::unique_ptr<FoFiTrueType> fft = FoFiTrueType::load(filepath, faceIndex);
+    if (!fft) {
+        error(errIO, -1, "Form::addFontToDefaultResources. Failed to FoFiTrueType::load %s", filepath);
+        return false;
+    }
+
+    // Look for the Unicode BMP cmaps, which are 0/3 or 3/1
+    int unicodeBMPCMap = fft->findCmap(0, 3);
+    if (unicodeBMPCMap < 0) {
+        unicodeBMPCMap = fft->findCmap(3, 1);
+    }
+    if (unicodeBMPCMap < 0) {
+        // for now we only support files with unicode bmp cmaps
+        return false;
+    }
+
+    const int glyph = fft->mapCodeToGID(unicodeBMPCMap, uChar);
+    return glyph > 0;
+}
+
 /* if you can't or don't want to use Fontconfig, you need to implement
    this function for your platform. For Windows, it's in GlobalParamsWin.cc
 */
@@ -1067,6 +1105,71 @@ fin:
     return path;
 }
 
+FamilyStyleFontSearchResult GlobalParams::findSystemFontFileForFamilyAndStyle(const std::string &fontFamily, const std::string &fontStyle)
+{
+    FcChar8 *fcFilePath = nullptr;
+    int faceIndex = 0;
+    FcPattern *p = FcPatternBuild(nullptr, FC_FAMILY, FcTypeString, fontFamily.c_str(), FC_STYLE, FcTypeString, fontStyle.c_str(), nullptr);
+    FcConfigSubstitute(nullptr, p, FcMatchPattern);
+    FcDefaultSubstitute(p);
+    if (p) {
+        FcResult res;
+        FcFontSet *set = FcFontSort(nullptr, p, FcFalse, nullptr, &res);
+        if (set) {
+            if (res == FcResultMatch && set->nfont > 0) {
+                FcPatternGetString(set->fonts[0], FC_FILE, 0, &fcFilePath);
+                FcPatternGetInteger(set->fonts[0], FC_INDEX, 0, &faceIndex);
+            }
+            FcFontSetDestroy(set);
+        }
+        FcPatternDestroy(p);
+    }
+
+    if (!fcFilePath) {
+        error(errIO, -1, "Couldn't find font file for %s %s", fontFamily.c_str(), fontStyle.c_str());
+        return {};
+    }
+
+    return FamilyStyleFontSearchResult(reinterpret_cast<char *>(fcFilePath), faceIndex);
+}
+
+UCharFontSearchResult GlobalParams::findSystemFontFileForUChar(Unicode uChar, const GfxFont &fontToEmulate)
+{
+    FcPattern *pattern = buildFcPattern(&fontToEmulate, nullptr);
+
+    FcConfigSubstitute(nullptr, pattern, FcMatchPattern);
+    FcDefaultSubstitute(pattern);
+
+    FcResult result = FcResultMatch;
+    FcFontSet *fontSet = FcFontSort(nullptr, pattern, FcFalse, nullptr, &result);
+    FcPatternDestroy(pattern);
+
+    if (fontSet) {
+        const std::unique_ptr<FcFontSet, void (*)(FcFontSet *)> fontSetDeleter(fontSet, [](FcFontSet *fSet) { FcFontSetDestroy(fSet); });
+        for (int i = 0; i < fontSet->nfont; i++) {
+            FcChar8 *fcFilePath = nullptr;
+            int faceIndex = 0;
+            FcChar8 *fcFamily = nullptr;
+            FcChar8 *fcStyle = nullptr;
+            FcPatternGetString(fontSet->fonts[i], FC_FILE, 0, &fcFilePath);
+            FcPatternGetInteger(fontSet->fonts[i], FC_INDEX, 0, &faceIndex);
+            FcPatternGetString(fontSet->fonts[i], FC_FAMILY, 0, &fcFamily);
+            FcPatternGetString(fontSet->fonts[i], FC_STYLE, 0, &fcStyle);
+            if (!fcFilePath || !fcFamily || !fcStyle) {
+                continue;
+            }
+
+            const char *filepath = reinterpret_cast<char *>(fcFilePath);
+
+            if (supportedFontForEmbedding(uChar, filepath, faceIndex)) {
+                return UCharFontSearchResult(filepath, faceIndex, reinterpret_cast<char *>(fcFamily), reinterpret_cast<char *>(fcStyle));
+            }
+        }
+    }
+
+    return {};
+}
+
 #elif defined(WITH_FONTCONFIGURATION_WIN32)
 #    include "GlobalParamsWin.cc"
 
@@ -1074,7 +1177,21 @@ GooString *GlobalParams::findBase14FontFile(const GooString *base14Name, const G
 {
     return findFontFile(base14Name->toStr());
 }
+
 #else
+
+FamilyStyleFontSearchResult GlobalParams::findSystemFontFileForFamilyAndStyle(const std::string &fontFamily, const std::string &fontStyle)
+{
+    error(errUnimplemented, -1, "GlobalParams::findSystemFontFileForFamilyAndStyle not implemented for this platform");
+    return {};
+}
+
+UCharFontSearchResult GlobalParams::findSystemFontFileForUChar(Unicode uChar, const GfxFont &fontToEmulate)
+{
+    error(errUnimplemented, -1, "GlobalParams::findSystemFontFileForUChar not implemented for this platform");
+    return {};
+}
+
 GooString *GlobalParams::findBase14FontFile(const GooString *base14Name, const GfxFont *font)
 {
     return findFontFile(base14Name->toStr());
diff --git a/poppler/GlobalParams.h b/poppler/GlobalParams.h
index 967268ca..af3e53dc 100644
--- a/poppler/GlobalParams.h
+++ b/poppler/GlobalParams.h
@@ -79,6 +79,32 @@ enum SysFontType
 
 //------------------------------------------------------------------------
 
+struct FamilyStyleFontSearchResult
+{
+    FamilyStyleFontSearchResult() = default;
+
+    FamilyStyleFontSearchResult(const std::string &filepathA, int faceIndexA) : filepath(filepathA), faceIndex(faceIndexA) { }
+
+    const std::string filepath;
+    const int faceIndex = 0;
+};
+
+//------------------------------------------------------------------------
+
+struct UCharFontSearchResult
+{
+    UCharFontSearchResult() = default;
+
+    UCharFontSearchResult(const std::string &filepathA, int faceIndexA, const std::string &familyA, const std::string &styleA) : filepath(filepathA), faceIndex(faceIndexA), family(familyA), style(styleA) { }
+
+    const std::string filepath;
+    const int faceIndex = 0;
+    const std::string family;
+    const std::string style;
+};
+
+//------------------------------------------------------------------------
+
 class POPPLER_PRIVATE_EXPORT GlobalParams
 {
 public:
@@ -111,6 +137,8 @@ public:
     GooString *findFontFile(const std::string &fontName);
     GooString *findBase14FontFile(const GooString *base14Name, const GfxFont *font);
     GooString *findSystemFontFile(const GfxFont *font, SysFontType *type, int *fontNum, GooString *substituteFontName = nullptr, const GooString *base14Name = nullptr);
+    FamilyStyleFontSearchResult findSystemFontFileForFamilyAndStyle(const std::string &fontFamily, const std::string &fontStyle);
+    UCharFontSearchResult findSystemFontFileForUChar(Unicode uChar, const GfxFont &fontToEmulate);
     std::string getTextEncodingName() const;
     bool getPrintCommands();
     bool getProfileCommands();
diff --git a/poppler/GlobalParamsWin.cc b/poppler/GlobalParamsWin.cc
index bd453862..63ee229b 100644
--- a/poppler/GlobalParamsWin.cc
+++ b/poppler/GlobalParamsWin.cc
@@ -535,3 +535,50 @@ GooString *GlobalParams::findSystemFontFile(const GfxFont *font, SysFontType *ty
 
     return path;
 }
+
+FamilyStyleFontSearchResult GlobalParams::findSystemFontFileForFamilyAndStyle(const std::string &fontFamily, const std::string &fontStyle)
+{
+    const std::scoped_lock locker(mutex);
+    setupBaseFonts(POPPLER_FONTSDIR);
+
+    const std::string familyAndStyle = fontFamily + " " + fontStyle;
+
+    const SysFontInfo *fi = sysFonts->find(familyAndStyle, false, false);
+    if (fi) {
+        return FamilyStyleFontSearchResult(fi->path->toStr(), fi->fontNum);
+    }
+
+    return {};
+}
+
+UCharFontSearchResult GlobalParams::findSystemFontFileForUChar(Unicode uChar, const GfxFont &fontToEmulate)
+{
+    const std::scoped_lock locker(mutex);
+    setupBaseFonts(POPPLER_FONTSDIR);
+
+    const std::vector<SysFontInfo *> &fonts = sysFonts->getFonts();
+    for (SysFontInfo *f : fonts) {
+        // This is not super great given that it ignores fontToEmulate, but will do for now
+        if (supportedFontForEmbedding(uChar, f->path->c_str(), f->fontNum)) {
+            std::string style;
+            if (f->italic) {
+                style = "Italic";
+            }
+            if (f->oblique) {
+                if (!style.empty()) {
+                    style += " ";
+                }
+                style += "Oblique";
+            }
+            if (f->bold) {
+                if (!style.empty()) {
+                    style += " ";
+                }
+                style += "Bold";
+            }
+            return UCharFontSearchResult(f->path->toStr(), f->fontNum, f->name->toStr(), style);
+        }
+    }
+
+    return {};
+}
diff --git a/poppler/PDFDoc.cc b/poppler/PDFDoc.cc
index dede5d8c..2b0139a4 100644
--- a/poppler/PDFDoc.cc
+++ b/poppler/PDFDoc.cc
@@ -2142,6 +2142,8 @@ bool PDFDoc::sign(const char *saveFilename, const char *certNickname, const char
 
     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));
 
     std::unique_ptr<::FormFieldSignature> field = std::make_unique<::FormFieldSignature>(this, Object(annotObj.getDict()), ref, nullptr, nullptr);
     field->setCustomAppearanceContent(signatureText);
diff --git a/qt5/src/poppler-annotation.cc b/qt5/src/poppler-annotation.cc
index cdf25da1..851f78a8 100644
--- a/qt5/src/poppler-annotation.cc
+++ b/qt5/src/poppler-annotation.cc
@@ -904,6 +904,25 @@ void AnnotationPrivate::removeAnnotationFromPage(::Page *pdfPage, const Annotati
     delete ann;
 }
 
+class TextAnnotationPrivate : public AnnotationPrivate
+{
+public:
+    TextAnnotationPrivate();
+    Annotation *makeAlias() override;
+    Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override;
+    void setDefaultAppearanceToNative();
+    std::unique_ptr<DefaultAppearance> getDefaultAppearanceFromNative() const;
+
+    // data fields
+    TextAnnotation::TextType textType;
+    QString textIcon;
+    std::optional<QFont> textFont;
+    QColor textColor = Qt::black;
+    int inplaceAlign; // 0:left, 1:center, 2:right
+    QVector<QPointF> inplaceCallout;
+    TextAnnotation::InplaceIntent inplaceIntent;
+};
+
 class Annotation::Style::Private : public QSharedData
 {
 public:
@@ -1440,6 +1459,11 @@ void Annotation::setContents(const QString &contents)
     }
 
     d->pdfAnnot->setContents(std::unique_ptr<GooString>(QStringToUnicodeGooString(contents)));
+
+    TextAnnotationPrivate *textAnnotD = dynamic_cast<TextAnnotationPrivate *>(d);
+    if (textAnnotD) {
+        textAnnotD->setDefaultAppearanceToNative();
+    }
 }
 
 QString Annotation::uniqueName() const
@@ -1644,7 +1668,10 @@ void Annotation::setBoundary(const QRectF &boundary)
         return;
     }
 
-    PDFRectangle rect = d->boundaryToPdfRectangle(boundary, flags());
+    const PDFRectangle rect = d->boundaryToPdfRectangle(boundary, flags());
+    if (rect == d->pdfAnnot->getRect()) {
+        return;
+    }
     d->pdfAnnot->setRect(&rect);
 }
 
@@ -1905,24 +1932,6 @@ void Annotation::setAnnotationAppearance(const AnnotationAppearance &annotationA
 // END Annotation implementation
 
 /** TextAnnotation [Annotation] */
-class TextAnnotationPrivate : public AnnotationPrivate
-{
-public:
-    TextAnnotationPrivate();
-    Annotation *makeAlias() override;
-    Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override;
-    void setDefaultAppearanceToNative();
-    std::unique_ptr<DefaultAppearance> getDefaultAppearanceFromNative() const;
-
-    // data fields
-    TextAnnotation::TextType textType;
-    QString textIcon;
-    std::optional<QFont> textFont;
-    QColor textColor = Qt::black;
-    int inplaceAlign; // 0:left, 1:center, 2:right
-    QVector<QPointF> inplaceCallout;
-    TextAnnotation::InplaceIntent inplaceIntent;
-};
 
 TextAnnotationPrivate::TextAnnotationPrivate() : AnnotationPrivate(), textType(TextAnnotation::Linked), textIcon(QStringLiteral("Note")), inplaceAlign(0), inplaceIntent(TextAnnotation::Unknown) { }
 
@@ -1949,8 +1958,7 @@ Annot *TextAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *
         if (pointSize < 0) {
             qWarning() << "TextAnnotationPrivate::createNativeAnnot: font pointSize < 0";
         }
-        DefaultAppearance da { { objName, "Invalid_font" }, pointSize, convertQColor(textColor) };
-        pdfAnnot = new AnnotFreeText { destPage->getDoc(), &rect, da };
+        pdfAnnot = new AnnotFreeText { destPage->getDoc(), &rect };
     }
 
     // Set properties
@@ -1964,6 +1972,8 @@ Annot *TextAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *
 
     inplaceCallout.clear(); // Free up memory
 
+    setDefaultAppearanceToNative();
+
     return pdfAnnot;
 }
 
@@ -1975,7 +1985,21 @@ void TextAnnotationPrivate::setDefaultAppearanceToNative()
         if (pointSize < 0) {
             qWarning() << "TextAnnotationPrivate::createNativeAnnot: font pointSize < 0";
         }
-        DefaultAppearance da { { objName, "Invalid_font" }, pointSize, convertQColor(textColor) };
+        std::string fontName = "Invalid_font";
+        if (textFont) {
+            Form *form = pdfPage->getDoc()->getCatalog()->getCreateForm();
+            fontName = form->findFontInDefaultResources(textFont->family().toStdString(), textFont->styleName().toStdString());
+            if (fontName.empty()) {
+                fontName = form->addFontToDefaultResources(textFont->family().toStdString(), textFont->styleName().toStdString());
+            }
+
+            if (!fontName.empty()) {
+                form->ensureFontsForAllCharacters(pdfAnnot->getContents(), fontName);
+            } else {
+                fontName = "Invalid_font";
+            }
+        }
+        DefaultAppearance da { { objName, fontName.c_str() }, pointSize, convertQColor(textColor) };
         ftextann->setDefaultAppearance(da);
     }
 }
diff --git a/qt6/src/poppler-annotation.cc b/qt6/src/poppler-annotation.cc
index 5770be6f..4472f62f 100644
--- a/qt6/src/poppler-annotation.cc
+++ b/qt6/src/poppler-annotation.cc
@@ -824,6 +824,25 @@ void AnnotationPrivate::removeAnnotationFromPage(::Page *pdfPage, const Annotati
     delete ann;
 }
 
+class TextAnnotationPrivate : public AnnotationPrivate
+{
+public:
+    TextAnnotationPrivate();
+    Annotation *makeAlias() override;
+    Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override;
+    void setDefaultAppearanceToNative();
+    std::unique_ptr<DefaultAppearance> getDefaultAppearanceFromNative() const;
+
+    // data fields
+    TextAnnotation::TextType textType;
+    QString textIcon;
+    std::optional<QFont> textFont;
+    QColor textColor = Qt::black;
+    TextAnnotation::InplaceAlignPosition inplaceAlign;
+    QVector<QPointF> inplaceCallout;
+    TextAnnotation::InplaceIntent inplaceIntent;
+};
+
 class Annotation::Style::Private : public QSharedData
 {
 public:
@@ -1078,6 +1097,11 @@ void Annotation::setContents(const QString &contents)
     }
 
     d->pdfAnnot->setContents(std::unique_ptr<GooString>(QStringToUnicodeGooString(contents)));
+
+    TextAnnotationPrivate *textAnnotD = dynamic_cast<TextAnnotationPrivate *>(d);
+    if (textAnnotD) {
+        textAnnotD->setDefaultAppearanceToNative();
+    }
 }
 
 QString Annotation::uniqueName() const
@@ -1283,7 +1307,10 @@ void Annotation::setBoundary(const QRectF &boundary)
         return;
     }
 
-    PDFRectangle rect = d->boundaryToPdfRectangle(boundary, flags());
+    const PDFRectangle rect = d->boundaryToPdfRectangle(boundary, flags());
+    if (rect == d->pdfAnnot->getRect()) {
+        return;
+    }
     d->pdfAnnot->setRect(&rect);
 }
 
@@ -1544,25 +1571,6 @@ void Annotation::setAnnotationAppearance(const AnnotationAppearance &annotationA
 // END Annotation implementation
 
 /** TextAnnotation [Annotation] */
-class TextAnnotationPrivate : public AnnotationPrivate
-{
-public:
-    TextAnnotationPrivate();
-    Annotation *makeAlias() override;
-    Annot *createNativeAnnot(::Page *destPage, DocumentData *doc) override;
-    void setDefaultAppearanceToNative();
-    std::unique_ptr<DefaultAppearance> getDefaultAppearanceFromNative() const;
-
-    // data fields
-    TextAnnotation::TextType textType;
-    QString textIcon;
-    std::optional<QFont> textFont;
-    QColor textColor = Qt::black;
-    TextAnnotation::InplaceAlignPosition inplaceAlign;
-    QVector<QPointF> inplaceCallout;
-    TextAnnotation::InplaceIntent inplaceIntent;
-};
-
 TextAnnotationPrivate::TextAnnotationPrivate() : AnnotationPrivate(), textType(TextAnnotation::Linked), textIcon(QStringLiteral("Note")), inplaceAlign(TextAnnotation::InplaceAlignLeft), inplaceIntent(TextAnnotation::Unknown) { }
 
 Annotation *TextAnnotationPrivate::makeAlias()
@@ -1588,8 +1596,7 @@ Annot *TextAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *
         if (pointSize < 0) {
             qWarning() << "TextAnnotationPrivate::createNativeAnnot: font pointSize < 0";
         }
-        DefaultAppearance da { { objName, "Invalid_font" }, pointSize, convertQColor(textColor) };
-        pdfAnnot = new AnnotFreeText { destPage->getDoc(), &rect, da };
+        pdfAnnot = new AnnotFreeText { destPage->getDoc(), &rect };
     }
 
     // Set properties
@@ -1603,6 +1610,8 @@ Annot *TextAnnotationPrivate::createNativeAnnot(::Page *destPage, DocumentData *
 
     inplaceCallout.clear(); // Free up memory
 
+    setDefaultAppearanceToNative();
+
     return pdfAnnot;
 }
 
@@ -1614,7 +1623,21 @@ void TextAnnotationPrivate::setDefaultAppearanceToNative()
         if (pointSize < 0) {
             qWarning() << "TextAnnotationPrivate::createNativeAnnot: font pointSize < 0";
         }
-        DefaultAppearance da { { objName, "Invalid_font" }, pointSize, convertQColor(textColor) };
+        std::string fontName = "Invalid_font";
+        if (textFont) {
+            Form *form = pdfPage->getDoc()->getCatalog()->getCreateForm();
+            fontName = form->findFontInDefaultResources(textFont->family().toStdString(), textFont->styleName().toStdString());
+            if (fontName.empty()) {
+                fontName = form->addFontToDefaultResources(textFont->family().toStdString(), textFont->styleName().toStdString());
+            }
+
+            if (!fontName.empty()) {
+                form->ensureFontsForAllCharacters(pdfAnnot->getContents(), fontName);
+            } else {
+                fontName = "Invalid_font";
+            }
+        }
+        DefaultAppearance da { { objName, fontName.c_str() }, pointSize, convertQColor(textColor) };
         ftextann->setDefaultAppearance(da);
     }
 }


More information about the poppler mailing list