Skip to content

Commit

Permalink
Add some tests for rendering RTL text
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed May 29, 2024
1 parent b976928 commit 6d7f885
Show file tree
Hide file tree
Showing 18 changed files with 512 additions and 13 deletions.
29 changes: 17 additions & 12 deletions src/core/qgsfontutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -258,23 +258,25 @@ bool QgsFontUtils::loadStandardTestFonts( const QStringList &loadstyles )
// load standard test font from filesystem or testdata.qrc (for unit tests and general testing)
bool fontsLoaded = false;

const QString fontFamily = standardTestFontFamily();
QMap<QString, QString> fontStyles;
fontStyles.insert( QStringLiteral( "Roman" ), QStringLiteral( "QGIS-Vera/QGIS-Vera.ttf" ) );
fontStyles.insert( QStringLiteral( "Oblique" ), QStringLiteral( "QGIS-Vera/QGIS-VeraIt.ttf" ) );
fontStyles.insert( QStringLiteral( "Bold" ), QStringLiteral( "QGIS-Vera/QGIS-VeraBd.ttf" ) );
fontStyles.insert( QStringLiteral( "Bold Oblique" ), QStringLiteral( "QGIS-Vera/QGIS-VeraBI.ttf" ) );
fontStyles.insert( QStringLiteral( "Deja Bold" ), QStringLiteral( "QGIS-DejaVu/QGISDejaVuSans-Bold.ttf" ) );

