From e281729fe736f43187fad05552a661bbd4d578e6 Mon Sep 17 00:00:00 2001 From: Oliver Sander Date: Fri, 18 Aug 2017 11:51:06 +0200 Subject: [PATCH 1/2] 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. It only adds the new code, in order to make the diff short and easy to review. A subsequent patch will then remove the Splash code in Arthur (which is quite a bit). 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 | 133 ++++++++++++++++++++++++++++++++++++++++++++- qt5/src/ArthurOutputDev.h | 7 +++ 2 files changed, 137 insertions(+), 3 deletions(-) diff --git a/qt5/src/ArthurOutputDev.cc b/qt5/src/ArthurOutputDev.cc index fe285aa9..2dae66a1 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,6 +51,8 @@ #include "ArthurOutputDev.h" #include +#include +#include #include //------------------------------------------------------------------------ @@ -284,9 +287,53 @@ void ArthurOutputDev::updateStrokeOpacity(GfxState *state) void ArthurOutputDev::updateFont(GfxState *state) { + GfxFont *font = state->getFont(); + if (!font) + { + return; + } + + 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 = std::unique_ptr(new QRawFont(QByteArray(fontData, fontDataLen), fontSize)); + break; + } + case gfxFontLocExternal:{ // font is in an external font file + QString fontFile(fontLoc->path->getCString()); + m_rawFont = std::unique_ptr(new QRawFont(fontFile, fontSize)); + 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; GfxFontType fontType; SplashOutFontFileID *id; SplashFontFile *fontFile; @@ -298,7 +345,7 @@ void ArthurOutputDev::updateFont(GfxState *state) int tmpBufLen = 0; int *codeToGID; double *textMat; - double m11, m12, m21, m22, fontSize; + double m11, m12, m21, m22; SplashCoord mat[4]; int n; int faceIndex = 0; @@ -576,9 +623,89 @@ 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)) + { + QPointF glyphPosition = QPointF(x-originX, y-originY); + + int numGlyphs = 1; + std::vector glyphIndexes(1); + + if (state->getFont()->isCIDFont()) { + + glyphIndexes[0] = code; + + } else { + + QChar chr(code); + + if (!m_rawFont->glyphIndexesForChars(&chr, 1, glyphIndexes.data(), &numGlyphs)) { + // There are more glyphs than we expected -- resize the array and try again. + glyphIndexes.resize(numGlyphs); + m_rawFont->glyphIndexesForChars(&chr, 1, glyphIndexes.data(), &numGlyphs); + } + + } + + // 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(glyphIndexes.data(), &glyphPosition, numGlyphs); + glyphRun.setRawFont(*m_rawFont); + + // 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(). + auto transformBackup = m_painter->transform(); + + // Make the glyph position the coordinate origin -- that's our center of scaling + m_painter->translate(QPointF(glyphPosition.x(),glyphPosition.y())); + + const double *textMat = state->getTextMat(); + + 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. + auto fillColoredPen = m_currentPen; + fillColoredPen.setBrush(m_currentBrush); + m_painter->setPen(fillColoredPen); + m_painter->drawGlyphRun(QPointF(-glyphPosition.x(),-glyphPosition.y()), glyphRun); + + // Restore transformation and pen color + m_painter->setTransform(transformBackup); + m_painter->setPen(m_currentPen); + } + + 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 00a2e08a..80089be8 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,8 @@ #pragma interface #endif +#include + #include "goo/gtypes.h" #include "OutputDev.h" #include "GfxState.h" @@ -48,6 +51,8 @@ class SplashFont; class SplashFontEngine; struct SplashGlyphBitmap; +class QRawFont; + //------------------------------------------------------------------------ // ArthurOutputDev - Qt 5 QPainter renderer //------------------------------------------------------------------------ @@ -169,6 +174,8 @@ private: SplashFontEngine *m_fontEngine; SplashFont *m_font; // current font XRef *xref; // xref table for current document + + std::unique_ptr m_rawFont; }; #endif -- 2.14.1