[poppler] poppler/Annot.cc poppler/Annot.h poppler/Form.cc poppler/Form.h poppler/PDFDoc.cc
GitLab Mirror
gitlab-mirror at kemper.freedesktop.org
Wed Apr 6 12:18:30 UTC 2022
poppler/Annot.cc | 213 +++++++++++++++++++++++++-----------------------------
poppler/Annot.h | 3
poppler/Form.cc | 25 ++++--
poppler/Form.h | 6 -
poppler/PDFDoc.cc | 10 ++
5 files changed, 131 insertions(+), 126 deletions(-)
New commits:
commit db7865c6a38dd9d26d8b16ab2727fcec311efad8
Author: Albert Astals Cid <aacid at kde.org>
Date: Wed Mar 23 13:35:01 2022 +0100
Signatures: Make sure we embed the needed fonts
diff --git a/poppler/Annot.cc b/poppler/Annot.cc
index 8312cf97..5959a993 100644
--- a/poppler/Annot.cc
+++ b/poppler/Annot.cc
@@ -3101,6 +3101,90 @@ private:
std::vector<Data> data;
};
+struct DrawFreeTextResult
+{
+ std::string text;
+ int nLines = 0;
+};
+
+static DrawFreeTextResult drawFreeTextText(const GooString &text, double availableWidth, const Form *form, const GfxFont &font, const std::string &fontName, double fontSize, VariableTextQuadding quadding)
+{
+ DrawFreeTextResult result;
+ int i = 0;
+ double xposPrev = 0;
+ const bool isUnicode = text.hasUnicodeMarker();
+ while (i < text.getLength()) {
+ HorizontalTextLayouter textLayouter;
+
+ GooString out;
+ double availableTextWidthInFontPtSize = availableWidth / fontSize;
+ double blockWidth;
+ bool newFontNeeded;
+ Annot::layoutText(&text, &out, &i, font, &blockWidth, availableTextWidthInFontPtSize, nullptr, false, &newFontNeeded);
+ availableTextWidthInFontPtSize -= blockWidth;
+ textLayouter.add(out.toStr(), fontName, blockWidth * fontSize);
+
+ 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)(text.getChar(i)) << 8;
+ uChar += (unsigned char)(text.getChar(i + 1));
+ } else {
+ uChar = pdfDocEncoding[text.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(text.toStr().substr(i, isUnicode ? 2 : 1));
+ if (isUnicode) {
+ auxContents.prependUnicodeMarker();
+ }
+ int auxI = 0;
+ Annot::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 * fontSize);
+ }
+
+ } else {
+ error(errSyntaxError, -1, "AnnotFreeText::generateFreeTextAppearance, couldn't find a font for character U+{0:04uX}", uChar);
+ newFontNeeded = false;
+ i += text.hasUnicodeMarker() ? 2 : 1;
+ }
+ }
+
+ // Now layout the rest of the text with the original font if we still have space
+ if (availableTextWidthInFontPtSize >= 0) {
+ Annot::layoutText(&text, &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(), fontName, blockWidth * fontSize);
+ } else {
+ i -= isUnicode ? 2 : 1;
+ }
+ }
+ }
+
+ result.text += textLayouter.layout(quadding, availableWidth, fontSize, xposPrev);
+ result.nLines += 1;
+ }
+ return result;
+}
+
void AnnotFreeText::generateFreeTextAppearance()
{
double borderWidth, ca = opacity;
@@ -3192,80 +3276,8 @@ 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());
-
- int i = 0;
- double xposPrev = 0;
- const bool isUnicode = contents->hasUnicodeMarker();
- while (i < contents->getLength()) {
- HorizontalTextLayouter textLayouter;
-
- GooString out;
- 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;
- }
- }
- }
-
- const std::string layoutedLine = textLayouter.layout(quadding, textwidth, da.getFontPtSize(), xposPrev);
- appearBuilder.append(layoutedLine.c_str());
- }
-
+ const DrawFreeTextResult textCommands = drawFreeTextText(*contents, textwidth, form, *font, da.getFontName().getName(), da.getFontPtSize(), quadding);
+ appearBuilder.append(textCommands.text.c_str());
appearBuilder.append("ET Q\n");
double bbox[4];
@@ -5195,23 +5207,23 @@ bool AnnotAppearanceBuilder::drawSignatureFieldText(const FormFieldSignature *fi
const GooString &leftText = field->getCustomAppearanceLeftContent();
if (leftText.toStr().empty()) {
- drawSignatureFieldText(contents, DefaultAppearance(_da), border, rect, xref, resourcesDict, 0, false /* don't center vertically */, false /* don't center horizontally */);
+ drawSignatureFieldText(contents, form, DefaultAppearance(_da), border, rect, xref, resourcesDict, 0, false /* don't center vertically */, false /* don't center horizontally */);
} else {
DefaultAppearance daLeft(_da);
daLeft.setFontPtSize(field->getCustomAppearanceLeftFontSize());
const double halfWidth = (rect->x2 - rect->x1) / 2;
PDFRectangle rectLeft(rect->x1, rect->y1, rect->x1 + halfWidth, rect->y2);
- drawSignatureFieldText(leftText, daLeft, border, &rectLeft, xref, resourcesDict, 0, true /* center vertically */, true /* center horizontally */);
+ drawSignatureFieldText(leftText, form, daLeft, border, &rectLeft, xref, resourcesDict, 0, true /* center vertically */, true /* center horizontally */);
PDFRectangle rectRight(rectLeft.x2, rect->y1, rect->x2, rect->y2);
- drawSignatureFieldText(contents, DefaultAppearance(_da), border, &rectRight, xref, resourcesDict, halfWidth, true /* center vertically */, false /* don't center horizontally */);
+ drawSignatureFieldText(contents, form, DefaultAppearance(_da), border, &rectRight, xref, resourcesDict, halfWidth, true /* center vertically */, false /* don't center horizontally */);
}
return true;
}
-void AnnotAppearanceBuilder::drawSignatureFieldText(const GooString &text, const DefaultAppearance &da, const AnnotBorder *border, const PDFRectangle *rect, XRef *xref, Dict *resourcesDict, double leftMargin, bool centerVertically,
- bool centerHorizontally)
+void AnnotAppearanceBuilder::drawSignatureFieldText(const GooString &text, const Form *form, const DefaultAppearance &da, const AnnotBorder *border, const PDFRectangle *rect, XRef *xref, Dict *resourcesDict, double leftMargin,
+ bool centerVertically, bool centerHorizontally)
{
double borderWidth = 0;
append("q\n");
@@ -5230,52 +5242,25 @@ void AnnotAppearanceBuilder::drawSignatureFieldText(const GooString &text, const
const double textwidth = width - 2 * textmargin;
// create a Helvetica fake font
- std::unique_ptr<const GfxFont> font = createAnnotDrawFont(xref, resourcesDict, da.getFontName().getName());
-
- // calculate the string tokenization
- int i = 0;
- std::vector<std::pair<std::string, double>> outTexts;
- while (i < text.getLength()) {
- GooString out;
- double textWidth;
- Annot::layoutText(&text, &out, &i, *font, &textWidth, textwidth / da.getFontPtSize(), nullptr, false);
- outTexts.emplace_back(out.toStr(), textWidth * da.getFontPtSize());
+ std::shared_ptr<const GfxFont> font = form->getDefaultResources()->lookupFont(da.getFontName().getName());
+ if (!font) {
+ font = createAnnotDrawFont(xref, resourcesDict, da.getFontName().getName());
}
// Setup text clipping
appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} re W n\n", leftMargin + textmargin, textmargin, textwidth, height - 2 * textmargin);
-
- // Set font state
setDrawColor(da.getFontColor(), true);
- appendf("BT 1 0 0 1 {0:.2f} {1:.2f} Tm\n", textmargin, height - textmargin - da.getFontPtSize() * font->getDescent());
- setTextFont(da.getFontName(), da.getFontPtSize());
+ const DrawFreeTextResult textCommands = drawFreeTextText(text, textwidth, form, *font, da.getFontName().getName(), da.getFontPtSize(), centerHorizontally ? VariableTextQuadding::centered : VariableTextQuadding::leftJustified);
- double xDelta = centerHorizontally ? 0 : leftMargin;
- double currentX = 0;
- double yDelta = -da.getFontPtSize();
+ double yDelta = height - textmargin - da.getFontPtSize() * font->getDescent();
if (centerVertically) {
- const double outTextHeight = outTexts.size() * da.getFontPtSize();
+ const double outTextHeight = textCommands.nLines * da.getFontPtSize();
if (outTextHeight < height) {
yDelta -= (height - outTextHeight) / 2;
}
}
- for (const std::pair<std::string, double> &outText : outTexts) {
- if (centerHorizontally) {
- const double lineX = (width - outText.second) / 2;
- xDelta = (lineX - currentX);
- currentX += xDelta;
- }
-
- appendf("{0:.2f} {1:.2f} Td\n", xDelta, yDelta);
- writeString(outText.first);
- append("Tj\n");
-
- if (!centerHorizontally) {
- xDelta = 0;
- }
- yDelta = -da.getFontPtSize();
- }
-
+ appendf("BT 1 0 0 1 {0:.2f} {1:.2f} Tm\n", leftMargin + textmargin, yDelta);
+ append(textCommands.text.c_str());
append("ET Q\n");
}
diff --git a/poppler/Annot.h b/poppler/Annot.h
index 29e8f6ea..f9c7816c 100644
--- a/poppler/Annot.h
+++ b/poppler/Annot.h
@@ -623,7 +623,8 @@ private:
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);
+ void drawSignatureFieldText(const GooString &text, const Form *form, 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, const VariableTextQuadding quadding, XRef *xref,
Dict *resourcesDict, const int flags = NoDrawTextFlags, const int nCombs = 0);
void drawArrowPath(double x, double y, const Matrix &m, int orientation = 1);
diff --git a/poppler/Form.cc b/poppler/Form.cc
index 165e7288..41f823dc 100644
--- a/poppler/Form.cc
+++ b/poppler/Form.cc
@@ -693,7 +693,13 @@ bool FormWidgetSignature::signDocumentWithAppearance(const char *saveFilename, c
GooString *aux = getField()->getDefaultAppearance();
std::string originalDefaultAppearance = aux ? aux->toStr() : std::string();
- const DefaultAppearance da { { objName, "SigFont" }, fontSize, std::move(fontColor) };
+ Form *form = doc->getCatalog()->getForm();
+ std::string pdfFontName = form->findFontInDefaultResources("Helvetica", "");
+ if (pdfFontName.empty()) {
+ pdfFontName = form->addFontToDefaultResources("Helvetica", "");
+ }
+
+ const DefaultAppearance da { { objName, pdfFontName.c_str() }, fontSize, std::move(fontColor) };
getField()->setDefaultAppearance(da.toAppearanceString());
std::unique_ptr<AnnotAppearanceCharacs> origAppearCharacs = getWidgetAnnotation()->getAppearCharacs() ? getWidgetAnnotation()->getAppearCharacs()->copy() : nullptr;
@@ -710,6 +716,9 @@ bool FormWidgetSignature::signDocumentWithAppearance(const char *saveFilename, c
getWidgetAnnotation()->generateFieldAppearance();
getWidgetAnnotation()->updateAppearanceStream();
+ form->ensureFontsForAllCharacters(&signatureText, pdfFontName);
+ form->ensureFontsForAllCharacters(&signatureTextLeft, pdfFontName);
+
::FormFieldSignature *ffs = static_cast<::FormFieldSignature *>(getField());
ffs->setCustomAppearanceContent(signatureText);
ffs->setCustomAppearanceLeftContent(signatureTextLeft);
@@ -2656,7 +2665,7 @@ FormField *Form::createFieldFromDict(Object &&obj, PDFDoc *docA, const Ref aref,
static const std::string kOurDictFontNamePrefix = "popplerfont";
-std::string Form::findFontInDefaultResources(const std::string &fontFamily, const std::string &fontStyle)
+std::string Form::findFontInDefaultResources(const std::string &fontFamily, const std::string &fontStyle) const
{
if (!resDict.isDict()) {
return {};
@@ -2899,9 +2908,11 @@ std::string Form::addFontToDefaultResources(const std::string &filepath, int fac
return dictFontName;
}
-std::string Form::getFallbackFontForChar(Unicode uChar, const GfxFont &fontToEmulate)
+std::string Form::getFallbackFontForChar(Unicode uChar, const GfxFont &fontToEmulate) const
{
- return doGetAddFontToDefaultResources(uChar, fontToEmulate, false /*addIfNotFound*/);
+ const UCharFontSearchResult res = globalParams->findSystemFontFileForUChar(uChar, fontToEmulate);
+
+ return findFontInDefaultResources(res.family, res.style);
}
void Form::ensureFontsForAllCharacters(const GooString *unicodeText, const std::string &pdfFontNameToEmulate)
@@ -2927,18 +2938,18 @@ void Form::ensureFontsForAllCharacters(const GooString *unicodeText, const std::
if (c < cidFont->getCIDToGIDLen() && c != 0 && c != '\r' && c != '\n') {
const int glyph = cidFont->getCIDToGID()[c];
if (glyph == 0) {
- doGetAddFontToDefaultResources(uChar, *f, true /*addIfNotFound*/);
+ doGetAddFontToDefaultResources(uChar, *f);
}
}
}
}
-std::string Form::doGetAddFontToDefaultResources(Unicode uChar, const GfxFont &fontToEmulate, bool addIfNotFound)
+std::string Form::doGetAddFontToDefaultResources(Unicode uChar, const GfxFont &fontToEmulate)
{
const UCharFontSearchResult res = globalParams->findSystemFontFileForUChar(uChar, fontToEmulate);
std::string pdfFontName = findFontInDefaultResources(res.family, res.style);
- if (addIfNotFound && pdfFontName.empty()) {
+ if (pdfFontName.empty()) {
pdfFontName = addFontToDefaultResources(res.filepath, res.faceIndex, res.family, res.style);
}
return pdfFontName;
diff --git a/poppler/Form.h b/poppler/Form.h
index 98421c9a..903482a0 100644
--- a/poppler/Form.h
+++ b/poppler/Form.h
@@ -679,7 +679,7 @@ public:
// 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);
+ std::string findFontInDefaultResources(const std::string &fontFamily, const std::string &fontStyle) const;
// 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
@@ -687,7 +687,7 @@ public:
// 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);
+ std::string getFallbackFontForChar(Unicode uChar, const GfxFont &fontToEmulate) const;
// 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
@@ -716,7 +716,7 @@ private:
// 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);
+ std::string doGetAddFontToDefaultResources(Unicode uChar, const GfxFont &fontToEmulate);
FormField **rootFields;
int numFields;
diff --git a/poppler/PDFDoc.cc b/poppler/PDFDoc.cc
index 2b0139a4..25d0b56a 100644
--- a/poppler/PDFDoc.cc
+++ b/poppler/PDFDoc.cc
@@ -2123,7 +2123,13 @@ bool PDFDoc::sign(const char *saveFilename, const char *certNickname, const char
}
}
- const DefaultAppearance da { { objName, "SigFont" }, fontSize, std::move(fontColor) };
+ Form *form = catalog->getCreateForm();
+ std::string pdfFontName = form->findFontInDefaultResources("Helvetica", "");
+ if (pdfFontName.empty()) {
+ pdfFontName = form->addFontToDefaultResources("Helvetica", "");
+ }
+
+ const DefaultAppearance da { { objName, pdfFontName.c_str() }, fontSize, std::move(fontColor) };
Object annotObj = Object(new Dict(getXRef()));
annotObj.dictSet("Type", Object(objName, "Annot"));
@@ -2144,6 +2150,8 @@ bool PDFDoc::sign(const char *saveFilename, const char *certNickname, const char
catalog->addFormToAcroForm(ref);
// say that there a now signatures and that we should append only
catalog->getAcroForm()->dictSet("SigFlags", Object(3));
+ form->ensureFontsForAllCharacters(&signatureText, pdfFontName);
+ form->ensureFontsForAllCharacters(&signatureTextLeft, pdfFontName);
std::unique_ptr<::FormFieldSignature> field = std::make_unique<::FormFieldSignature>(this, Object(annotObj.getDict()), ref, nullptr, nullptr);
field->setCustomAppearanceContent(signatureText);
More information about the poppler
mailing list