From 6dc3690fde159a996fba31e9b12a2717e5a850f9 Mon Sep 17 00:00:00 2001 From: Oliver Sander Date: Fri, 18 Aug 2017 11:51:06 +0200 Subject: [PATCH 3/3] 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. The patch only adds the new code, in order to make the diff short and easy to review. A subsequent patch will then remove the obsolete Splash code in Arthur. 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. --- qt5/src/ArthurOutputDev.cc | 145 ++++++++++++++++++++++++++++++++++++++++++++- qt5/src/ArthurOutputDev.h | 21 +++++++ splash/SplashFTFontFile.cc | 4 ++ splash/SplashFTFontFile.h | 3 + 4 files changed, 172 insertions(+), 1 deletion(-) diff --git a/qt5/src/ArthurOutputDev.cc b/qt5/src/ArthurOutputDev.cc index 1b0b03f2..68165638 100644 --- a/qt5/src/ArthurOutputDev.cc +++ b/qt5/src/ArthurOutputDev.cc @@ -22,6 +22,7 @@ // Copyright (C) 2011 Andreas Hartmetz // Copyright (C) 2013 Thomas Freitag // Copyright (C) 2013 Dominik Haumann +// Copyright (C) 2013 Mihai Niculescu // Copyright (C) 2017 Oliver Sander // // To see a description of the changes please see the Changelog file that @@ -50,12 +51,15 @@ #include "ArthurOutputDev.h" #include +#include +#include #include //------------------------------------------------------------------------ #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" @@ -354,6 +358,66 @@ void ArthurOutputDev::updateStrokeOpacity(GfxState *state) void ArthurOutputDev::updateFont(GfxState *state) { + GfxFont *font = state->getFont(); + if (!font) + { + return; + } + + // is the font in the cache? + auto fontID = *font->getID(); + 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(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(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"; + } + + // ***************************************************************************** + // Here starts the old Splash-based code + // ***************************************************************************** + #ifdef HAVE_SPLASH GfxFont *gfxFont; GfxFontLoc *fontLoc; @@ -373,6 +437,7 @@ void ArthurOutputDev::updateFont(GfxState *state) int n; int faceIndex = 0; SplashCoord matrix[6]; + SplashFTFontFile* ftFontFile; m_needFontUpdate = false; m_font = NULL; @@ -388,6 +453,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))) { @@ -548,6 +616,10 @@ void ArthurOutputDev::updateFont(GfxState *state) } } + ftFontFile = dynamic_cast(fontFile); + if (ftFontFile) + m_codeToGID = ftFontFile->getCodeToGID(); + // get the font matrix textMat = state->getTextMat(); fontSize = state->getFontSize(); @@ -646,9 +718,80 @@ 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) { + + // check for invisible text -- this is used by Acrobat Capture + int render = state->getRender(); + if (render == 3 || !m_rawFont) { + qDebug() << "Invisible text found!"; + return; + } + + 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(); + } + + return; + + // ***************************************************************************** + // Here starts the Splash-based render code + // ***************************************************************************** + #ifdef HAVE_SPLASH // SplashPath *path; - int render; if (m_needFontUpdate) { updateFont(state); diff --git a/qt5/src/ArthurOutputDev.h b/qt5/src/ArthurOutputDev.h index f0bd362e..57ac988b 100644 --- a/qt5/src/ArthurOutputDev.h +++ b/qt5/src/ArthurOutputDev.h @@ -19,6 +19,7 @@ // Copyright (C) 2010 Pino Toscano // Copyright (C) 2011 Andreas Hartmetz // Copyright (C) 2013 Thomas Freitag +// Copyright (C) 2013 Mihai Niculescu // Copyright (C) 2017 Oliver Sander // // To see a description of the changes please see the Changelog file that @@ -33,6 +34,9 @@ #pragma interface #endif +#include +#include + #include "goo/gtypes.h" #include "OutputDev.h" #include "GfxState.h" @@ -48,6 +52,8 @@ class SplashFont; class SplashFontEngine; struct SplashGlyphBitmap; +class QRawFont; + //------------------------------------------------------------------------ // ArthurOutputDev - Qt 5 QPainter renderer //------------------------------------------------------------------------ @@ -170,6 +176,21 @@ private: SplashFontEngine *m_fontEngine; SplashFont *m_font; // current font XRef *xref; // xref table for current document + + // The current font in use + QRawFont* m_rawFont; + + struct RefCompare { + bool operator() (const Ref& lhs, const Ref& rhs) const { + return lhs.num < rhs.num; + } + }; + + // Cache all fonts + std::map, RefCompare> 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, -- 2.14.1