[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