[poppler] qt5/src splash/SplashFTFontFile.cc splash/SplashFTFontFile.h
Albert Astals Cid
aacid at kemper.freedesktop.org
Thu Sep 14 22:18:33 UTC 2017
qt5/src/ArthurOutputDev.cc | 254 +++++++++++++++++++++++++--------------------
qt5/src/ArthurOutputDev.h | 31 ++++-
splash/SplashFTFontFile.cc | 4
splash/SplashFTFontFile.h | 3
4 files changed, 177 insertions(+), 115 deletions(-)
New commits:
commit cc43c720e857548175a9e35b0686a1a7a8957f50
Author: Oliver Sander <oliver.sander at tu-dresden.de>
Date: Fri Aug 18 11:51:06 2017 +0200
Replace Splash font rendering by Qt font rendering
Previously, the Arthur backend would use Splash code to do
its font rendering. That was not a satisfactory solution:
Qt can do font rendering directly. Also, the Splash font
rendering in the Arthur code had a few bugs, which lead
to legible-but-not-pretty fonts.
This patch replaces the Splash font rendering by Qt font
rendering. Some Splash code will have to remain, because
Qt seems unable to do the proper charcode-to-glyph-index
transformations.
I took a lot of inspiration from Mihai Niculescu's patch at
https://lists.freedesktop.org/archives/poppler/2013-June/010370.html
That's why the patch adds Mihai's name in the copyright list.
diff --git a/qt5/src/ArthurOutputDev.cc b/qt5/src/ArthurOutputDev.cc
index fe285aa9..ca0e0b27 100644
--- a/qt5/src/ArthurOutputDev.cc
+++ b/qt5/src/ArthurOutputDev.cc
@@ -22,6 +22,7 @@
// Copyright (C) 2011 Andreas Hartmetz <ahartmetz at gmail.com>
// Copyright (C) 2013 Thomas Freitag <Thomas.Freitag at alfa.de>
// Copyright (C) 2013 Dominik Haumann <dhaumann at kde.org>
+// Copyright (C) 2013 Mihai Niculescu <q.quark at gmail.com>
// Copyright (C) 2017 Oliver Sander <oliver.sander at tu-dresden.de>
//
// To see a description of the changes please see the Changelog file that
@@ -50,17 +51,15 @@
#include "ArthurOutputDev.h"
#include <QtCore/QtDebug>
+#include <QRawFont>
+#include <QGlyphRun>
#include <QtGui/QPainterPath>
//------------------------------------------------------------------------
#ifdef HAVE_SPLASH
#include "splash/SplashFontFileID.h"
-#include "splash/SplashFontFile.h"
+#include "splash/SplashFTFontFile.h"
#include "splash/SplashFontEngine.h"
-#include "splash/SplashFont.h"
-#include "splash/SplashMath.h"
-#include "splash/SplashPath.h"
-#include "splash/SplashGlyphBitmap.h"
//------------------------------------------------------------------------
// SplashOutFontFileID
//------------------------------------------------------------------------
@@ -94,7 +93,6 @@ ArthurOutputDev::ArthurOutputDev(QPainter *painter):
{
m_currentBrush = QBrush(Qt::SolidPattern);
m_fontEngine = 0;
- m_font = 0;
}
ArthurOutputDev::~ArthurOutputDev()
@@ -284,6 +282,71 @@ void ArthurOutputDev::updateStrokeOpacity(GfxState *state)
void ArthurOutputDev::updateFont(GfxState *state)
{
+ GfxFont *font = state->getFont();
+ if (!font)
+ {
+ return;
+ }
+
+ // is the font in the cache?
+ ArthurFontID fontID = {*font->getID(), state->getFontSize()};
+ auto cacheEntry = m_rawFontCache.find(fontID);
+
+ if (cacheEntry!=m_rawFontCache.end()) {
+
+ // Take the font from the cache
+ m_rawFont = cacheEntry->second.get();
+
+ } else {
+
+ // New font: load it into the cache
+ float fontSize = state->getFontSize();
+
+ GfxFontLoc* fontLoc = font->locateFont(xref, nullptr);
+
+ if (fontLoc) {
+ // load the font from respective location
+ switch (fontLoc->locType) {
+ case gfxFontLocEmbedded: {// if there is an embedded font, read it to memory
+ int fontDataLen;
+ const char* fontData = font->readEmbFontFile(xref, &fontDataLen);
+
+ m_rawFont = new QRawFont(QByteArray(fontData, fontDataLen), fontSize);
+ m_rawFontCache.insert(std::make_pair(fontID,std::unique_ptr<QRawFont>(m_rawFont)));
+ break;
+ }
+ case gfxFontLocExternal:{ // font is in an external font file
+ QString fontFile(fontLoc->path->getCString());
+ m_rawFont = new QRawFont(fontFile, fontSize);
+ m_rawFontCache.insert(std::make_pair(fontID,std::unique_ptr<QRawFont>(m_rawFont)));
+ break;
+ }
+ case gfxFontLocResident:{ // font resides in a PS printer
+ qDebug() << "Font Resident Encoding:" << fontLoc->encoding->getCString() << ", not implemented yet!";
+
+ break;
+ }
+ }// end switch
+
+ } else {
+ qDebug() << "Font location not found!";
+ return;
+ }
+ }
+
+ if (!m_rawFont->isValid()) {
+ qDebug() << "RawFont is not valid";
+ }
+
+ // *****************************************************************************
+ // We have now successfully loaded the font into a QRawFont object. This
+ // allows us to draw all the glyphs in the font. However, what is missing is
+ // the charcode-to-glyph-index mapping. Apparently, Qt does not provide this
+ // information at all. We therefore now load the font again, this time into
+ // a Splash font object. This is wasteful, but I see no other way to access
+ // the important glyph-index mapping.
+ // *****************************************************************************
+
#ifdef HAVE_SPLASH
GfxFont *gfxFont;
GfxFontLoc *fontLoc;
@@ -297,15 +360,13 @@ void ArthurOutputDev::updateFont(GfxState *state)
char *tmpBuf;
int tmpBufLen = 0;
int *codeToGID;
- double *textMat;
- double m11, m12, m21, m22, fontSize;
- SplashCoord mat[4];
+ SplashCoord mat[4] = {0,0,0,0};
int n;
int faceIndex = 0;
- SplashCoord matrix[6];
+ SplashCoord matrix[6] = {1,0,0,1,0,0};
+ SplashFTFontFile* ftFontFile;
m_needFontUpdate = false;
- m_font = NULL;
fileName = NULL;
tmpBuf = NULL;
fontLoc = NULL;
@@ -318,6 +379,9 @@ void ArthurOutputDev::updateFont(GfxState *state)
goto err1;
}
+ // Default: no codeToGID table
+ m_codeToGID = nullptr;
+
// check the font file cache
id = new SplashOutFontFileID(gfxFont->getID());
if ((fontFile = m_fontEngine->getFontFile(id))) {
@@ -478,28 +542,15 @@ void ArthurOutputDev::updateFont(GfxState *state)
}
}
- // get the font matrix
- textMat = state->getTextMat();
- fontSize = state->getFontSize();
- m11 = textMat[0] * fontSize * state->getHorizScaling();
- m12 = textMat[1] * fontSize * state->getHorizScaling();
- m21 = textMat[2] * fontSize;
- m22 = textMat[3] * fontSize;
-
- {
- QMatrix painterMatrix = m_painter->worldMatrix();
- matrix[0] = painterMatrix.m11();
- matrix[1] = painterMatrix.m12();
- matrix[2] = painterMatrix.m21();
- matrix[3] = painterMatrix.m22();
- matrix[4] = painterMatrix.dx();
- matrix[5] = painterMatrix.dy();
- }
+ ftFontFile = dynamic_cast<SplashFTFontFile*>(fontFile);
+ if (ftFontFile)
+ m_codeToGID = ftFontFile->getCodeToGID();
- // create the scaled font
- mat[0] = m11; mat[1] = -m12;
- mat[2] = m21; mat[3] = -m22;
- m_font = m_fontEngine->getFont(fontFile, mat, matrix);
+ // create dummy font
+ // The font matrices are bogus, but we will never use the glyphs anyway.
+ // However we need to call m_fontEngine->getFont, in order to have the
+ // font in the Splash font cache. Otherwise we'd load it again and again.
+ m_fontEngine->getFont(fontFile, mat, matrix);
delete fontLoc;
if (fontsrc && !fontsrc->isFile)
@@ -576,92 +627,71 @@ void ArthurOutputDev::drawChar(GfxState *state, double x, double y,
double dx, double dy,
double originX, double originY,
CharCode code, int nBytes, Unicode *u, int uLen) {
-#ifdef HAVE_SPLASH
-// SplashPath *path;
- int render;
-
- if (m_needFontUpdate) {
- updateFont(state);
- }
- if (!m_font) {
- return;
- }
// check for invisible text -- this is used by Acrobat Capture
- render = state->getRender();
- if (render == 3) {
+ int render = state->getRender();
+ if (render == 3 || !m_rawFont) {
+ qDebug() << "Invisible text found!";
return;
}
- x -= originX;
- y -= originY;
-
- // fill
- if (!(render & 1)) {
- SplashPath * fontPath;
- fontPath = m_font->getGlyphPath(code);
- if (fontPath) {
- QPainterPath qPath;
- qPath.setFillRule(Qt::WindingFill);
- for (int i = 0; i < fontPath->length; ++i) {
- // SplashPath.flags: bitwise or allowed
- if (fontPath->flags[i] & splashPathLast || fontPath->flags[i] & splashPathClosed) {
- qPath.closeSubpath();
- }
- if (fontPath->flags[i] & splashPathFirst) {
- qPath.moveTo(fontPath->pts[i].x+x,-fontPath->pts[i].y+y);
- }
- if (fontPath->flags[i] & splashPathCurve) {
- qPath.quadTo(fontPath->pts[i].x+x,-fontPath->pts[i].y+y,
- fontPath->pts[i+1].x+x,-fontPath->pts[i+1].y+y);
- ++i;
- }
- // FIXME fix this
- // else if (fontPath->flags[i] & splashPathArcCW) {
- // qDebug() << "Need to implement arc";
- // }
- else {
- qPath.lineTo(fontPath->pts[i].x+x,-fontPath->pts[i].y+y);
- }
- }
- GfxRGB rgb;
- QColor brushColour = m_currentBrush.color();
- state->getFillRGB(&rgb);
- brushColour.setRgbF(colToDbl(rgb.r), colToDbl(rgb.g), colToDbl(rgb.b), state->getFillOpacity());
- m_painter->setBrush(brushColour);
- m_painter->setPen(Qt::NoPen);
- m_painter->drawPath(qPath);
- delete fontPath;
- }
- }
-
- // stroke
- if ((render & 3) == 1 || (render & 3) == 2) {
- qDebug() << "no stroke";
- /*
- if ((path = m_font->getGlyphPath(code))) {
- path->offset((SplashCoord)x1, (SplashCoord)y1);
- splash->stroke(path);
- delete path;
- }
- */
- }
-
- // clip
- if (render & 4) {
- qDebug() << "no clip";
- /*
- path = m_font->getGlyphPath(code);
- path->offset((SplashCoord)x1, (SplashCoord)y1);
- if (textClipPath) {
- textClipPath->append(path);
- delete path;
- } else {
- textClipPath = path;
- }
- */
+ if (!(render & 1))
+ {
+ quint32 glyphIndex = (m_codeToGID) ? m_codeToGID[code] : code;
+ QPointF glyphPosition = QPointF(x-originX, y-originY);
+
+ // QGlyphRun objects can hold an entire sequence of glyphs, and it would possibly
+ // be more efficient to simply note the glyph and glyph position here and then
+ // draw several glyphs at once in the endString method. What keeps us from doing
+ // that is the transformation below: each glyph needs to be drawn upside down,
+ // i.e., reflected at its own baseline. Since we have no guarantee that this
+ // baseline is the same for all glyphs in a string we have to do it one by one.
+ QGlyphRun glyphRun;
+ glyphRun.setRawData(&glyphIndex, &glyphPosition, 1);
+ glyphRun.setRawFont(*m_rawFont);
+
+ // Store the QPainter state; we need to modify it temporarily
+ m_painter->save();
+
+ // Apply the text matrix to the glyph. The glyph is not scaled by the font size,
+ // because the font in m_rawFont already has the correct size.
+ // Additionally, the CTM is upside down, i.e., it contains a negative Y-scaling
+ // entry. Therefore, Qt will paint the glyphs upside down. We need to temporarily
+ // reflect the page at glyphPosition.y().
+
+ // Make the glyph position the coordinate origin -- that's our center of scaling
+ const double *textMat = state->getTextMat();
+
+ m_painter->translate(QPointF(glyphPosition.x(),glyphPosition.y()));
+
+ QTransform textTransform(textMat[0] * state->getHorizScaling(),
+ textMat[1] * state->getHorizScaling(),
+ -textMat[2], // reflect at the horizontal axis,
+ -textMat[3], // because CTM is upside-down.
+ 0,
+ 0);
+
+ m_painter->setTransform(textTransform,true);
+
+ // We are painting a filled glyph here. But QPainter uses the pen to draw even filled text,
+ // not the brush. (see, e.g., http://doc.qt.io/qt-5/qpainter.html#setPen )
+ // Therefore we have to temporarily overwrite the pen color.
+
+ // Since we are drawing a filled glyph, one would really expect to have m_currentBrush
+ // have the correct color. However, somehow state->getFillRGB can change without
+ // updateFillColor getting called. Then m_currentBrush may not contain the correct color.
+ GfxRGB rgb;
+ state->getFillRGB(&rgb);
+ QColor fontColor;
+ fontColor.setRgbF(colToDbl(rgb.r), colToDbl(rgb.g), colToDbl(rgb.b), state->getFillOpacity());
+ m_painter->setPen(fontColor);
+
+ // Actually draw the glyph
+ m_painter->drawGlyphRun(QPointF(-glyphPosition.x(),-glyphPosition.y()), glyphRun);
+
+ // Restore transformation and pen color
+ m_painter->restore();
}
-#endif
}
GBool ArthurOutputDev::beginType3Char(GfxState *state, double x, double y,
diff --git a/qt5/src/ArthurOutputDev.h b/qt5/src/ArthurOutputDev.h
index 00a2e08a..dc8b547c 100644
--- a/qt5/src/ArthurOutputDev.h
+++ b/qt5/src/ArthurOutputDev.h
@@ -19,6 +19,7 @@
// Copyright (C) 2010 Pino Toscano <pino at kde.org>
// Copyright (C) 2011 Andreas Hartmetz <ahartmetz at gmail.com>
// Copyright (C) 2013 Thomas Freitag <Thomas.Freitag at alfa.de>
+// Copyright (C) 2013 Mihai Niculescu <q.quark at gmail.com>
// Copyright (C) 2017 Oliver Sander <oliver.sander at tu-dresden.de>
//
// To see a description of the changes please see the Changelog file that
@@ -33,6 +34,9 @@
#pragma interface
#endif
+#include <memory>
+#include <map>
+
#include "goo/gtypes.h"
#include "OutputDev.h"
#include "GfxState.h"
@@ -44,9 +48,9 @@ class GfxPath;
class Gfx8BitFont;
struct GfxRGB;
-class SplashFont;
class SplashFontEngine;
-struct SplashGlyphBitmap;
+
+class QRawFont;
//------------------------------------------------------------------------
// ArthurOutputDev - Qt 5 QPainter renderer
@@ -167,8 +171,29 @@ private:
QBrush m_currentBrush;
GBool m_needFontUpdate; // set when the font needs to be updated
SplashFontEngine *m_fontEngine;
- SplashFont *m_font; // current font
XRef *xref; // xref table for current document
+
+ // The current font in use
+ QRawFont* m_rawFont;
+
+ // Identify a font by its 'Ref' and its font size
+ struct ArthurFontID
+ {
+ Ref ref;
+ double fontSize;
+
+ bool operator<(const ArthurFontID& other) const
+ {
+ return (ref.num < other.ref.num)
+ || ((ref.num == other.ref.num) && (fontSize < other.fontSize));
+ }
+ };
+
+ // Cache all fonts
+ std::map<ArthurFontID,std::unique_ptr<QRawFont> > m_rawFontCache;
+
+ // The table that maps character codes to glyph indexes
+ int* m_codeToGID;
};
#endif
diff --git a/splash/SplashFTFontFile.cc b/splash/SplashFTFontFile.cc
index f0dcf503..5c4f24d3 100644
--- a/splash/SplashFTFontFile.cc
+++ b/splash/SplashFTFontFile.cc
@@ -145,4 +145,8 @@ SplashFont *SplashFTFontFile::makeFont(SplashCoord *mat,
return font;
}
+int *SplashFTFontFile::getCodeToGID() {
+ return codeToGID;
+}
+
#endif // HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
diff --git a/splash/SplashFTFontFile.h b/splash/SplashFTFontFile.h
index 7a7bb218..25303ccc 100644
--- a/splash/SplashFTFontFile.h
+++ b/splash/SplashFTFontFile.h
@@ -62,6 +62,9 @@ public:
SplashFont *makeFont(SplashCoord *mat,
SplashCoord *textMat) override;
+ // Provide access to the code-to-GID map
+ int* getCodeToGID();
+
private:
SplashFTFontFile(SplashFTFontEngine *engineA,
More information about the poppler
mailing list