[poppler] poppler/Annot.cc poppler/Annot.h qt5/src qt6/src

GitLab Mirror gitlab-mirror at kemper.freedesktop.org
Tue Jul 6 22:53:25 UTC 2021


 poppler/Annot.cc                 |  126 ++++++++++++++++++---------------------
 poppler/Annot.h                  |   17 ++---
 qt5/src/poppler-pdf-converter.cc |    4 -
 qt6/src/poppler-pdf-converter.cc |    4 -
 4 files changed, 71 insertions(+), 80 deletions(-)

New commits:
commit e674ca6453f3f20c4bf0cb463222a97c99221dd6
Author: Zachary Travis <ztravis at everlaw.com>
Date:   Tue Jul 6 22:53:22 2021 +0000

    Create fallback fonts as needed.
    
    If a PDF form field value uses a font that is not in the resources dictionary, a warning is logged and the field value is ignored/not displayed. It's unclear whether this behavior is strictly valid based on the PDF spec (since typically font references, even to base fonts, require a corresponding font dictionary) but Acrobat seems to display the content anyway.

diff --git a/poppler/Annot.cc b/poppler/Annot.cc
index 0e36b209..66db4a32 100644
--- a/poppler/Annot.cc
+++ b/poppler/Annot.cc
@@ -245,6 +245,21 @@ static const char *getFormAdditionalActionKey(Annot::FormAdditionalActionsType t
     return (type == Annot::actionFieldModified ? "K" : type == Annot::actionFormatField ? "F" : type == Annot::actionValidateField ? "V" : type == Annot::actionCalculateField ? "C" : nullptr);
 }
 
+static const char *determineFallbackFont(GooString *tok, const char *defaultFallback)
+{
+    // TODO: adjust these based on other example PDFs.
+    if (!tok->cmp("/ZaDb")) {
+        return "ZapfDingbats";
+    } else if (!tok->cmp("/Cour")) {
+        return "Courier";
+    } else if (!tok->cmp("/TiRo")) {
+        return "TimesNewRoman";
+    } else if (!tok->cmp("/Helvetica-Bold")) {
+        return "Helvetica-Bold";
+    }
+    return defaultFallback;
+}
+
 //------------------------------------------------------------------------
 // AnnotBorderEffect
 //------------------------------------------------------------------------