QMap<QString, QString>::const_iterator f = fontStyles.constBegin();
for ( ; f != fontStyles.constEnd(); ++f )
{
const QString fontstyle( f.key() );
const QString fontpath( f.value() );
if ( !( loadstyles.contains( fontstyle ) || loadstyles.contains( QStringLiteral( "All" ) ) ) )
if ( !( loadstyles.contains( f.key() ) || loadstyles.contains( QStringLiteral( "All" ) ) ) )
{
continue;
}

const QString fontFamily = !f.key().startsWith( QStringLiteral( "Deja" ) ) ? standardTestFontFamily() : QStringLiteral( "QGIS DejaVu Sans" );
const QString fontstyle = !f.key().startsWith( QStringLiteral( "Deja" ) ) ? f.key() : f.key().mid( 5 );

if ( fontFamilyHasStyle( fontFamily, fontstyle ) )
{
QgsDebugMsgLevel( QStringLiteral( "Test font '%1 %2' already available" ).arg( fontFamily, fontstyle ), 2 );
Expand Down Expand Up @@ -316,37 +318,40 @@ bool QgsFontUtils::loadStandardTestFonts( const QStringList &loadstyles )

QFont QgsFontUtils::getStandardTestFont( const QString &style, int pointsize )
{
if ( ! fontFamilyHasStyle( standardTestFontFamily(), style ) )
const QString fontFamily = !style.startsWith( QStringLiteral( "Deja" ) ) ? standardTestFontFamily() : QStringLiteral( "QGIS DejaVu Sans" );
const QString fontStyle = !style.startsWith( QStringLiteral( "Deja" ) ) ? style : style.mid( 5 );

if ( ! fontFamilyHasStyle( fontFamily, fontStyle ) )
{
loadStandardTestFonts( QStringList() << style );
}

const QFontDatabase fontDB;
QFont f = fontDB.font( standardTestFontFamily(), style, pointsize );
QFont f = fontDB.font( fontFamily, fontStyle, pointsize );
#ifdef Q_OS_WIN
if ( !f.exactMatch() )
{
QString modified;
if ( style == "Roman" )
if ( fontStyle == "Roman" )
modified = "Normal";
else if ( style == "Oblique" )
else if ( fontStyle == "Oblique" )
modified = "Italic";
else if ( style == "Bold Oblique" )
else if ( fontStyle == "Bold Oblique" )
modified = "Bold Italic";
if ( !modified.isEmpty() )
f = fontDB.font( standardTestFontFamily(), modified, pointsize );
f = fontDB.font( fontFamily, modified, pointsize );
}
if ( !f.exactMatch() )
{
QgsDebugMsgLevel( QStringLiteral( "Inexact font match - consider installing the %1 font." ).arg( standardTestFontFamily() ), 2 );
QgsDebugMsgLevel( QStringLiteral( "Inexact font match - consider installing the %1 font." ).arg( fontFamily ), 2 );
QgsDebugMsgLevel( QStringLiteral( "Requested: %1" ).arg( f.toString() ), 2 );
QFontInfo fi( f );
QgsDebugMsgLevel( QStringLiteral( "Replaced: %1,%2,%3,%4,%5,%6,%7,%8,%9" ).arg( fi.family() ).arg( fi.pointSizeF() ).arg( fi.pixelSize() ).arg( fi.styleHint() ).arg( fi.weight() ).arg( fi.style() ).arg( fi.underline() ).arg( fi.strikeOut() ).arg( fi.fixedPitch() ), 2 );
}
#endif
// in case above statement fails to set style
f.setBold( style.contains( QLatin1String( "Bold" ) ) );
f.setItalic( style.contains( QLatin1String( "Oblique" ) ) || style.contains( QLatin1String( "Italic" ) ) );
f.setBold( fontStyle.contains( QLatin1String( "Bold" ) ) );
f.setItalic( fontStyle.contains( QLatin1String( "Oblique" ) ) || fontStyle.contains( QLatin1String( "Italic" ) ) );

return f;
}
Expand Down
174 changes: 174 additions & 0 deletions tests/src/python/test_qgstextrenderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -1895,6 +1895,180 @@ def testDrawForcedItalic(self):
format.setForcedItalic(True)
assert self.checkRender(format, 'forced_italic', text=['Forced italic'])

def testDrawRTL(self):
"""
Test drawing with simple rtl text
"""
format = QgsTextFormat()
format.setFont(getTestFont('Deja bold'))
format.setSize(30)

self.assertTrue(self.checkRenderPoint(format, 'rtl', text=['مرحبا بالعالم'],
point=QPointF(5, 200)))

def testDrawRTLHTML(self):
"""
Test drawing with rtl text with HTML formatting
"""
format = QgsTextFormat()
format.setFont(getTestFont('Deja bold'))
format.setSize(30)
format.setAllowHtmlFormatting(True)
self.assertTrue(self.checkRenderPoint(format, 'rtl_html', text=['<span style="font-size:50pt; color:green">بالعالم</span> مرحبا'],
point=QPointF(5, 200)))

def testDrawRTLRightAlign(self):
"""
Test drawing with rtl text with right align
"""
format = QgsTextFormat()
format.setFont(getTestFont('Deja bold'))
format.setSize(30)

self.assertTrue(self.checkRender(format, 'rtl_right_align', text=['مرحبا بالعالم'],
rect=QRectF(5, 100, 350, 250),
alignment=QgsTextRenderer.HAlignment.AlignRight))

def testDrawRTLBuffer(self):
"""
Test drawing with right to left text with buffer
"""
format = QgsTextFormat()
format.setFont(getTestFont('Deja bold'))
format.setSize(30)
format.buffer().setEnabled(True)
format.buffer().setSize(2)
format.buffer().setSizeUnit(QgsUnitTypes.RenderUnit.RenderMillimeters)

self.assertTrue(self.checkRenderPoint(format, 'rtl_buffer', text=['مرحبا بالعالم'],
point=QPointF(5, 200)))

def testDrawRTLShadow(self):
"""
Test drawing with right to left text with shadow
"""
format = QgsTextFormat()
format.setFont(getTestFont('Deja bold'))
format.setSize(30)
format.setColor(
QColor(255, 255, 0))
format.shadow().setEnabled(True)
format.shadow().setShadowPlacement(QgsTextShadowSettings.ShadowPlacement.ShadowText)
format.shadow().setOpacity(1.0)
format.shadow().setBlurRadius(0)
format.shadow().setOffsetDistance(5)
format.shadow().setOffsetUnit(QgsUnitTypes.RenderUnit.RenderMillimeters)

self.assertTrue(self.checkRenderPoint(format, 'rtl_shadow', text=['مرحبا بالعالم'],
point=QPointF(5, 200)))

@unittest.skip('broken')
def testDrawTextOnLineRTL(self):
"""
Test drawing right to left text on line
"""
format = QgsTextFormat()
format.setFont(getTestFont('Deja bold'))
format.setSize(25)
format.setSizeUnit(QgsUnitTypes.RenderUnit.RenderPoints)

image = QImage(400, 400, QImage.Format.Format_RGB32)

painter = QPainter()
ms = QgsMapSettings()
ms.setExtent(QgsRectangle(0, 0, 50, 50))
ms.setOutputSize(image.size())
context = QgsRenderContext.fromMapSettings(ms)
context.setPainter(painter)
context.setScaleFactor(96 / 25.4) # 96 DPI
context.setFlag(QgsRenderContext.Flag.ApplyScalingWorkaroundForTextRendering, True)

painter.begin(image)
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
image.fill(QColor(152, 219, 249))

painter.setBrush(Qt.BrushStyle.NoBrush)
painter.setPen(QPen(QColor(0, 0, 0)))

line = QPolygonF([QPointF(50, 200), QPointF(350, 200)])
painter.drawPolygon(line)

painter.setBrush(QBrush(QColor(182, 239, 255)))
painter.setPen(Qt.PenStyle.NoPen)

QgsTextRenderer.drawTextOnLine(line, 'مرحبا بالعالم', context, format, 0)

painter.end()
self.assertTrue(self.image_check('text_on_line_at_start', 'text_on_line_at_start', image, 'text_on_line_at_start'))

def testDrawRTLLTRMixed(self):
"""
Test drawing with mixed right to left and left to right text
"""
format = QgsTextFormat()
format.setFont(getTestFont('Deja bold'))
format.setSize(30)

self.assertTrue(self.checkRenderPoint(format, 'rtl_mixed', text=['hello באמת abc'],
point=QPointF(5, 200)))

def testDrawRTLLTRMixedHtml(self):
"""
Test drawing with right to left marker, html mode
"""
format = QgsTextFormat()
format.setFont(getTestFont('Deja bold'))
format.setSize(30)
format.setAllowHtmlFormatting(True)

self.assertTrue(self.checkRenderPoint(format, 'rtl_mixed_html', text=['<i>hello באמת </i>abc'],
point=QPointF(5, 200)))

def testDrawRTLLTRMixedRect(self):
"""
Test drawing with right to left marker in rect mode, right aligned
"""
format = QgsTextFormat()
format.setFont(getTestFont('Deja bold'))
format.setSize(30)

self.assertTrue(self.checkRender(format, 'rtl_mixed_right_align', text=['hello באמת abc'],
rect=QRectF(5, 100, 350, 250),
alignment=QgsTextRenderer.HAlignment.AlignRight))

def testDrawRTLLTRMixedBuffer(self):
"""
Test drawing with right to left marker with buffer
"""
format = QgsTextFormat()
format.setFont(getTestFont('Deja bold'))
format.setSize(30)
format.buffer().setEnabled(True)
format.buffer().setSize(2)
format.buffer().setSizeUnit(QgsUnitTypes.RenderUnit.RenderMillimeters)

self.assertTrue(self.checkRenderPoint(format, 'rtl_mixed_buffer', text=['hello באמת abc'],
point=QPointF(5, 200)))

def testDrawRTLLTRMixedShadow(self):
"""
Test drawing with right to left marker with shadow
"""
format = QgsTextFormat()
format.setFont(getTestFont('Deja bold'))
format.setSize(30)
format.setColor(
QColor(255, 255, 0))
format.shadow().setEnabled(True)
format.shadow().setShadowPlacement(QgsTextShadowSettings.ShadowPlacement.ShadowText)
format.shadow().setOpacity(1.0)
format.shadow().setBlurRadius(0)
format.shadow().setOffsetDistance(5)
format.shadow().setOffsetUnit(QgsUnitTypes.RenderUnit.RenderMillimeters)

self.assertTrue(self.checkRenderPoint(format, 'rtl_mixed_shadow', text=['hello באמת abc'],
point=QPointF(5, 200)))

@unittest.skipIf(int(QT_VERSION_STR.split('.')[0]) < 6 or (int(QT_VERSION_STR.split('.')[0]) == 6 and int(QT_VERSION_STR.split('.')[1]) < 3), 'Too old Qt')
def testDrawSmallCaps(self):
format = QgsTextFormat()
Expand Down
2 changes: 1 addition & 1 deletion tests/src/python/utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ def loadTestFonts():

global FONTSLOADED # pylint: disable=W0603
if FONTSLOADED is False:
QgsFontUtils.loadStandardTestFonts(['Roman', 'Bold'])
QgsFontUtils.loadStandardTestFonts(['Roman', 'Bold', 'Deja Bold'])
msg = getTestFontFamily() + ' base test font styles could not be loaded'
res = (QgsFontUtils.fontFamilyHasStyle(getTestFontFamily(), 'Roman') and
QgsFontUtils.fontFamilyHasStyle(getTestFontFamily(), 'Bold'))
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
57 changes: 57 additions & 0 deletions tests/testdata/font/QGIS-DejaVu/AUTHORS
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
abysta at yandex.ru
Adrian Schroeter
Aleksey Chalabyan
Andrey Valentinovich Panov
Ben Laenen
Besarion Gugushvili
Bhikkhu Pesala
Clayborne Arevalo
Dafydd Harries
Danilo Segan
Davide Viti
David Jez
David Lawrence Ramsey
Denis Jacquerye
Dwayne Bailey
Eugeniy Meshcheryakov
Frédéric Wang
Gee Fung Sit
Heikki Lindroos
James Cloos
James Crippen
John Karp
Keenan Pepper
Lars Næsbye Christensen
Lior Halphon
MaEr
Mashrab Kuvatov
Max Berger
Mederic Boquien
Michael Everson
MihailJP
Misu Moldovan
Nguyen Thai Ngoc Duy
Nicolas Mailhot
Norayr Chilingarian
Olleg Samoylov
Ognyan Kulev
Ondrej Koala Vacha
Peter Cernak
Remy Oudompheng
Roozbeh Pournader
Rouben Hakobian
Sahak Petrosyan
Sami Tarazi
Sander Vesik
Stepan Roh
Stephen Hartke
Steve Tinney
Tavmjong Bah
Thomas Henlich
Tim May
Valentin Stoykov
Vasek Stodulka
Wesley Transue
Yoshiki Ohshima

$Id$
Loading

0 comments on commit 6d7f885

Please sign in to comment.