From 846b535663966d417a7c693c25994bc1b4e7fb5e Mon Sep 17 00:00:00 2001 From: Oliver Sander Date: Tue, 14 Aug 2018 10:51:25 +0200 Subject: [PATCH 4/4] Freetext annotation: respect the font when there is DA but no AP --- poppler/Annot.cc | 99 +++++++++++++++++++++++++++++++++++++++--------- poppler/Annot.h | 5 ++- 2 files changed, 85 insertions(+), 19 deletions(-) diff --git a/poppler/Annot.cc b/poppler/Annot.cc index d37824c1..2a39ae10 100644 --- a/poppler/Annot.cc +++ b/poppler/Annot.cc @@ -1731,6 +1731,10 @@ void AnnotAppearanceBuilder::drawCircleBottomRight(double cx, double cy, double } Object Annot::createForm(const GooString *appearBuf, double *bbox, GBool transparencyGroup, Dict *resDict) { + return createForm(appearBuf, bbox, transparencyGroup, Object(resDict)); +} + +Object Annot::createForm(const GooString *appearBuf, double *bbox, GBool transparencyGroup, Object &&resDictObject) { Dict *appearDict = new Dict(xref); appearDict->set("Length", Object(appearBuf->getLength())); appearDict->set("Subtype", Object(objName, "Form")); @@ -1746,8 +1750,8 @@ Object Annot::createForm(const GooString *appearBuf, double *bbox, GBool transpa d->set("S", Object(objName, "Transparency")); appearDict->set("Group", Object(d)); } - if (resDict) - appearDict->set("Resources", Object(resDict)); + if (resDictObject.isDict()) + appearDict->set("Resources", std::move(resDictObject)); Stream *mStream = new AutoFreeMemStream(copyString(appearBuf->getCString()), 0, appearBuf->getLength(), Object(appearDict)); @@ -2805,24 +2809,24 @@ void AnnotFreeText::setIntent(AnnotFreeTextIntent new_intent) { update ("IT", Object(objName, intentName)); } -static GfxFont * createAnnotDrawFont(XRef * xref, Dict *fontResDict) +static GfxFont * createAnnotDrawFont(XRef * xref, Dict *fontResDict, const char* resourceName = "AnnotDrawFont", const char* fontname = "Helvetica") { const Ref dummyRef = { -1, -1 }; Dict *fontDict = new Dict(xref); - fontDict->add(copyString("BaseFont"), Object(objName, "Helvetica")); + fontDict->add(copyString("BaseFont"), Object(objName, fontname)); fontDict->add(copyString("Subtype"), Object(objName, "Type0")); fontDict->add(copyString("Encoding"), Object(objName, "WinAnsiEncoding")); Dict *fontsDict = new Dict(xref); - fontsDict->add(copyString("AnnotDrawFont"), Object(fontDict)); + fontsDict->add(copyString(resourceName), Object(fontDict)); fontResDict->add(copyString("Font"), Object(fontsDict)); - return GfxFont::makeFont(xref, "AnnotDrawFont", dummyRef, fontDict); + return GfxFont::makeFont(xref, resourceName, dummyRef, fontDict); } -void AnnotFreeText::parseAppearanceString(GooString *da, double &fontsize, AnnotColor* &fontcolor) { +void AnnotFreeText::parseAppearanceString(GooString *da, double &fontsize, AnnotColor* &fontcolor, std::string& fontname) { fontsize = -1; fontcolor = nullptr; if (da) { @@ -2831,7 +2835,9 @@ void AnnotFreeText::parseAppearanceString(GooString *da, double &fontsize, Annot if (i >= 1) { fontsize = gatof(( (GooString *)daToks->get(i-1) )->getCString()); - // TODO: Font name + } + if (i >= 2) { + fontname = std::string(((GooString *)daToks->get(i-2))->getCString()); } // Scan backwards: we are looking for the last set value for (i = daToks->getLength()-1; i >= 0; --i) { @@ -2872,7 +2878,12 @@ void AnnotFreeText::generateFreeTextAppearance() // Parse some properties from the appearance string double fontsize; AnnotColor *fontcolor; - parseAppearanceString(appearanceString, fontsize, fontcolor); + std::string fontname; + parseAppearanceString(appearanceString, fontsize, fontcolor, fontname); + + // skip '/' at beginning of operand + const char* fonttag = fontname.c_str()+1; + // Default values if (fontsize <= 0) fontsize = 10; @@ -2902,13 +2913,63 @@ void AnnotFreeText::generateFreeTextAppearance() const double textwidth = width - 2*textmargin; appearBuilder.appendf ("{0:.2f} {0:.2f} {1:.2f} {2:.2f} re W n\n", textmargin, textwidth, height - 2*textmargin); - Dict *fontResDict = new Dict(xref); - GfxFont *font = createAnnotDrawFont(xref, fontResDict); + GfxFont *font = nullptr; + + // look for font name in default resources + Form *form = doc->getCatalog()->getForm(); // form is owned by catalog, no need to clean it up + + Object resourceObj; + if (form && form->getDefaultResourcesObj() && form->getDefaultResourcesObj()->isDict()) { + resourceObj = form->getDefaultResourcesObj()->copy(); // No real copy, but increment refcount of /DR Dict + if (resourceObj.isDict()) { + Dict *resDict = resourceObj.getDict(); + Object fontResources = resDict->lookupNF("Font"); // The 'Font' subdictionary + + // Resolve reference, if necessary + if (fontResources.isRef()) { + fontResources = fontResources.fetch(xref); + } + + if (!fontResources.isDict()) { + error(errSyntaxWarning, -1, "Font subdictionary is not a dictionary"); + } + + // Get the font dictionary for the actual requested font + Object fontDictionary = fontResources.getDict()->lookupNF(fonttag); + + // Resolve reference, if necessary + Ref fontReference = {-1, -1}; + if (fontDictionary.isRef()) { + fontReference = fontDictionary.getRef(); + fontDictionary = fontDictionary.fetch(xref); + } + + if (!fontDictionary.isDict()) { + error(errSyntaxWarning, -1, "Font dictionary is not a dictionary"); + } + font = GfxFont::makeFont(xref, fontname.c_str(), fontReference, fontDictionary.getDict()); + } + } + + // if fontname is not in in default resources, create a fake font + if (!font) { + Dict *fontResDict = new Dict(xref); + resourceObj = Object(fontResDict); + + // If we get here, our PDF document has either no default resource dictionary, + // or the font in the /DA's Tf operator didn't match an entry in the default resource dictionary. + // This means that the file is broken. As the first fallback we assume that the file writer + // assumed that it must give the actual font name to the Tf operand (rather than a resource name, + // which would have been correct). + // Create a fake font where the value from Tf is used both as resource name and as value to /BaseFont. + // We hardcode other things like encoding in the font resource. + font = createAnnotDrawFont(xref, fontResDict, fonttag, fonttag); + } // Set font state appearBuilder.setDrawColor(fontcolor, gTrue); appearBuilder.appendf ("BT 1 0 0 1 {0:.2f} {1:.2f} Tm\n", textmargin, height - textmargin - fontsize * font->getDescent()); - appearBuilder.appendf ("/AnnotDrawFont {0:.2f} Tf\n", fontsize); + appearBuilder.appendf ("/{0:s} {1:.2f} Tf\n", fonttag, fontsize); int i = 0; double xposPrev = 0; @@ -2944,9 +3005,11 @@ void AnnotFreeText::generateFreeTextAppearance() bbox[3] = rect->y2 - rect->y1; if (ca == 1) { - appearance = createForm(appearBuilder.buffer(), bbox, gFalse, fontResDict); + // transfer ownership of resourceObj to AnnotFreeText::appearance + appearance = createForm(appearBuilder.buffer(), bbox, gFalse, std::move(resourceObj)); } else { - Object aStream = createForm(appearBuilder.buffer(), bbox, gTrue, fontResDict); + // transfer ownership of resourceObj to AnnotFreeText::appearance + Object aStream = createForm(appearBuilder.buffer(), bbox, gTrue, std::move(resourceObj)); GooString appearBuf("/GS0 gs\n/Fm0 Do"); Dict *resDict = createResourcesDict("Fm0", std::move(aStream), "GS0", ca, nullptr); @@ -2959,9 +3022,9 @@ void AnnotFreeText::draw(Gfx *gfx, GBool printing) { return; annotLocker(); - if (appearance.isNull()) { + //if (appearance.isNull()) { generateFreeTextAppearance(); - } + //} // draw the appearance stream Object obj = appearance.fetch(gfx->getXRef()); @@ -2972,9 +3035,9 @@ void AnnotFreeText::draw(Gfx *gfx, GBool printing) { // Before retrieving the res dict, regenerate the appearance stream if needed, // because AnnotFreeText::draw needs to store font info in the res dict Object AnnotFreeText::getAppearanceResDict() { - if (appearance.isNull()) { + //if (appearance.isNull()) { generateFreeTextAppearance(); - } + //} return Annot::getAppearanceResDict(); } diff --git a/poppler/Annot.h b/poppler/Annot.h index 83c10b39..800e54fd 100644 --- a/poppler/Annot.h +++ b/poppler/Annot.h @@ -40,6 +40,8 @@ #pragma interface #endif +#include + #include "Object.h" class XRef; @@ -693,6 +695,7 @@ protected: virtual ~Annot(); virtual void removeReferencedObjects(); // Called by Page::removeAnnot Object createForm(const GooString *appearBuf, double *bbox, GBool transparencyGroup, Dict *resDict); + Object createForm(const GooString *appearBuf, double *bbox, GBool transparencyGroup, Object &&resDictObject); // overload to support incRef/decRef Dict *createResourcesDict(const char *formName, Object &&formStream, const char *stateName, double opacity, const char *blendMode); GBool isVisible(GBool printing); @@ -997,7 +1000,7 @@ public: protected: void initialize(PDFDoc *docA, Dict *dict); - static void parseAppearanceString(GooString *da, double &fontsize, AnnotColor* &fontcolor); + static void parseAppearanceString(GooString *da, double &fontsize, AnnotColor* &fontcolor, std::string& fontname); void generateFreeTextAppearance(); // required -- 2.18.0