@@ -2852,7 +2867,9 @@ static GfxFont *createAnnotDrawFont(XRef *xref, Dict *fontParentDict, const char
     Dict *fontDict = new Dict(xref);
     fontDict->add("BaseFont", Object(objName, fontname));
     fontDict->add("Subtype", Object(objName, "Type1"));
-    fontDict->add("Encoding", Object(objName, "WinAnsiEncoding"));
+    if (strcmp(fontname, "ZapfDingbats") && strcmp(fontname, "Symbol")) {
+        fontDict->add("Encoding", Object(objName, "WinAnsiEncoding"));
+    }
 
     Object fontsDictObj = fontParentDict->lookup("Font");
     if (!fontsDictObj.isDict()) {
@@ -4110,7 +4127,7 @@ void AnnotAppearanceBuilder::writeString(const GooString &str)
 
 // Draw the variable text or caption for a field.
 bool AnnotAppearanceBuilder::drawText(const GooString *text, const GooString *da, const GfxResources *resources, const AnnotBorder *border, const AnnotAppearanceCharacs *appearCharacs, const PDFRectangle *rect, bool multiline, int comb,
-                                      int quadding, bool txField, bool forceZapfDingbats, XRef *xref, bool *addedDingbatsResource, bool password)
+                                      int quadding, bool txField, bool forceZapfDingbats, XRef *xref, bool password, Dict *resourcesDict, const char *defaultFallback)
 {
     std::vector<GooString *> *daToks;
     GooString *tok;
@@ -4154,36 +4171,24 @@ bool AnnotAppearanceBuilder::drawText(const GooString *text, const GooString *da
         daToks = nullptr;
     }
 
-    // force ZapfDingbats
-    if (forceZapfDingbats) {
-        assert(xref != nullptr);
-        assert(addedDingbatsResource != nullptr);
-        *addedDingbatsResource = false;
-
-        if (tfPos >= 0) {
-            tok = (*daToks)[tfPos];
-            if (tok->cmp("/ZaDb")) {
-                tok->clear();
-                tok->append("/ZaDb");
-            }
-        }
-    }
     // get the font and font size
     font = nullptr;
     fontSize = 0;
     if (tfPos >= 0) {
         tok = (*daToks)[tfPos];
+        if (forceZapfDingbats) {
+            assert(xref != nullptr);
+            if (tok->cmp("/ZaDb")) {
+                tok->clear();
+                tok->append("/ZaDb");
+            }
+        }
         if (tok->getLength() >= 1 && tok->getChar(0) == '/') {
             if (!resources || !(font = resources->lookupFont(tok->c_str() + 1))) {
-                if (forceZapfDingbats) {
-                    // We are forcing ZaDb but the font does not exist
-                    // so create a fake one
-                    Ref r = Ref::INVALID(); // dummy Ref, it's not used at all in this codepath
-                    Dict *d = new Dict(xref);
-                    fontToFree = new Gfx8BitFont(xref, "ZaDb", r, new GooString("ZapfDingbats"), fontType1, r, d);
-                    delete d;
+                if (xref != nullptr && resourcesDict != nullptr) {
+                    const char *fallback = determineFallbackFont(tok, defaultFallback);
+                    fontToFree = createAnnotDrawFont(xref, resourcesDict, tok->c_str() + 1, fallback);
                     font = fontToFree;
-                    *addedDingbatsResource = true;
                 } else {
                     error(errSyntaxError, -1, "Unknown font in field's DA string");
                 }
@@ -4522,7 +4527,7 @@ bool AnnotAppearanceBuilder::drawText(const GooString *text, const GooString *da
 }
 
 // Draw the variable text or caption for a field.
-bool AnnotAppearanceBuilder::drawListBox(const FormFieldChoice *fieldChoice, const AnnotBorder *border, const PDFRectangle *rect, const GooString *da, const GfxResources *resources, int quadding)
+bool AnnotAppearanceBuilder::drawListBox(const FormFieldChoice *fieldChoice, const AnnotBorder *border, const PDFRectangle *rect, const GooString *da, const GfxResources *resources, int quadding, XRef *xref, Dict *resourcesDict)
 {
     std::vector<GooString *> *daToks;
     GooString *tok;
@@ -4530,6 +4535,7 @@ bool AnnotAppearanceBuilder::drawListBox(const FormFieldChoice *fieldChoice, con
     const GfxFont *font;
     double fontSize, borderWidth, x, y, w, wMax;
     int tfPos, tmPos, i, j;
+    GfxFont *fontToFree = nullptr;
 
     //~ if there is no MK entry, this should use the existing content stream,
     //~ and only replace the marked content portion of it
@@ -4569,7 +4575,13 @@ bool AnnotAppearanceBuilder::drawListBox(const FormFieldChoice *fieldChoice, con
         tok = (*daToks)[tfPos];
         if (tok->getLength() >= 1 && tok->getChar(0) == '/') {
             if (!resources || !(font = resources->lookupFont(tok->c_str() + 1))) {
-                error(errSyntaxError, -1, "Unknown font in field's DA string");
+                if (xref != nullptr && resourcesDict != nullptr) {
+                    const char *fallback = determineFallbackFont(tok, "Helvetica");
+                    fontToFree = createAnnotDrawFont(xref, resourcesDict, tok->c_str() + 1, fallback);
+                    font = fontToFree;
+                } else {
+                    error(errSyntaxError, -1, "Unknown font in field's DA string");
+                }
             }
         } else {
             error(errSyntaxError, -1, "Invalid font name in 'Tf' operator in field's DA string");
@@ -4701,6 +4713,9 @@ bool AnnotAppearanceBuilder::drawListBox(const FormFieldChoice *fieldChoice, con
         }
         delete daToks;
     }
+    if (fontToFree) {
+        fontToFree->decRefCnt();
+    }
 
     return true;
 }
@@ -4808,17 +4823,17 @@ void AnnotAppearanceBuilder::drawFieldBorder(const FormField *field, const Annot
 }
 
 bool AnnotAppearanceBuilder::drawFormField(const FormField *field, const Form *form, const GfxResources *resources, const GooString *da, const AnnotBorder *border, const AnnotAppearanceCharacs *appearCharacs, const PDFRectangle *rect,
-                                           const GooString *appearState, XRef *xref, bool *addedDingbatsResource, Dict *resourcesDict)
+                                           const GooString *appearState, XRef *xref, Dict *resourcesDict)
 {
     // draw the field contents
     switch (field->getType()) {
     case formButton:
-        return drawFormFieldButton(static_cast<const FormFieldButton *>(field), resources, da, border, appearCharacs, rect, appearState, xref, addedDingbatsResource);
+        return drawFormFieldButton(static_cast<const FormFieldButton *>(field), resources, da, border, appearCharacs, rect, appearState, xref, resourcesDict);
         break;
     case formText:
-        return drawFormFieldText(static_cast<const FormFieldText *>(field), form, resources, da, border, appearCharacs, rect);
+        return drawFormFieldText(static_cast<const FormFieldText *>(field), form, resources, da, border, appearCharacs, rect, xref, resourcesDict);
     case formChoice:
-        return drawFormFieldChoice(static_cast<const FormFieldChoice *>(field), form, resources, da, border, appearCharacs, rect);
+        return drawFormFieldChoice(static_cast<const FormFieldChoice *>(field), form, resources, da, border, appearCharacs, rect, xref, resourcesDict);
         break;
     case formSignature:
         return drawSignatureFieldText(static_cast<const FormFieldSignature *>(field), form, resources, da, border, appearCharacs, rect, xref, resourcesDict);
@@ -4832,7 +4847,7 @@ bool AnnotAppearanceBuilder::drawFormField(const FormField *field, const Form *f
 }
 
 bool AnnotAppearanceBuilder::drawFormFieldButton(const FormFieldButton *field, const GfxResources *resources, const GooString *da, const AnnotBorder *border, const AnnotAppearanceCharacs *appearCharacs, const PDFRectangle *rect,
-                                                 const GooString *appearState, XRef *xref, bool *addedDingbatsResource)
+                                                 const GooString *appearState, XRef *xref, Dict *resourcesDict)
 {
     const GooString *caption = nullptr;
     if (appearCharacs)
@@ -4843,7 +4858,7 @@ bool AnnotAppearanceBuilder::drawFormFieldButton(const FormFieldButton *field, c
         //~ Acrobat doesn't draw a caption if there is no AP dict (?)
         if (appearState && appearState->cmp("Off") != 0 && field->getState(appearState->c_str())) {
             if (caption) {
-                return drawText(caption, da, resources, border, appearCharacs, rect, false, 0, fieldQuadCenter, false, true, xref, addedDingbatsResource, false);
+                return drawText(caption, da, resources, border, appearCharacs, rect, false, 0, fieldQuadCenter, false, true, xref, false, resourcesDict, "ZapfDingbats");
             } else if (appearCharacs) {
                 const AnnotColor *aColor = appearCharacs->getBorderColor();
                 if (aColor) {
@@ -4858,15 +4873,15 @@ bool AnnotAppearanceBuilder::drawFormFieldButton(const FormFieldButton *field, c
     } break;
     case formButtonPush:
         if (caption)
-            return drawText(caption, da, resources, border, appearCharacs, rect, false, 0, fieldQuadCenter, false, false, xref, addedDingbatsResource, false);
+            return drawText(caption, da, resources, border, appearCharacs, rect, false, 0, fieldQuadCenter, false, false, xref, false, resourcesDict);
         break;
     case formButtonCheck:
         if (appearState && appearState->cmp("Off") != 0) {
             if (!caption) {
                 GooString checkMark("3");
-                return drawText(&checkMark, da, resources, border, appearCharacs, rect, false, 0, fieldQuadCenter, false, true, xref, addedDingbatsResource, false);
+                return drawText(&checkMark, da, resources, border, appearCharacs, rect, false, 0, fieldQuadCenter, false, true, xref, false, resourcesDict, "ZapfDingbats");
             } else {
-                return drawText(caption, da, resources, border, appearCharacs, rect, false, 0, fieldQuadCenter, false, true, xref, addedDingbatsResource, false);
+                return drawText(caption, da, resources, border, appearCharacs, rect, false, 0, fieldQuadCenter, false, true, xref, false, resourcesDict, "ZapfDingbats");
             }
         }
         break;
@@ -4876,7 +4891,7 @@ bool AnnotAppearanceBuilder::drawFormFieldButton(const FormFieldButton *field, c
 }
 
 bool AnnotAppearanceBuilder::drawFormFieldText(const FormFieldText *fieldText, const Form *form, const GfxResources *resources, const GooString *da, const AnnotBorder *border, const AnnotAppearanceCharacs *appearCharacs,
-                                               const PDFRectangle *rect)
+                                               const PDFRectangle *rect, XRef *xref, Dict *resourcesDict)
 {
     VariableTextQuadding quadding;
     const GooString *contents;
@@ -4895,7 +4910,7 @@ bool AnnotAppearanceBuilder::drawFormFieldText(const FormFieldText *fieldText, c
         if (fieldText->isComb())
             comb = fieldText->getMaxLen();
 
-        return drawText(contents, da, resources, border, appearCharacs, rect, fieldText->isMultiline(), comb, quadding, true, false, nullptr, nullptr, fieldText->isPassword());
+        return drawText(contents, da, resources, border, appearCharacs, rect, fieldText->isMultiline(), comb, quadding, true, false, xref, fieldText->isPassword(), resourcesDict);
     }
 
     return true;
@@ -4995,7 +5010,7 @@ void AnnotAppearanceBuilder::drawSignatureFieldText(const GooString &text, const
 }
 
 bool AnnotAppearanceBuilder::drawFormFieldChoice(const FormFieldChoice *fieldChoice, const Form *form, const GfxResources *resources, const GooString *da, const AnnotBorder *border, const AnnotAppearanceCharacs *appearCharacs,
-                                                 const PDFRectangle *rect)
+                                                 const PDFRectangle *rect, XRef *xref, Dict *resourcesDict)
 {
     const GooString *selected;
     VariableTextQuadding quadding;
@@ -5011,18 +5026,18 @@ bool AnnotAppearanceBuilder::drawFormFieldChoice(const FormFieldChoice *fieldCho
     if (fieldChoice->isCombo()) {
         selected = fieldChoice->getSelectedChoice();
         if (selected) {
-            return drawText(selected, da, resources, border, appearCharacs, rect, false, 0, quadding, true, false, nullptr, nullptr, false);
+            return drawText(selected, da, resources, border, appearCharacs, rect, false, 0, quadding, true, false, xref, false, resourcesDict);
             //~ Acrobat draws a popup icon on the right side
         }
         // list box
     } else {
-        return drawListBox(fieldChoice, border, rect, da, resources, quadding);
+        return drawListBox(fieldChoice, border, rect, da, resources, quadding, xref, resourcesDict);
     }
 
     return true;
 }
 
-void AnnotWidget::generateFieldAppearance(bool *addedDingbatsResource)
+void AnnotWidget::generateFieldAppearance()
 {
     const GooString *da;
 
@@ -5055,10 +5070,10 @@ void AnnotWidget::generateFieldAppearance(bool *addedDingbatsResource)
         resourcesDictObj = Object(new Dict(doc->getXRef()));
     }
 
-    const bool success = appearBuilder.drawFormField(field, form, resources, da, border.get(), appearCharacs.get(), rect.get(), appearState.get(), doc->getXRef(), addedDingbatsResource, resourcesDictObj.getDict());
+    const bool success = appearBuilder.drawFormField(field, form, resources, da, border.get(), appearCharacs.get(), rect.get(), appearState.get(), doc->getXRef(), resourcesDictObj.getDict());
     if (!success && form && da != form->getDefaultAppearance()) {
         da = form->getDefaultAppearance();
-        appearBuilder.drawFormField(field, form, resources, da, border.get(), appearCharacs.get(), rect.get(), appearState.get(), doc->getXRef(), addedDingbatsResource, resourcesDictObj.getDict());
+        appearBuilder.drawFormField(field, form, resources, da, border.get(), appearCharacs.get(), rect.get(), appearState.get(), doc->getXRef(), resourcesDictObj.getDict());
     }
 
     const GooString *appearBuf = appearBuilder.buffer();
@@ -5096,9 +5111,7 @@ void AnnotWidget::updateAppearanceStream()
         return;
 
     // Create the new appearance
-    bool dummyAddDingbatsResource = false; // This is only update so if we didn't need to add
-                                           // the dingbats resource we should not need it now
-    generateFieldAppearance(&dummyAddDingbatsResource);
+    generateFieldAppearance();
 
     // Fetch the appearance stream we've just created
     Object obj1 = appearance.fetch(doc->getXRef());
@@ -5130,38 +5143,19 @@ void AnnotWidget::draw(Gfx *gfx, bool printing)
         return;
 
     annotLocker();
-    bool addDingbatsResource = false;
 
     // Only construct the appearance stream when
     // - annot doesn't have an AP or
     // - NeedAppearances is true
     if (field) {
         if (appearance.isNull() || (form && form->getNeedAppearances())) {
-            generateFieldAppearance(&addDingbatsResource);
+            generateFieldAppearance();
         }
     }
 
     // draw the appearance stream
     Object obj = appearance.fetch(gfx->getXRef());
-    if (addDingbatsResource) {
-        // We are forcing ZaDb but the font does not exist
-        // so create a fake one
-        Dict *fontDict = new Dict(gfx->getXRef());
-        fontDict->add("BaseFont", Object(objName, "ZapfDingbats"));
-        fontDict->add("Subtype", Object(objName, "Type1"));
-
-        Dict *fontsDict = new Dict(gfx->getXRef());
-        fontsDict->add("ZaDb", Object(fontDict));
-
-        Dict *dict = new Dict(gfx->getXRef());
-        dict->add("Font", Object(fontsDict));
-        gfx->pushResources(dict);
-        delete dict;
-    }
     gfx->drawAnnot(&obj, nullptr, color.get(), rect->x1, rect->y1, rect->x2, rect->y2, getRotation());
-    if (addDingbatsResource) {
-        gfx->popResources();
-    }
 }
 
 void AnnotWidget::invalidateAppearance()
diff --git a/poppler/Annot.h b/poppler/Annot.h
index afafc775..de4ce74c 100644
--- a/poppler/Annot.h
+++ b/poppler/Annot.h
@@ -574,7 +574,7 @@ public:
     void drawLineEndSlash(double x, double y, double size, const Matrix &m);
     void drawFieldBorder(const FormField *field, const AnnotBorder *border, const AnnotAppearanceCharacs *appearCharacs, const PDFRectangle *rect);
     bool drawFormField(const FormField *field, const Form *form, const GfxResources *resources, const GooString *da, const AnnotBorder *border, const AnnotAppearanceCharacs *appearCharacs, const PDFRectangle *rect,
-                       const GooString *appearState, XRef *xref, bool *addedDingbatsResource, Dict *resourcesDict);
+                       const GooString *appearState, XRef *xref, Dict *resourcesDict);
     static double lineEndingXShorten(AnnotLineEndingStyle endingStyle, double size);
     static double lineEndingXExtendBBox(AnnotLineEndingStyle endingStyle, double size);
     void writeString(const GooString &str);
@@ -585,17 +585,18 @@ public:
     const GooString *buffer() const;
 
 private:
-    bool drawListBox(const FormFieldChoice *fieldChoice, const AnnotBorder *border, const PDFRectangle *rect, const GooString *da, const GfxResources *resources, int quadding);
+    bool drawListBox(const FormFieldChoice *fieldChoice, const AnnotBorder *border, const PDFRectangle *rect, const GooString *da, const GfxResources *resources, int quadding, XRef *xref, Dict *resourcesDict);
     bool drawFormFieldButton(const FormFieldButton *field, const GfxResources *resources, const GooString *da, const AnnotBorder *border, const AnnotAppearanceCharacs *appearCharacs, const PDFRectangle *rect, const GooString *appearState,
-                             XRef *xref, bool *addedDingbatsResource);
-    bool drawFormFieldText(const FormFieldText *fieldText, const Form *form, const GfxResources *resources, const GooString *da, const AnnotBorder *border, const AnnotAppearanceCharacs *appearCharacs, const PDFRectangle *rect);
-    bool drawFormFieldChoice(const FormFieldChoice *fieldChoice, const Form *form, const GfxResources *resources, const GooString *da, const AnnotBorder *border, const AnnotAppearanceCharacs *appearCharacs, const PDFRectangle *rect);
+                             XRef *xref, Dict *resourcesDict);
+    bool drawFormFieldText(const FormFieldText *fieldText, const Form *form, const GfxResources *resources, const GooString *da, const AnnotBorder *border, const AnnotAppearanceCharacs *appearCharacs, const PDFRectangle *rect, XRef *xref,
+                           Dict *resourcesDict);
+    bool drawFormFieldChoice(const FormFieldChoice *fieldChoice, const Form *form, const GfxResources *resources, const GooString *da, const AnnotBorder *border, const AnnotAppearanceCharacs *appearCharacs, const PDFRectangle *rect,
+                             XRef *xref, Dict *resourcesDict);
     bool drawSignatureFieldText(const FormFieldSignature *field, const Form *form, const GfxResources *resources, const GooString *da, const AnnotBorder *border, const AnnotAppearanceCharacs *appearCharacs, const PDFRectangle *rect,
                                 XRef *xref, Dict *resourcesDict);
     void drawSignatureFieldText(const GooString &text, const DefaultAppearance &da, const AnnotBorder *border, const PDFRectangle *rect, XRef *xref, Dict *resourcesDict, double leftMargin, bool centerVertically, bool centerHorizontally);
     bool drawText(const GooString *text, const GooString *da, const GfxResources *resources, const AnnotBorder *border, const AnnotAppearanceCharacs *appearCharacs, const PDFRectangle *rect, bool multiline, int comb, int quadding,
-                  bool txField, bool forceZapfDingbats, XRef *xref, bool *addedDingbatsResource, // xref and addedDingbatsResource both must not be null if forceZapfDingbats is passed
-                  bool password);
+                  bool txField, bool forceZapfDingbats, XRef *xref, bool password, Dict *resourcesDict, const char *defaultFallback = "Helvetica");
     void drawArrowPath(double x, double y, const Matrix &m, int orientation = 1);
 
     GooString *appearBuf;
@@ -1424,7 +1425,7 @@ public:
     void draw(Gfx *gfx, bool printing) override;
     void invalidateAppearance() override;
 
-    void generateFieldAppearance(bool *addedDingbatsResource);
+    void generateFieldAppearance();
     void updateAppearanceStream();
 
     AnnotWidgetHighlightMode getMode() { return mode; }
diff --git a/qt5/src/poppler-pdf-converter.cc b/qt5/src/poppler-pdf-converter.cc
index 5606b6e5..379312f6 100644
--- a/qt5/src/poppler-pdf-converter.cc
+++ b/qt5/src/poppler-pdf-converter.cc
@@ -168,9 +168,7 @@ bool PDFConverter::sign(const NewSignatureData &data)
     appearCharacs->setBackColor(std::unique_ptr<AnnotColor> { convertQColor(data.backgroundColor()) });
     signatureAnnot->setAppearCharacs(std::move(appearCharacs));
 
-    bool dummyAddDingbatsResource = false; // This is only update so if we didn't need to add
-                                           // the dingbats resource we should not need it now
-    signatureAnnot->generateFieldAppearance(&dummyAddDingbatsResource);
+    signatureAnnot->generateFieldAppearance();
     signatureAnnot->updateAppearanceStream();
 
     FormWidget *formWidget = field->getWidget(field->getNumWidgets() - 1);
diff --git a/qt6/src/poppler-pdf-converter.cc b/qt6/src/poppler-pdf-converter.cc
index 04ee5761..19933c41 100644
--- a/qt6/src/poppler-pdf-converter.cc
+++ b/qt6/src/poppler-pdf-converter.cc
@@ -168,9 +168,7 @@ bool PDFConverter::sign(const NewSignatureData &data)
     appearCharacs->setBackColor(std::unique_ptr<AnnotColor> { convertQColor(data.backgroundColor()) });
     signatureAnnot->setAppearCharacs(std::move(appearCharacs));
 
-    bool dummyAddDingbatsResource = false; // This is only update so if we didn't need to add
-                                           // the dingbats resource we should not need it now
-    signatureAnnot->generateFieldAppearance(&dummyAddDingbatsResource);
+    signatureAnnot->generateFieldAppearance();
     signatureAnnot->updateAppearanceStream();
 
     FormWidget *formWidget = field->getWidget(field->getNumWidgets() - 1);


More information about the poppler mailing list