[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